* refactor: long-tail type-aware sweep (T3.c) Cluster T3.c — 15 files, 24 errors cleared. Rules fixed: - no-useless-default-assignment: 6 → 0 (mcp-server.ts — Zod .default() makes destructuring defaults redundant) - no-unnecessary-type-assertion: 1 → 0 (workspace-registry-model.ts — remove redundant ! on string[]) - no-unsafe-enum-comparison: 3 → 0 (use-push-token-registration.ts, use-image-attachment-picker.ts — use PermissionStatus enum values) - no-redundant-type-constituents: 6 → 0 (workspace-setup.ts, tool-call-detail-state.ts, workspace-draft-agent-tab.tsx, stream.test.ts, desktop-settings-commands.ts — unknown | T → unknown) - no-unnecessary-type-parameters: 4 → 0 (indexeddb-attachment-store.test.ts, use-archive-agent.ts, class-mocks.ts, agent-stream-coalescer.ts — narrow interface / remove unused generics) - no-unnecessary-type-conversion: 4 → 0 (session-context.tsx — remove String() on already-string values; class-mocks.ts — remove String(prop) after symbol guard) - no-implied-eval: 1 → 0 (agent-stream-coalescer.test.ts — narrow timer interface to function-only, eliminating string overload) Deferred (need caller updates or extra files beyond 15-file cap): - no-unnecessary-type-parameters: host.ts:onResized/onDragDropEvent, electron/events.ts (callers pass typed handlers; removing generic breaks contravariance without cast) - no-base-to-string: ~21 errors, 18 files - restrict-template-expressions: ~11 errors, 9 files - await-thenable: ~7 errors, 4 files - no-unnecessary-type-conversion: ~9 remaining errors, 9 files * fix(server): restore MCP tool handler defaults that the linter mis-flagged The `no-useless-default-assignment` rule fires because Zod's `.default()` chain makes the TypeScript type non-optional, so the linter treats the destructuring defaults as redundant. But the MCP framework invokes handlers with raw (untransformed) params — Zod defaults are not applied before the call. The destructuring defaults are the runtime safety net. Reverts the three handler changes from T3.c: - send_prompt: background = false, notifyOnFinish = false - list_agents: includeArchived = false, sinceHours = 48, limit = 50 - get_terminal_lines: stripAnsi = true The no-useless-default-assignment errors on these lines remain and are deferred to a future cluster that addresses the MCP framework integration.
104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
|
|
|
|
function hasMeaningfulUnknownValue(value: unknown): boolean {
|
|
if (value === null || value === undefined) {
|
|
return false;
|
|
}
|
|
if (typeof value === "string") {
|
|
return value.trim().length > 0;
|
|
}
|
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
return true;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.some(hasMeaningfulUnknownValue);
|
|
}
|
|
if (typeof value === "object") {
|
|
return Object.values(value).some(hasMeaningfulUnknownValue);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function hasMeaningfulSearchDetail(detail: Extract<ToolCallDetail, { type: "search" }>): boolean {
|
|
if (detail.query.trim().length > 0) return true;
|
|
if (detail.content) return true;
|
|
if (detail.filePaths && detail.filePaths.length > 0) return true;
|
|
if (detail.webResults && detail.webResults.length > 0) return true;
|
|
if (detail.annotations && detail.annotations.length > 0) return true;
|
|
return false;
|
|
}
|
|
|
|
function hasMeaningfulReadLikeDetail(detail: { filePath?: unknown; content?: unknown }): boolean {
|
|
return Boolean(detail.filePath || detail.content);
|
|
}
|
|
|
|
function hasMeaningfulEditDetail(detail: Extract<ToolCallDetail, { type: "edit" }>): boolean {
|
|
return Boolean(detail.filePath || detail.unifiedDiff || detail.oldString || detail.newString);
|
|
}
|
|
|
|
function hasMeaningfulFetchDetail(detail: Extract<ToolCallDetail, { type: "fetch" }>): boolean {
|
|
return Boolean(detail.url || detail.result || detail.codeText);
|
|
}
|
|
|
|
function hasMeaningfulWorktreeSetupDetail(
|
|
detail: Extract<ToolCallDetail, { type: "worktree_setup" }>,
|
|
): boolean {
|
|
return Boolean(detail.branchName || detail.worktreePath || detail.log);
|
|
}
|
|
|
|
function hasMeaningfulSubAgentDetail(
|
|
detail: Extract<ToolCallDetail, { type: "sub_agent" }>,
|
|
): boolean {
|
|
return Boolean(detail.subAgentType || detail.description || detail.log);
|
|
}
|
|
|
|
function hasMeaningfulPlainTextDetail(
|
|
detail: Extract<ToolCallDetail, { type: "plain_text" }>,
|
|
): boolean {
|
|
return Boolean(detail.label || detail.text);
|
|
}
|
|
|
|
function hasMeaningfulUnknownDetail(detail: Extract<ToolCallDetail, { type: "unknown" }>): boolean {
|
|
return hasMeaningfulUnknownValue(detail.input) || hasMeaningfulUnknownValue(detail.output);
|
|
}
|
|
|
|
export function hasMeaningfulToolCallDetail(detail: ToolCallDetail | undefined): boolean {
|
|
if (!detail) {
|
|
return false;
|
|
}
|
|
|
|
switch (detail.type) {
|
|
case "shell":
|
|
return true;
|
|
case "read":
|
|
return hasMeaningfulReadLikeDetail(detail);
|
|
case "edit":
|
|
return hasMeaningfulEditDetail(detail);
|
|
case "write":
|
|
return hasMeaningfulReadLikeDetail(detail);
|
|
case "search":
|
|
return hasMeaningfulSearchDetail(detail);
|
|
case "fetch":
|
|
return hasMeaningfulFetchDetail(detail);
|
|
case "worktree_setup":
|
|
return hasMeaningfulWorktreeSetupDetail(detail);
|
|
case "sub_agent":
|
|
return hasMeaningfulSubAgentDetail(detail);
|
|
case "plain_text":
|
|
return hasMeaningfulPlainTextDetail(detail);
|
|
case "plan":
|
|
return detail.text.trim().length > 0;
|
|
case "unknown":
|
|
return hasMeaningfulUnknownDetail(detail);
|
|
}
|
|
}
|
|
|
|
export function isPendingToolCallDetail(params: {
|
|
detail: ToolCallDetail | undefined;
|
|
status: "executing" | "running" | "completed" | "failed" | "canceled";
|
|
error: unknown;
|
|
}): boolean {
|
|
const isRunning = params.status === "running" || params.status === "executing";
|
|
return isRunning && params.error == null && !hasMeaningfulToolCallDetail(params.detail);
|
|
}
|