@sayknow-cli/coding-agent 0.3.2 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/types/config/model-profiles.d.ts +2 -2
  2. package/dist/types/config/model-registry.d.ts +3 -3
  3. package/dist/types/config/models-config-schema.d.ts +0 -5
  4. package/dist/types/config/settings-schema.d.ts +78 -9
  5. package/dist/types/export/html/template.generated.d.ts +1 -1
  6. package/dist/types/skc-runtime/state-renderer.d.ts +5 -0
  7. package/dist/types/skc-runtime/ultragoal-guard.d.ts +37 -1
  8. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +80 -0
  9. package/dist/types/tools/browser/tab-supervisor.d.ts +31 -0
  10. package/dist/types/tools/computer-gc.d.ts +23 -0
  11. package/dist/types/tools/cron.d.ts +36 -61
  12. package/dist/types/tools/index.d.ts +0 -1
  13. package/dist/types/tools/resource-gc.d.ts +54 -0
  14. package/dist/types/web/search/index.d.ts +1 -0
  15. package/dist/types/web/search/providers/utils.d.ts +11 -4
  16. package/package.json +7 -7
  17. package/scripts/verify-insane-vendor.ts +5 -0
  18. package/src/cli/args.ts +0 -1
  19. package/src/cli/fast-help.ts +0 -1
  20. package/src/cli/plugin-cli.ts +1 -1
  21. package/src/cli/web-search-cli.ts +5 -0
  22. package/src/config/model-profile-activation.ts +7 -1
  23. package/src/config/model-profiles.ts +3 -4
  24. package/src/config/model-registry.ts +3 -6
  25. package/src/config/models-config-schema.ts +1 -1
  26. package/src/config/settings-schema.ts +80 -10
  27. package/src/export/html/template.generated.ts +1 -1
  28. package/src/export/html/template.js +0 -12
  29. package/src/goals/tools/goal-tool.ts +14 -1
  30. package/src/internal-urls/docs-index.generated.ts +3 -4
  31. package/src/modes/components/model-selector.ts +9 -1
  32. package/src/modes/controllers/selector-controller.ts +6 -0
  33. package/src/prompts/system/system-prompt.md +16 -20
  34. package/src/prompts/tools/cron.md +5 -3
  35. package/src/prompts/tools/read.md +1 -1
  36. package/src/sdk.ts +5 -0
  37. package/src/session/agent-session.ts +44 -0
  38. package/src/skc-runtime/state-renderer.ts +13 -0
  39. package/src/skc-runtime/ultragoal-guard.ts +167 -0
  40. package/src/skc-runtime/ultragoal-runtime.ts +221 -1
  41. package/src/tools/browser/tab-supervisor.ts +86 -2
  42. package/src/tools/computer-gc.ts +66 -0
  43. package/src/tools/computer.ts +2 -0
  44. package/src/tools/cron.ts +75 -112
  45. package/src/tools/index.ts +2 -8
  46. package/src/tools/read.ts +25 -55
  47. package/src/tools/renderers.ts +0 -2
  48. package/src/tools/resource-gc.ts +291 -0
  49. package/src/tools/ultragoal-ask-guard.ts +7 -1
  50. package/src/web/search/index.ts +1 -0
  51. package/src/web/search/providers/utils.ts +22 -5
  52. package/vendor/insane-search/MANIFEST.json +5 -1
  53. package/vendor/insane-search/engine/__init__.py +14 -0
  54. package/vendor/insane-search/engine/content_safety.py +151 -0
  55. package/vendor/insane-search/engine/fetch_chain.py +32 -0
  56. package/vendor/insane-search/engine/tests/test_u1.py +28 -0
  57. package/vendor/insane-search/engine/tests/test_u8.py +216 -0
  58. package/vendor/insane-search/engine/transport.py +3 -2
  59. package/vendor/insane-search/engine/validators.py +14 -0
  60. package/dist/types/tools/inspect-image-renderer.d.ts +0 -26
  61. package/dist/types/tools/inspect-image.d.ts +0 -31
  62. package/src/prompts/tools/inspect-image-system.md +0 -20
  63. package/src/prompts/tools/inspect-image.md +0 -32
  64. package/src/tools/inspect-image-renderer.ts +0 -103
  65. package/src/tools/inspect-image.ts +0 -172
package/src/tools/cron.ts CHANGED
@@ -19,34 +19,36 @@ const MAX_RECURRING_JITTER_MS = 30 * 60 * 1000;
19
19
  const MAX_ONE_SHOT_EARLY_JITTER_MS = 90 * 1000;
20
20
  const MAX_TIMEOUT_MS = 2_147_483_647;
21
21
 
22
- const cronCreateSchema = z.object({
23
- cron_expression: z
24
- .string()
25
- .describe(
26
- "Standard 5-field cron expression in the user's local timezone: 'minute hour day-of-month month day-of-week'. Examples: '*/5 * * * *' (every 5 min), '0 9 * * *' (9am daily), '0 9 * * 1-5' (weekdays at 9am). Day-of-week uses 0/7 for Sunday through 6 for Saturday. When both day-of-month and day-of-week are constrained, a date matches if either field matches (vixie-cron semantics).",
27
- ),
28
- prompt: z
29
- .string()
30
- .describe(
31
- "Prompt to inject between turns when the cron fires. May reference slash commands (e.g. '/review-pr 1234') or natural-language instructions.",
32
- ),
33
- recurring: z
34
- .boolean()
35
- .default(true)
36
- .describe(
37
- "true to fire on every match of the cron expression (recurring, auto-expires after 7 days); false to fire once at the next match and then self-delete.",
38
- ),
39
- });
40
-
41
- export type CronCreateParams = z.infer<typeof cronCreateSchema>;
42
-
43
- const cronListSchema = z.object({});
44
- export type CronListParams = z.infer<typeof cronListSchema>;
45
-
46
- const cronDeleteSchema = z.object({
47
- id: z.string().min(1).describe("The 8-character job ID returned by CronCreate."),
48
- });
49
- export type CronDeleteParams = z.infer<typeof cronDeleteSchema>;
22
+ const cronSchema = z
23
+ .object({
24
+ op: z
25
+ .enum(["create", "list", "delete"])
26
+ .describe(
27
+ "operation: 'create' schedules a prompt on a cron expression, 'list' enumerates scheduled tasks, 'delete' cancels a task by id",
28
+ ),
29
+ cron_expression: z
30
+ .string()
31
+ .optional()
32
+ .describe(
33
+ "(op=create, required) Standard 5-field cron expression in the user's local timezone: 'minute hour day-of-month month day-of-week'. Examples: '*/5 * * * *' (every 5 min), '0 9 * * *' (9am daily), '0 9 * * 1-5' (weekdays at 9am). Day-of-week uses 0/7 for Sunday through 6 for Saturday. When both day-of-month and day-of-week are constrained, a date matches if either field matches (vixie-cron semantics).",
34
+ ),
35
+ prompt: z
36
+ .string()
37
+ .optional()
38
+ .describe(
39
+ "(op=create, required) Prompt to inject between turns when the cron fires. May reference slash commands (e.g. '/review-pr 1234') or natural-language instructions.",
40
+ ),
41
+ recurring: z
42
+ .boolean()
43
+ .optional()
44
+ .describe(
45
+ "(op=create) true to fire on every match of the cron expression (recurring, auto-expires after 7 days); false to fire once at the next match and then self-delete.",
46
+ ),
47
+ id: z.string().optional().describe("(op=delete, required) The 8-character job ID returned by op=create."),
48
+ })
49
+ .strict();
50
+
51
+ export type CronParams = z.infer<typeof cronSchema>;
50
52
 
51
53
  export interface CronJobSnapshot {
52
54
  id: string;
@@ -68,20 +70,14 @@ export interface CronListJobDetails {
68
70
  humanSchedule: string;
69
71
  }
70
72
 
71
- export interface CronCreateToolDetails {
72
- id: string;
73
- cron_expression: string;
74
- recurring: boolean;
73
+ export interface CronToolDetails {
74
+ op: "create" | "list" | "delete";
75
+ id?: string;
76
+ cron_expression?: string;
77
+ recurring?: boolean;
75
78
  nextFireAt?: number;
76
- }
77
-
78
- export interface CronListToolDetails {
79
- jobs: CronListJobDetails[];
80
- }
81
-
82
- export interface CronDeleteToolDetails {
83
- id: string;
84
- deleted: boolean;
79
+ jobs?: CronListJobDetails[];
80
+ deleted?: boolean;
85
81
  }
86
82
 
87
83
  interface CronTimerHandle {
@@ -543,12 +539,12 @@ function isCronDisabled(): boolean {
543
539
  return process.env.CLAUDE_CODE_DISABLE_CRON === "1";
544
540
  }
545
541
 
546
- export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCreateToolDetails> {
547
- readonly name = "CronCreate";
548
- readonly label = "CronCreate";
549
- readonly summary = "Schedule a prompt on a 5-field cron expression";
542
+ export class CronTool implements AgentTool<typeof cronSchema, CronToolDetails> {
543
+ readonly name = "cron";
544
+ readonly label = "Cron";
545
+ readonly summary = "Schedule, list, and cancel cron-style prompts (op: create | list | delete)";
550
546
  readonly description: string;
551
- readonly parameters = cronCreateSchema;
547
+ readonly parameters = cronSchema;
552
548
  readonly strict = true;
553
549
  readonly loadMode = "discoverable";
554
550
 
@@ -556,19 +552,30 @@ export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCr
556
552
  this.description = prompt.render(cronDescription);
557
553
  }
558
554
 
559
- static createIf(session: ToolSession): CronCreateTool | null {
555
+ static createIf(session: ToolSession): CronTool | null {
560
556
  if (!isBackgroundJobSupportEnabled(session.settings)) return null;
561
557
  if (isCronDisabled()) return null;
562
- return new CronCreateTool(session);
558
+ return new CronTool(session);
563
559
  }
564
560
 
565
561
  async execute(
566
562
  _toolCallId: string,
567
- params: CronCreateParams,
563
+ params: CronParams,
568
564
  _signal?: AbortSignal,
569
- _onUpdate?: AgentToolUpdateCallback<CronCreateToolDetails>,
565
+ _onUpdate?: AgentToolUpdateCallback<CronToolDetails>,
570
566
  _context?: AgentToolContext,
571
- ): Promise<AgentToolResult<CronCreateToolDetails>> {
567
+ ): Promise<AgentToolResult<CronToolDetails>> {
568
+ switch (params.op) {
569
+ case "create":
570
+ return this.#create(params);
571
+ case "list":
572
+ return this.#list();
573
+ case "delete":
574
+ return this.#delete(params);
575
+ }
576
+ }
577
+
578
+ async #create(params: CronParams): Promise<AgentToolResult<CronToolDetails>> {
572
579
  const manager = AsyncJobManager.instance();
573
580
  if (!manager) {
574
581
  throw new ToolError("Async execution is disabled; cron is unavailable in this session.");
@@ -576,26 +583,30 @@ export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCr
576
583
  if (isCronDisabled()) {
577
584
  throw new ToolError("Cron is disabled by CLAUDE_CODE_DISABLE_CRON=1.");
578
585
  }
586
+ if (!params.cron_expression || !params.prompt) {
587
+ throw new ToolError("cron op=create requires both 'cron_expression' and 'prompt'.");
588
+ }
579
589
  validateCronExpression(params.cron_expression);
580
590
 
581
591
  const ownerId = this.session.getAgentId?.() ?? undefined;
582
592
  const state = getOrCreateOwnerState(ownerId);
583
593
  if (state.jobs.size >= MAX_CRON_TASKS_PER_OWNER) {
584
594
  throw new ToolError(
585
- `Cron task limit reached (${MAX_CRON_TASKS_PER_OWNER}). Cancel an existing task with CronDelete first.`,
595
+ `Cron task limit reached (${MAX_CRON_TASKS_PER_OWNER}). Cancel an existing task with cron op=delete first.`,
586
596
  );
587
597
  }
588
598
  ensureOwnerCleanup(ownerId, manager, state);
589
599
 
590
600
  const id = generateCronId(new Set(state.jobs.keys()));
591
601
  const now = Date.now();
602
+ const recurring = params.recurring ?? true;
592
603
  const snapshot: CronJobSnapshot = {
593
604
  id,
594
605
  cron_expression: params.cron_expression.trim(),
595
606
  prompt: params.prompt,
596
- recurring: params.recurring,
607
+ recurring,
597
608
  createdAt: now,
598
- expiresAt: params.recurring ? now + CRON_RECURRING_MAX_AGE_MS : undefined,
609
+ expiresAt: recurring ? now + CRON_RECURRING_MAX_AGE_MS : undefined,
599
610
  humanSchedule: humanizeCronExpression(params.cron_expression.trim()),
600
611
  ownerId,
601
612
  };
@@ -604,7 +615,7 @@ export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCr
604
615
  scheduleExpiry(ownerId, record);
605
616
  scheduleRecord(ownerId, state, record);
606
617
 
607
- logger.debug("CronCreate: scheduled task", {
618
+ logger.debug("cron op=create: scheduled task", {
608
619
  id,
609
620
  ownerId,
610
621
  cron: snapshot.cron_expression,
@@ -614,6 +625,7 @@ export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCr
614
625
  return {
615
626
  content: [{ type: "text", text: `Scheduled ${id} (${snapshot.humanSchedule})` }],
616
627
  details: {
628
+ op: "create",
617
629
  id,
618
630
  cron_expression: snapshot.cron_expression,
619
631
  recurring: snapshot.recurring,
@@ -621,34 +633,8 @@ export class CronCreateTool implements AgentTool<typeof cronCreateSchema, CronCr
621
633
  },
622
634
  };
623
635
  }
624
- }
625
-
626
- export class CronListTool implements AgentTool<typeof cronListSchema, CronListToolDetails> {
627
- readonly name = "CronList";
628
- readonly label = "CronList";
629
- readonly summary = "List scheduled cron jobs";
630
- readonly description: string;
631
- readonly parameters = cronListSchema;
632
- readonly strict = true;
633
- readonly loadMode = "discoverable";
634
-
635
- constructor(private readonly session: ToolSession) {
636
- this.description = prompt.render(cronDescription);
637
- }
638
636
 
639
- static createIf(session: ToolSession): CronListTool | null {
640
- if (!isBackgroundJobSupportEnabled(session.settings)) return null;
641
- if (isCronDisabled()) return null;
642
- return new CronListTool(session);
643
- }
644
-
645
- async execute(
646
- _toolCallId: string,
647
- _params: CronListParams,
648
- _signal?: AbortSignal,
649
- _onUpdate?: AgentToolUpdateCallback<CronListToolDetails>,
650
- _context?: AgentToolContext,
651
- ): Promise<AgentToolResult<CronListToolDetails>> {
637
+ async #list(): Promise<AgentToolResult<CronToolDetails>> {
652
638
  const ownerId = this.session.getAgentId?.() ?? undefined;
653
639
  const state = schedulesByOwner.get(ownerKey(ownerId));
654
640
  const records = state
@@ -658,7 +644,7 @@ export class CronListTool implements AgentTool<typeof cronListSchema, CronListTo
658
644
  if (jobs.length === 0) {
659
645
  return {
660
646
  content: [{ type: "text", text: "No scheduled jobs" }],
661
- details: { jobs: [] },
647
+ details: { op: "list", jobs: [] },
662
648
  };
663
649
  }
664
650
  const lines = jobs.map(job => {
@@ -667,43 +653,20 @@ export class CronListTool implements AgentTool<typeof cronListSchema, CronListTo
667
653
  });
668
654
  return {
669
655
  content: [{ type: "text", text: lines.join("\n") }],
670
- details: { jobs },
656
+ details: { op: "list", jobs },
671
657
  };
672
658
  }
673
- }
674
659
 
675
- export class CronDeleteTool implements AgentTool<typeof cronDeleteSchema, CronDeleteToolDetails> {
676
- readonly name = "CronDelete";
677
- readonly label = "CronDelete";
678
- readonly summary = "Cancel a scheduled cron job by ID";
679
- readonly description: string;
680
- readonly parameters = cronDeleteSchema;
681
- readonly strict = true;
682
- readonly loadMode = "discoverable";
683
-
684
- constructor(private readonly session: ToolSession) {
685
- this.description = prompt.render(cronDescription);
686
- }
687
-
688
- static createIf(session: ToolSession): CronDeleteTool | null {
689
- if (!isBackgroundJobSupportEnabled(session.settings)) return null;
690
- if (isCronDisabled()) return null;
691
- return new CronDeleteTool(session);
692
- }
693
-
694
- async execute(
695
- _toolCallId: string,
696
- params: CronDeleteParams,
697
- _signal?: AbortSignal,
698
- _onUpdate?: AgentToolUpdateCallback<CronDeleteToolDetails>,
699
- _context?: AgentToolContext,
700
- ): Promise<AgentToolResult<CronDeleteToolDetails>> {
660
+ async #delete(params: CronParams): Promise<AgentToolResult<CronToolDetails>> {
661
+ if (!params.id) {
662
+ throw new ToolError("cron op=delete requires 'id'.");
663
+ }
701
664
  const ownerId = this.session.getAgentId?.() ?? undefined;
702
665
  const deleted = deleteRecord(ownerId, params.id);
703
666
  const text = deleted ? `Cancelled ${params.id}` : `No scheduled task '${params.id}' found; nothing to cancel.`;
704
667
  return {
705
668
  content: [{ type: "text", text }],
706
- details: { id: params.id, deleted },
669
+ details: { op: "delete", id: params.id, deleted },
707
670
  };
708
671
  }
709
672
  }
@@ -38,12 +38,11 @@ import { BrowserTool } from "./browser";
38
38
  import { CalculatorTool } from "./calculator";
39
39
  import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
40
40
  import { ComputerTool, isComputerCallable, isComputerLoadablePlatform } from "./computer";
41
- import { CronCreateTool, CronDeleteTool, CronListTool } from "./cron";
41
+ import { CronTool } from "./cron";
42
42
  import { DebugTool } from "./debug";
43
43
  import { EvalTool } from "./eval";
44
44
  import { FindTool } from "./find";
45
45
  import { GithubTool } from "./gh";
46
- import { InspectImageTool } from "./inspect-image";
47
46
  import { IrcTool } from "./irc";
48
47
  import { JobTool } from "./job";
49
48
  import { MonitorTool } from "./monitor";
@@ -83,7 +82,6 @@ export * from "./eval";
83
82
  export * from "./find";
84
83
  export * from "./gh";
85
84
  export * from "./image-gen";
86
- export * from "./inspect-image";
87
85
  export * from "./irc";
88
86
  export * from "./job";
89
87
  export * from "./monitor";
@@ -389,7 +387,6 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
389
387
  find: s => new FindTool(s),
390
388
  search: s => new SearchTool(s),
391
389
  lsp: LspTool.createIf,
392
- inspect_image: s => new InspectImageTool(s),
393
390
  browser: s => new BrowserTool(s),
394
391
  ...(isComputerLoadablePlatform() ? { computer: ComputerTool.createIf } : {}),
395
392
  checkpoint: CheckpointTool.createIf,
@@ -398,9 +395,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
398
395
  subagent: s => new SubagentTool(s),
399
396
  job: JobTool.createIf,
400
397
  monitor: MonitorTool.createIf,
401
- CronCreate: CronCreateTool.createIf,
402
- CronList: CronListTool.createIf,
403
- CronDelete: CronDeleteTool.createIf,
398
+ cron: CronTool.createIf,
404
399
  recipe: RecipeTool.createIf,
405
400
  irc: IrcTool.createIf,
406
401
  todo_write: s => new TodoWriteTool(s),
@@ -560,7 +555,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
560
555
  if (name === "ast_grep") return session.settings.get("astGrep.enabled");
561
556
  if (name === "ast_edit") return session.settings.get("astEdit.enabled");
562
557
  if (name === "render_mermaid") return session.settings.get("renderMermaid.enabled");
563
- if (name === "inspect_image") return session.settings.get("inspect_image.enabled");
564
558
  if (name === "web_search") return session.settings.get("web_search.enabled");
565
559
  // search_tool_bm25 is allowed when either legacy mcp.discoveryMode or new tools.discoveryMode is active.
566
560
  if (name === "search_tool_bm25") return discoveryActive;
package/src/tools/read.ts CHANGED
@@ -670,7 +670,6 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
670
670
 
671
671
  readonly #autoResizeImages: boolean;
672
672
  readonly #defaultLimit: number;
673
- readonly #inspectImageEnabled: boolean;
674
673
 
675
674
  constructor(private readonly session: ToolSession) {
676
675
  const displayMode = resolveFileDisplayMode(session);
@@ -679,7 +678,6 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
679
678
  1,
680
679
  Math.min(session.settings.get("read.defaultLimit") ?? DEFAULT_MAX_LINES, DEFAULT_MAX_LINES),
681
680
  );
682
- this.#inspectImageEnabled = session.settings.get("inspect_image.enabled");
683
681
  this.description = prompt.render(readDescription, {
684
682
  DEFAULT_LIMIT: String(this.#defaultLimit),
685
683
  DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
@@ -1572,62 +1570,34 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1572
1570
  | undefined;
1573
1571
 
1574
1572
  if (mimeType) {
1575
- if (this.#inspectImageEnabled) {
1576
- const metadata = imageMetadata;
1577
- const outputMime = metadata?.mimeType ?? mimeType;
1578
- const outputBytes = fileSize;
1579
- const metadataLines = [
1580
- "Image metadata:",
1581
- `- MIME: ${outputMime}`,
1582
- `- Bytes: ${outputBytes} (${formatBytes(outputBytes)})`,
1583
- metadata?.width !== undefined && metadata.height !== undefined
1584
- ? `- Dimensions: ${metadata.width}x${metadata.height}`
1585
- : "- Dimensions: unknown",
1586
- metadata?.channels !== undefined ? `- Channels: ${metadata.channels}` : "- Channels: unknown",
1587
- metadata?.hasAlpha === true
1588
- ? "- Alpha: yes"
1589
- : metadata?.hasAlpha === false
1590
- ? "- Alpha: no"
1591
- : "- Alpha: unknown",
1592
- "",
1593
- `If you want to analyze the image, call inspect_image with path="${formatPathRelativeToCwd(
1594
- absolutePath,
1595
- this.session.cwd,
1596
- )}" and a question describing what to inspect and the desired output format.`,
1573
+ if (fileSize > MAX_IMAGE_SIZE) {
1574
+ const sizeStr = formatBytes(fileSize);
1575
+ const maxStr = formatBytes(MAX_IMAGE_SIZE);
1576
+ throw new ToolError(`Image file too large: ${sizeStr} exceeds ${maxStr} limit.`);
1577
+ }
1578
+ try {
1579
+ const imageInput = await loadImageInput({
1580
+ path: readPath,
1581
+ cwd: this.session.cwd,
1582
+ autoResize: this.#autoResizeImages,
1583
+ maxBytes: MAX_IMAGE_SIZE,
1584
+ resolvedPath: absolutePath,
1585
+ detectedMimeType: mimeType,
1586
+ });
1587
+ if (!imageInput) {
1588
+ throw new ToolError(`Read image file [${mimeType}] failed: unsupported image format.`);
1589
+ }
1590
+ content = [
1591
+ { type: "text", text: imageInput.textNote },
1592
+ { type: "image", data: imageInput.data, mimeType: imageInput.mimeType },
1597
1593
  ];
1598
- content = [{ type: "text", text: metadataLines.join("\n") }];
1599
1594
  details = {};
1600
- sourcePath = absolutePath;
1601
- } else {
1602
- if (fileSize > MAX_IMAGE_SIZE) {
1603
- const sizeStr = formatBytes(fileSize);
1604
- const maxStr = formatBytes(MAX_IMAGE_SIZE);
1605
- throw new ToolError(`Image file too large: ${sizeStr} exceeds ${maxStr} limit.`);
1606
- }
1607
- try {
1608
- const imageInput = await loadImageInput({
1609
- path: readPath,
1610
- cwd: this.session.cwd,
1611
- autoResize: this.#autoResizeImages,
1612
- maxBytes: MAX_IMAGE_SIZE,
1613
- resolvedPath: absolutePath,
1614
- detectedMimeType: mimeType,
1615
- });
1616
- if (!imageInput) {
1617
- throw new ToolError(`Read image file [${mimeType}] failed: unsupported image format.`);
1618
- }
1619
- content = [
1620
- { type: "text", text: imageInput.textNote },
1621
- { type: "image", data: imageInput.data, mimeType: imageInput.mimeType },
1622
- ];
1623
- details = {};
1624
- sourcePath = imageInput.resolvedPath;
1625
- } catch (error) {
1626
- if (error instanceof ImageInputTooLargeError) {
1627
- throw new ToolError(error.message);
1628
- }
1629
- throw error;
1595
+ sourcePath = imageInput.resolvedPath;
1596
+ } catch (error) {
1597
+ if (error instanceof ImageInputTooLargeError) {
1598
+ throw new ToolError(error.message);
1630
1599
  }
1600
+ throw error;
1631
1601
  }
1632
1602
  } else if (isNotebookPath(absolutePath) && !isRawSelector(parsed)) {
1633
1603
  const notebookText = await readEditableNotebookText(absolutePath, localReadPath);
@@ -22,7 +22,6 @@ import { debugToolRenderer } from "./debug";
22
22
  import { evalToolRenderer } from "./eval";
23
23
  import { findToolRenderer } from "./find";
24
24
  import { githubToolRenderer } from "./gh-renderer";
25
- import { inspectImageToolRenderer } from "./inspect-image-renderer";
26
25
  import { jobToolRenderer } from "./job";
27
26
  import { readToolRenderer } from "./read";
28
27
  import { recipeToolRenderer } from "./recipe/render";
@@ -63,7 +62,6 @@ export const toolRenderers: Record<string, ToolRenderer> = {
63
62
  find: findToolRenderer as ToolRenderer,
64
63
  search: searchToolRenderer as ToolRenderer,
65
64
  lsp: lspToolRenderer as ToolRenderer,
66
- inspect_image: inspectImageToolRenderer as ToolRenderer,
67
65
  read: readToolRenderer as ToolRenderer,
68
66
  job: jobToolRenderer as ToolRenderer,
69
67
  resolve: resolveToolRenderer as ToolRenderer,