@code-yeongyu/senpi 2026.7.16 → 2026.7.17-2

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 (149) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/core/dynamic-prompt/build.d.ts +7 -0
  3. package/dist/core/dynamic-prompt/build.d.ts.map +1 -1
  4. package/dist/core/dynamic-prompt/build.js +5 -0
  5. package/dist/core/dynamic-prompt/build.js.map +1 -1
  6. package/dist/core/dynamic-prompt/workstation.d.ts +35 -0
  7. package/dist/core/dynamic-prompt/workstation.d.ts.map +1 -0
  8. package/dist/core/dynamic-prompt/workstation.js +81 -0
  9. package/dist/core/dynamic-prompt/workstation.js.map +1 -0
  10. package/dist/core/extensions/builtin/anthropic-web-search/index.d.ts +6 -2
  11. package/dist/core/extensions/builtin/anthropic-web-search/index.d.ts.map +1 -1
  12. package/dist/core/extensions/builtin/anthropic-web-search/index.js +73 -5
  13. package/dist/core/extensions/builtin/anthropic-web-search/index.js.map +1 -1
  14. package/dist/core/extensions/builtin/goal/command-registration.d.ts +1 -0
  15. package/dist/core/extensions/builtin/goal/command-registration.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/goal/command-registration.js +4 -5
  17. package/dist/core/extensions/builtin/goal/command-registration.js.map +1 -1
  18. package/dist/core/extensions/builtin/goal/elapsed-ticker.d.ts +43 -0
  19. package/dist/core/extensions/builtin/goal/elapsed-ticker.d.ts.map +1 -0
  20. package/dist/core/extensions/builtin/goal/elapsed-ticker.js +58 -0
  21. package/dist/core/extensions/builtin/goal/elapsed-ticker.js.map +1 -0
  22. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/goal/index.js +39 -14
  24. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  25. package/dist/core/extensions/builtin/goal/tool-registration.d.ts +1 -0
  26. package/dist/core/extensions/builtin/goal/tool-registration.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/goal/tool-registration.js +3 -4
  28. package/dist/core/extensions/builtin/goal/tool-registration.js.map +1 -1
  29. package/dist/core/extensions/builtin/goal/ui.d.ts +2 -2
  30. package/dist/core/extensions/builtin/goal/ui.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/goal/ui.js +8 -4
  32. package/dist/core/extensions/builtin/goal/ui.js.map +1 -1
  33. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +5 -1
  35. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
  36. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.js +5 -1
  38. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.js.map +1 -1
  39. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.js +5 -1
  41. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.js.map +1 -1
  42. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.js +5 -1
  44. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.js.map +1 -1
  45. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.js +5 -1
  47. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.js.map +1 -1
  48. package/dist/core/extensions/builtin/prompt-preset/glm-5-2.js +1 -1
  49. package/dist/core/extensions/builtin/prompt-preset/glm-5-2.js.map +1 -1
  50. package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.js +1 -1
  51. package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.js.map +1 -1
  52. package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.js +1 -1
  53. package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.js.map +1 -1
  54. package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.js +1 -1
  55. package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.js.map +1 -1
  56. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +1 -1
  57. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js.map +1 -1
  58. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +1 -1
  59. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  60. package/dist/core/extensions/builtin/prompt-preset/gpt-5.js +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/gpt-5.js.map +1 -1
  62. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.js +1 -1
  63. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.js.map +1 -1
  64. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js +1 -1
  65. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js.map +1 -1
  66. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.d.ts +3 -0
  67. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.d.ts.map +1 -0
  68. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +14 -0
  69. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -0
  70. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  71. package/dist/core/extensions/builtin/prompt-preset/presets.js +13 -0
  72. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  73. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  74. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  75. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  76. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  77. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.d.ts.map +1 -1
  78. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.js +2 -1
  79. package/dist/core/extensions/builtin/webfetch/webfetch/renderers.js.map +1 -1
  80. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts +27 -0
  81. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +60 -1
  83. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js.map +1 -1
  84. package/dist/core/extensions/types.d.ts +5 -0
  85. package/dist/core/extensions/types.d.ts.map +1 -1
  86. package/dist/core/extensions/types.js.map +1 -1
  87. package/dist/core/model-config.d.ts +5 -0
  88. package/dist/core/model-config.d.ts.map +1 -1
  89. package/dist/core/model-config.js +1 -0
  90. package/dist/core/model-config.js.map +1 -1
  91. package/dist/modes/interactive/components/tool-execution-renderer.d.ts.map +1 -1
  92. package/dist/modes/interactive/components/tool-execution-renderer.js +1 -0
  93. package/dist/modes/interactive/components/tool-execution-renderer.js.map +1 -1
  94. package/docs/settings.md +1 -1
  95. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  96. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  97. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  98. package/examples/extensions/gondolin/package-lock.json +2 -2
  99. package/examples/extensions/gondolin/package.json +1 -1
  100. package/examples/extensions/sandbox/package-lock.json +2 -2
  101. package/examples/extensions/sandbox/package.json +1 -1
  102. package/examples/extensions/with-deps/package-lock.json +2 -2
  103. package/examples/extensions/with-deps/package.json +1 -1
  104. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +37 -0
  105. package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
  106. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +59 -19
  107. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +68 -2
  108. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +6 -0
  109. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +6 -0
  110. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  111. package/node_modules/@earendil-works/pi-ai/README.md +1 -1
  112. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  113. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +58 -7
  114. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  115. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts +2 -1
  116. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  117. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +6 -1
  118. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +70 -110
  120. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  121. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts +26 -0
  122. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts.map +1 -1
  123. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js +6 -0
  124. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js.map +1 -1
  125. package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.js +3 -3
  126. package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai-cn.models.js.map +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.js +3 -3
  128. package/node_modules/@earendil-works/pi-ai/dist/providers/moonshotai.models.js.map +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts +44 -0
  130. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
  131. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +36 -0
  132. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
  133. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +20 -20
  134. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.d.ts +0 -110
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.d.ts.map +1 -1
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +0 -90
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js.map +1 -1
  139. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +14 -0
  140. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  142. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-schema-compat.d.ts +19 -0
  143. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-schema-compat.d.ts.map +1 -0
  144. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-schema-compat.js +171 -0
  145. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-schema-compat.js.map +1 -0
  146. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  147. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  148. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  149. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # Changelog
2
2
 
3
+ ## [2026.7.17-2] - 2026-07-17
4
+
5
+ ### Breaking Changes
6
+
7
+ ### Added
8
+
9
+ - Added a `<workstation>` block (OS/kernel, arch, CPU, GPU, terminal) to the dynamic system prompt, followed by a dialect-tuned execution-context instruction telling the model that `bash`/`eval` run on this workstation while written code may target other machines. Presets pass their model-family dialect (Claude/GLM, GPT, Kimi); the fallback prompt uses a maximum-emphasis default.
10
+
11
+ ### Changed
12
+
13
+ ### Fixed
14
+
15
+ - Fixed Kimi K3 prompt preset tuning to discourage overthinking and keep concise tool use ([#225](https://github.com/code-yeongyu/senpi/pull/225)).
16
+ - Fixed Moonshot-flavored OpenAI backends by normalizing tool parameters through a dedicated compatibility layer so function schemas and result images are accepted by Moonshot's API ([#225](https://github.com/code-yeongyu/senpi/pull/225)).
17
+
18
+ ### Removed
19
+
20
+ ## [2026.7.17] - 2026-07-17
21
+
22
+ ### Breaking Changes
23
+
24
+ ### Added
25
+
26
+ - Added a Kimi K3 prompt preset for catalog models using `kimi-k3` or the Kimi Coding `k3` id ([#220](https://github.com/code-yeongyu/senpi/pull/220)).
27
+
28
+ ### Changed
29
+
30
+ ### Fixed
31
+
32
+ - Fixed interactive tool renderers to receive result-presence context, allowing self-framed tools to replace their pending view instead of stacking duplicate output ([#223](https://github.com/code-yeongyu/senpi/pull/223)).
33
+ - Fixed the active goal footer to update elapsed time live while preserving accounting parity and stopping the ticker outside active TUI goals ([#221](https://github.com/code-yeongyu/senpi/pull/221)).
34
+ - Fixed builtin webfetch output to cap model-facing content at 50 KB, preserve a UTF-8-safe leading prefix, and report truncation metadata instead of forcing compaction with oversized responses ([#222](https://github.com/code-yeongyu/senpi/pull/222)).
35
+ - Fixed Anthropic native web search gating and replay sanitization for compatible endpoints while preserving the function-tool fallback and allowing explicit `compat.supportsWebSearch` model configuration ([#213](https://github.com/code-yeongyu/senpi/pull/213) by [@tmdgusya](https://github.com/tmdgusya)).
36
+
37
+ ### Removed
38
+
39
+ ## [2026.7.16-3] - 2026-07-16
40
+
41
+ ### Breaking Changes
42
+
43
+ ### Added
44
+
45
+ ### Changed
46
+
47
+ ### Fixed
48
+
49
+ - Fixed inherited OpenCode Go catalog metadata to include Grok 4.5 and Kimi K3, and refreshed OpenRouter pricing metadata from upstream v0.80.10.
50
+
51
+ ### Removed
52
+
53
+ ## [2026.7.16-2] - 2026-07-16
54
+
55
+ ### Breaking Changes
56
+
57
+ ### Added
58
+
59
+ ### Changed
60
+
61
+ ### Fixed
62
+
63
+ - Fixed inherited Kimi K3 pricing metadata for Moonshot AI and Moonshot AI China.
64
+ - Fixed inherited catalog generation restoring xAI models removed in 0.80.9 ([#6736](https://github.com/earendil-works/pi/issues/6736)).
65
+
66
+ ### Removed
67
+
3
68
  ## [2026.7.16] - 2026-07-16
4
69
 
5
70
  ### Breaking Changes
@@ -1,5 +1,6 @@
1
1
  import type { Skill } from "../skills.ts";
2
2
  import type { AvailableTool } from "./types.ts";
3
+ import { type WorkstationDialect } from "./workstation.ts";
3
4
  /** Context handed to a `corePrompt` override so it can reuse the dynamic pieces. */
4
5
  export interface DynamicPromptCoreContext {
5
6
  tools: AvailableTool[];
@@ -23,6 +24,12 @@ export interface BuildDynamicSystemPromptOptions {
23
24
  * date, and cwd assembly stay in this builder.
24
25
  */
25
26
  corePrompt?: (context: DynamicPromptCoreContext) => string;
27
+ /**
28
+ * Wording dialect for the workstation execution-context instruction.
29
+ * Presets pass their model family; the fallback prompt uses `default`
30
+ * (maximum emphasis).
31
+ */
32
+ workstationDialect?: WorkstationDialect;
26
33
  }
27
34
  export declare function buildDynamicSystemPrompt(options: BuildDynamicSystemPromptOptions): string;
28
35
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAU1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,oFAAoF;AACpF,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,MAAM,CAAC;CAC3D;AAmBD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,MAAM,CAiDzF"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAU1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAA2B,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEpF,oFAAoF;AACpF,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,wBAAwB,KAAK,MAAM,CAAC;IAC3D;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAmBD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,MAAM,CAyDzF"}
@@ -8,6 +8,7 @@ import { buildStyleSection } from "./style.js";
8
8
  import { categorizeTools } from "./tool-categorization.js";
9
9
  import { buildToolSection } from "./tool-section.js";
10
10
  import { buildVerificationSection } from "./verification.js";
11
+ import { buildWorkstationSection } from "./workstation.js";
11
12
  function buildContextFilesSection(contextFiles) {
12
13
  if (contextFiles.length === 0) {
13
14
  return "";
@@ -63,6 +64,10 @@ export function buildDynamicSystemPrompt(options) {
63
64
  if (skillsSection) {
64
65
  sections.push(skillsSection);
65
66
  }
67
+ sections.push("", buildWorkstationSection({
68
+ selectedTools: options.selectedTools,
69
+ dialect: options.workstationDialect ?? "default",
70
+ }));
66
71
  sections.push("", `Current date: ${date}`, `Current working directory: ${promptCwd}`);
67
72
  return sections.join("\n");
68
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/build.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Skill } from \"../skills.ts\";\nimport { formatSkillsForPrompt } from \"../skills.ts\";\nimport { buildExplorationSection } from \"./exploration.ts\";\nimport { buildIdentitySection } from \"./identity.ts\";\nimport { buildIntentGate } from \"./intent-gate.ts\";\nimport { buildParallelToolsSection } from \"./parallel-tools.ts\";\nimport { buildPoliciesSection } from \"./policies.ts\";\nimport { buildStyleSection } from \"./style.ts\";\nimport { categorizeTools } from \"./tool-categorization.ts\";\nimport { buildToolSection } from \"./tool-section.ts\";\nimport type { AvailableTool } from \"./types.ts\";\nimport { buildVerificationSection } from \"./verification.ts\";\n\n/** Context handed to a `corePrompt` override so it can reuse the dynamic pieces. */\nexport interface DynamicPromptCoreContext {\n\ttools: AvailableTool[];\n\t/** Rendered \"## Available Tools\" (+ \"## Tool Guidelines\") section. */\n\ttoolSection: string;\n}\n\nexport interface BuildDynamicSystemPromptOptions {\n\tcwd: string;\n\tselectedTools: string[];\n\ttoolSnippets: Record<string, string>;\n\tpromptGuidelines: string[];\n\tcontextFiles: Array<{ path: string; content: string }>;\n\tskills: Skill[];\n\ttuningSection?: string;\n\t/**\n\t * Replaces the default core sections (identity through style) with a\n\t * model-specific full rewrite. Tool section, tuning, context files, skills,\n\t * date, and cwd assembly stay in this builder.\n\t */\n\tcorePrompt?: (context: DynamicPromptCoreContext) => string;\n}\n\nfunction buildContextFilesSection(contextFiles: Array<{ path: string; content: string }>): string {\n\tif (contextFiles.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"## Project Context\",\n\t\t\"\",\n\t\t\"Project instruction files (below, and in [Directory Context: ...] blocks injected during reads) bind files under their directory; deeper files win on conflict; explicit user instructions override.\",\n\t\t\"\",\n\t];\n\tfor (const contextFile of contextFiles) {\n\t\tlines.push(`### ${contextFile.path}`, \"\", contextFile.content.trimEnd(), \"\");\n\t}\n\treturn lines.join(\"\\n\").trimEnd();\n}\n\nexport function buildDynamicSystemPrompt(options: BuildDynamicSystemPromptOptions): string {\n\tconst promptCwd = options.cwd.replace(/\\\\/g, \"/\");\n\tconst tools = categorizeTools(options.selectedTools);\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst toolSection = buildToolSection({\n\t\ttools,\n\t\ttoolSnippets: options.toolSnippets,\n\t\tpromptGuidelines: options.promptGuidelines,\n\t});\n\n\tconst sections = options.corePrompt\n\t\t? [options.corePrompt({ tools, toolSection })]\n\t\t: [\n\t\t\t\tbuildIdentitySection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildIntentGate({ tools }),\n\t\t\t\t\"\",\n\t\t\t\tbuildParallelToolsSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildExplorationSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildVerificationSection(),\n\t\t\t\t\"\",\n\t\t\t\ttoolSection,\n\t\t\t\t\"\",\n\t\t\t\tbuildPoliciesSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildStyleSection(),\n\t\t\t];\n\n\tconst tuning = options.tuningSection?.trim();\n\tif (tuning) {\n\t\tsections.push(\"\", tuning);\n\t}\n\n\tconst contextFilesSection = buildContextFilesSection(options.contextFiles);\n\tif (contextFilesSection) {\n\t\tsections.push(\"\", contextFilesSection);\n\t}\n\n\tconst skillsSection = formatSkillsForPrompt(options.skills);\n\tif (skillsSection) {\n\t\tsections.push(skillsSection);\n\t}\n\n\tsections.push(\"\", `Current date: ${date}`, `Current working directory: ${promptCwd}`);\n\n\treturn sections.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/build.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Skill } from \"../skills.ts\";\nimport { formatSkillsForPrompt } from \"../skills.ts\";\nimport { buildExplorationSection } from \"./exploration.ts\";\nimport { buildIdentitySection } from \"./identity.ts\";\nimport { buildIntentGate } from \"./intent-gate.ts\";\nimport { buildParallelToolsSection } from \"./parallel-tools.ts\";\nimport { buildPoliciesSection } from \"./policies.ts\";\nimport { buildStyleSection } from \"./style.ts\";\nimport { categorizeTools } from \"./tool-categorization.ts\";\nimport { buildToolSection } from \"./tool-section.ts\";\nimport type { AvailableTool } from \"./types.ts\";\nimport { buildVerificationSection } from \"./verification.ts\";\nimport { buildWorkstationSection, type WorkstationDialect } from \"./workstation.ts\";\n\n/** Context handed to a `corePrompt` override so it can reuse the dynamic pieces. */\nexport interface DynamicPromptCoreContext {\n\ttools: AvailableTool[];\n\t/** Rendered \"## Available Tools\" (+ \"## Tool Guidelines\") section. */\n\ttoolSection: string;\n}\n\nexport interface BuildDynamicSystemPromptOptions {\n\tcwd: string;\n\tselectedTools: string[];\n\ttoolSnippets: Record<string, string>;\n\tpromptGuidelines: string[];\n\tcontextFiles: Array<{ path: string; content: string }>;\n\tskills: Skill[];\n\ttuningSection?: string;\n\t/**\n\t * Replaces the default core sections (identity through style) with a\n\t * model-specific full rewrite. Tool section, tuning, context files, skills,\n\t * date, and cwd assembly stay in this builder.\n\t */\n\tcorePrompt?: (context: DynamicPromptCoreContext) => string;\n\t/**\n\t * Wording dialect for the workstation execution-context instruction.\n\t * Presets pass their model family; the fallback prompt uses `default`\n\t * (maximum emphasis).\n\t */\n\tworkstationDialect?: WorkstationDialect;\n}\n\nfunction buildContextFilesSection(contextFiles: Array<{ path: string; content: string }>): string {\n\tif (contextFiles.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"## Project Context\",\n\t\t\"\",\n\t\t\"Project instruction files (below, and in [Directory Context: ...] blocks injected during reads) bind files under their directory; deeper files win on conflict; explicit user instructions override.\",\n\t\t\"\",\n\t];\n\tfor (const contextFile of contextFiles) {\n\t\tlines.push(`### ${contextFile.path}`, \"\", contextFile.content.trimEnd(), \"\");\n\t}\n\treturn lines.join(\"\\n\").trimEnd();\n}\n\nexport function buildDynamicSystemPrompt(options: BuildDynamicSystemPromptOptions): string {\n\tconst promptCwd = options.cwd.replace(/\\\\/g, \"/\");\n\tconst tools = categorizeTools(options.selectedTools);\n\tconst date = new Date().toISOString().slice(0, 10);\n\n\tconst toolSection = buildToolSection({\n\t\ttools,\n\t\ttoolSnippets: options.toolSnippets,\n\t\tpromptGuidelines: options.promptGuidelines,\n\t});\n\n\tconst sections = options.corePrompt\n\t\t? [options.corePrompt({ tools, toolSection })]\n\t\t: [\n\t\t\t\tbuildIdentitySection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildIntentGate({ tools }),\n\t\t\t\t\"\",\n\t\t\t\tbuildParallelToolsSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildExplorationSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildVerificationSection(),\n\t\t\t\t\"\",\n\t\t\t\ttoolSection,\n\t\t\t\t\"\",\n\t\t\t\tbuildPoliciesSection(),\n\t\t\t\t\"\",\n\t\t\t\tbuildStyleSection(),\n\t\t\t];\n\n\tconst tuning = options.tuningSection?.trim();\n\tif (tuning) {\n\t\tsections.push(\"\", tuning);\n\t}\n\n\tconst contextFilesSection = buildContextFilesSection(options.contextFiles);\n\tif (contextFilesSection) {\n\t\tsections.push(\"\", contextFilesSection);\n\t}\n\n\tconst skillsSection = formatSkillsForPrompt(options.skills);\n\tif (skillsSection) {\n\t\tsections.push(skillsSection);\n\t}\n\n\tsections.push(\n\t\t\"\",\n\t\tbuildWorkstationSection({\n\t\t\tselectedTools: options.selectedTools,\n\t\t\tdialect: options.workstationDialect ?? \"default\",\n\t\t}),\n\t);\n\n\tsections.push(\"\", `Current date: ${date}`, `Current working directory: ${promptCwd}`);\n\n\treturn sections.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,35 @@
1
+ /** Snapshot of host facts rendered into the workstation block. */
2
+ export interface WorkstationFacts {
3
+ /** `os.platform()`, e.g. `darwin`. */
4
+ readonly osLine: string;
5
+ /** Kernel identity: `<uname -s> <uname -r>`, e.g. `Darwin 25.3.0`. */
6
+ readonly kernel: string;
7
+ /** `os.arch()`, e.g. `arm64`. */
8
+ readonly arch: string;
9
+ /** CPU model plus logical core count, e.g. `Apple M5 Max (18 cores)`. */
10
+ readonly cpu?: string;
11
+ /** GPU model when cheaply derivable (Apple Silicon shares the SoC name). */
12
+ readonly gpu?: string;
13
+ /** Terminal emulator name/version from TERM_PROGRAM/TERM. */
14
+ readonly terminal?: string;
15
+ }
16
+ /**
17
+ * Wording dialect for the execution-context instruction under the facts.
18
+ * Families follow the prompt-preset conventions: Claude/GLM take direct
19
+ * imperatives in a tagged block, GPT (codex) takes terse bounded rules, Kimi
20
+ * takes positive operational constraints (all-caps directives make K2.x
21
+ * overthink), and `default` is the maximum-emphasis fallback for unmatched
22
+ * models.
23
+ */
24
+ export type WorkstationDialect = "default" | "claude" | "codex" | "kimi";
25
+ /** Collect host facts once per process; every call after the first is free. */
26
+ export declare function collectWorkstationFacts(): WorkstationFacts;
27
+ export interface BuildWorkstationSectionOptions {
28
+ readonly selectedTools: readonly string[];
29
+ readonly dialect?: WorkstationDialect;
30
+ /** Override collected facts (tests). */
31
+ readonly facts?: WorkstationFacts;
32
+ }
33
+ /** Render the `<workstation>` facts block plus the dialect-tuned execution-context instruction. */
34
+ export declare function buildWorkstationSection(options: BuildWorkstationSectionOptions): string;
35
+ //# sourceMappingURL=workstation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workstation.d.ts","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/workstation.ts"],"names":[],"mappings":"AAGA,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAChC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAmCzE,+EAA+E;AAC/E,wBAAgB,uBAAuB,IAAI,gBAAgB,CAgB1D;AAsBD,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IACtC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED,mGAAmG;AACnG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM,CAYvF"}
@@ -0,0 +1,81 @@
1
+ import { readFileSync } from "node:fs";
2
+ import * as os from "node:os";
3
+ // Kernel identity: `<uname -s> <uname -r>` (`Darwin 25.3.0`, `Linux 6.8.0-45-generic`).
4
+ // Deliberately NOT `os.version()` — Node returns the full xnu build string on
5
+ // macOS (noisy) and some runtimes return the literal "unknown", which makes
6
+ // models misidentify the host platform.
7
+ function cpuModel() {
8
+ if (process.platform === "linux") {
9
+ try {
10
+ const cpuInfo = readFileSync("/proc/cpuinfo", "utf8");
11
+ const match = /^model name\s*:\s*(.+)$/m.exec(cpuInfo);
12
+ if (match?.[1])
13
+ return match[1].trim();
14
+ }
15
+ catch {
16
+ // fall through to os.cpus()
17
+ }
18
+ }
19
+ return os.cpus()[0]?.model?.trim() || undefined;
20
+ }
21
+ // GPU: only derivable without a subprocess on Apple Silicon, where the SoC
22
+ // name doubles as the GPU name. Other platforms would need nvidia-smi/lspci —
23
+ // deliberately skipped to keep prompt assembly synchronous.
24
+ function terminalName() {
25
+ const program = process.env.TERM_PROGRAM?.trim();
26
+ if (program) {
27
+ const version = process.env.TERM_PROGRAM_VERSION?.trim();
28
+ return version ? `${program} ${version}` : program;
29
+ }
30
+ return process.env.TERM?.trim() || undefined;
31
+ }
32
+ let cachedFacts;
33
+ /** Collect host facts once per process; every call after the first is free. */
34
+ export function collectWorkstationFacts() {
35
+ if (cachedFacts)
36
+ return cachedFacts;
37
+ const cpu = cpuModel();
38
+ const cores = os.availableParallelism();
39
+ const gpu = process.platform === "darwin" && os.arch() === "arm64" && cpu?.startsWith("Apple ") ? cpu : undefined;
40
+ const terminal = terminalName();
41
+ const facts = {
42
+ osLine: os.platform(),
43
+ kernel: `${os.type()} ${os.release()}`.trim(),
44
+ arch: os.arch(),
45
+ ...(cpu === undefined ? {} : { cpu: `${cpu} (${cores} cores)` }),
46
+ ...(gpu === undefined ? {} : { gpu }),
47
+ ...(terminal === undefined ? {} : { terminal }),
48
+ };
49
+ cachedFacts = facts;
50
+ return facts;
51
+ }
52
+ /** Subject + verb naming the local executors, e.g. "`bash` and `eval` execute". */
53
+ function executorPhrase(selectedTools) {
54
+ const executors = ["bash", "eval"].filter((name) => selectedTools.includes(name));
55
+ if (executors.length === 0)
56
+ return "Everything you run executes";
57
+ return `${executors.map((name) => `\`${name}\``).join(" and ")} execute${executors.length === 1 ? "s" : ""}`;
58
+ }
59
+ const INSTRUCTIONS = {
60
+ claude: (executors) => `<execution_context>
61
+ ${executors} on THIS workstation. Match commands, paths, package managers, and parallel pool sizes to it. Code you write may target any machine; code you run always runs here.
62
+ </execution_context>`,
63
+ codex: (executors) => `${executors} on this workstation; match commands, paths, and parallelism to it. Written code may target other platforms — executed code runs here.`,
64
+ kimi: (executors) => `${executors} on this workstation — choose commands, paths, and parallelism that fit it. When writing code for another target platform, keep the code portable; only the execution happens here.`,
65
+ default: (executors) => `**EXECUTION HAPPENS HERE.** ${executors} on THIS machine — you MUST match commands, paths, package managers, and parallel pool sizes to the workstation above. Code you WRITE may target a different machine; anything you RUN executes HERE.`,
66
+ };
67
+ /** Render the `<workstation>` facts block plus the dialect-tuned execution-context instruction. */
68
+ export function buildWorkstationSection(options) {
69
+ const facts = options.facts ?? collectWorkstationFacts();
70
+ const dialect = options.dialect ?? "default";
71
+ const lines = [
72
+ `- OS: ${facts.osLine} (kernel ${facts.kernel})`,
73
+ `- Arch: ${facts.arch}`,
74
+ ...(facts.cpu ? [`- CPU: ${facts.cpu}`] : []),
75
+ ...(facts.gpu ? [`- GPU: ${facts.gpu}`] : []),
76
+ ...(facts.terminal ? [`- Terminal: ${facts.terminal}`] : []),
77
+ ];
78
+ const instruction = INSTRUCTIONS[dialect](executorPhrase(options.selectedTools));
79
+ return `<workstation>\n${lines.join("\n")}\n</workstation>\n${instruction}`;
80
+ }
81
+ //# sourceMappingURL=workstation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workstation.js","sourceRoot":"","sources":["../../../src/core/dynamic-prompt/workstation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AA4B9B,wFAAwF;AACxF,8EAA8E;AAC9E,4EAA4E;AAC5E,wCAAwC;AAExC,SAAS,QAAQ;IAChB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACR,4BAA4B;QAC7B,CAAC;IACF,CAAC;IACD,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;AACjD,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,4DAA4D;AAE5D,SAAS,YAAY;IACpB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IACjD,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC;QACzD,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACpD,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;AAC9C,CAAC;AAED,IAAI,WAAyC,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB;IACtC,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,EAAE,CAAC,oBAAoB,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,OAAO,IAAI,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAClH,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,KAAK,GAAqB;QAC/B,MAAM,EAAE,EAAE,CAAC,QAAQ,EAAE;QACrB,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,EAAE;QAC7C,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;QACf,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,GAAG,KAAK,KAAK,SAAS,EAAE,CAAC;QAChE,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;KAC/C,CAAC;IACF,WAAW,GAAG,KAAK,CAAC;IACpB,OAAO,KAAK,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,SAAS,cAAc,CAAC,aAAgC;IACvD,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAClF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,6BAA6B,CAAC;IACjE,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC9G,CAAC;AAED,MAAM,YAAY,GAA8D;IAC/E,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,CACrB;EACA,SAAS;qBACU;IACpB,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE,CACpB,GAAG,SAAS,wIAAwI;IACrJ,IAAI,EAAE,CAAC,SAAS,EAAE,EAAE,CACnB,GAAG,SAAS,qLAAqL;IAClM,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,CACtB,+BAA+B,SAAS,uMAAuM;CAChP,CAAC;AASF,mGAAmG;AACnG,MAAM,UAAU,uBAAuB,CAAC,OAAuC;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,uBAAuB,EAAE,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAC7C,MAAM,KAAK,GAAG;QACb,SAAS,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,GAAG;QAChD,WAAW,KAAK,CAAC,IAAI,EAAE;QACvB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,eAAe,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC;IACF,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IACjF,OAAO,kBAAkB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,WAAW,EAAE,CAAC;AAC7E,CAAC","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport * as os from \"node:os\";\n\n/** Snapshot of host facts rendered into the workstation block. */\nexport interface WorkstationFacts {\n\t/** `os.platform()`, e.g. `darwin`. */\n\treadonly osLine: string;\n\t/** Kernel identity: `<uname -s> <uname -r>`, e.g. `Darwin 25.3.0`. */\n\treadonly kernel: string;\n\t/** `os.arch()`, e.g. `arm64`. */\n\treadonly arch: string;\n\t/** CPU model plus logical core count, e.g. `Apple M5 Max (18 cores)`. */\n\treadonly cpu?: string;\n\t/** GPU model when cheaply derivable (Apple Silicon shares the SoC name). */\n\treadonly gpu?: string;\n\t/** Terminal emulator name/version from TERM_PROGRAM/TERM. */\n\treadonly terminal?: string;\n}\n\n/**\n * Wording dialect for the execution-context instruction under the facts.\n * Families follow the prompt-preset conventions: Claude/GLM take direct\n * imperatives in a tagged block, GPT (codex) takes terse bounded rules, Kimi\n * takes positive operational constraints (all-caps directives make K2.x\n * overthink), and `default` is the maximum-emphasis fallback for unmatched\n * models.\n */\nexport type WorkstationDialect = \"default\" | \"claude\" | \"codex\" | \"kimi\";\n\n// Kernel identity: `<uname -s> <uname -r>` (`Darwin 25.3.0`, `Linux 6.8.0-45-generic`).\n// Deliberately NOT `os.version()` — Node returns the full xnu build string on\n// macOS (noisy) and some runtimes return the literal \"unknown\", which makes\n// models misidentify the host platform.\n\nfunction cpuModel(): string | undefined {\n\tif (process.platform === \"linux\") {\n\t\ttry {\n\t\t\tconst cpuInfo = readFileSync(\"/proc/cpuinfo\", \"utf8\");\n\t\t\tconst match = /^model name\\s*:\\s*(.+)$/m.exec(cpuInfo);\n\t\t\tif (match?.[1]) return match[1].trim();\n\t\t} catch {\n\t\t\t// fall through to os.cpus()\n\t\t}\n\t}\n\treturn os.cpus()[0]?.model?.trim() || undefined;\n}\n\n// GPU: only derivable without a subprocess on Apple Silicon, where the SoC\n// name doubles as the GPU name. Other platforms would need nvidia-smi/lspci —\n// deliberately skipped to keep prompt assembly synchronous.\n\nfunction terminalName(): string | undefined {\n\tconst program = process.env.TERM_PROGRAM?.trim();\n\tif (program) {\n\t\tconst version = process.env.TERM_PROGRAM_VERSION?.trim();\n\t\treturn version ? `${program} ${version}` : program;\n\t}\n\treturn process.env.TERM?.trim() || undefined;\n}\n\nlet cachedFacts: WorkstationFacts | undefined;\n\n/** Collect host facts once per process; every call after the first is free. */\nexport function collectWorkstationFacts(): WorkstationFacts {\n\tif (cachedFacts) return cachedFacts;\n\tconst cpu = cpuModel();\n\tconst cores = os.availableParallelism();\n\tconst gpu = process.platform === \"darwin\" && os.arch() === \"arm64\" && cpu?.startsWith(\"Apple \") ? cpu : undefined;\n\tconst terminal = terminalName();\n\tconst facts: WorkstationFacts = {\n\t\tosLine: os.platform(),\n\t\tkernel: `${os.type()} ${os.release()}`.trim(),\n\t\tarch: os.arch(),\n\t\t...(cpu === undefined ? {} : { cpu: `${cpu} (${cores} cores)` }),\n\t\t...(gpu === undefined ? {} : { gpu }),\n\t\t...(terminal === undefined ? {} : { terminal }),\n\t};\n\tcachedFacts = facts;\n\treturn facts;\n}\n\n/** Subject + verb naming the local executors, e.g. \"`bash` and `eval` execute\". */\nfunction executorPhrase(selectedTools: readonly string[]): string {\n\tconst executors = [\"bash\", \"eval\"].filter((name) => selectedTools.includes(name));\n\tif (executors.length === 0) return \"Everything you run executes\";\n\treturn `${executors.map((name) => `\\`${name}\\``).join(\" and \")} execute${executors.length === 1 ? \"s\" : \"\"}`;\n}\n\nconst INSTRUCTIONS: Record<WorkstationDialect, (executors: string) => string> = {\n\tclaude: (executors) =>\n\t\t`<execution_context>\n${executors} on THIS workstation. Match commands, paths, package managers, and parallel pool sizes to it. Code you write may target any machine; code you run always runs here.\n</execution_context>`,\n\tcodex: (executors) =>\n\t\t`${executors} on this workstation; match commands, paths, and parallelism to it. Written code may target other platforms — executed code runs here.`,\n\tkimi: (executors) =>\n\t\t`${executors} on this workstation — choose commands, paths, and parallelism that fit it. When writing code for another target platform, keep the code portable; only the execution happens here.`,\n\tdefault: (executors) =>\n\t\t`**EXECUTION HAPPENS HERE.** ${executors} on THIS machine — you MUST match commands, paths, package managers, and parallel pool sizes to the workstation above. Code you WRITE may target a different machine; anything you RUN executes HERE.`,\n};\n\nexport interface BuildWorkstationSectionOptions {\n\treadonly selectedTools: readonly string[];\n\treadonly dialect?: WorkstationDialect;\n\t/** Override collected facts (tests). */\n\treadonly facts?: WorkstationFacts;\n}\n\n/** Render the `<workstation>` facts block plus the dialect-tuned execution-context instruction. */\nexport function buildWorkstationSection(options: BuildWorkstationSectionOptions): string {\n\tconst facts = options.facts ?? collectWorkstationFacts();\n\tconst dialect = options.dialect ?? \"default\";\n\tconst lines = [\n\t\t`- OS: ${facts.osLine} (kernel ${facts.kernel})`,\n\t\t`- Arch: ${facts.arch}`,\n\t\t...(facts.cpu ? [`- CPU: ${facts.cpu}`] : []),\n\t\t...(facts.gpu ? [`- GPU: ${facts.gpu}`] : []),\n\t\t...(facts.terminal ? [`- Terminal: ${facts.terminal}`] : []),\n\t];\n\tconst instruction = INSTRUCTIONS[dialect](executorPhrase(options.selectedTools));\n\treturn `<workstation>\\n${lines.join(\"\\n\")}\\n</workstation>\\n${instruction}`;\n}\n"]}
@@ -1,7 +1,11 @@
1
- import type { Api } from "@earendil-works/pi-ai";
1
+ import type { Api, Model } from "@earendil-works/pi-ai";
2
2
  import type { ExtensionAPI } from "../../types.ts";
3
- export declare function addAnthropicWebSearchToPayload(api: Api | undefined, payload: unknown): unknown;
3
+ type AnthropicWebSearchModel = Pick<Model<Api>, "api" | "provider" | "baseUrl" | "compat">;
4
+ type AnthropicWebSearchTarget = Api | AnthropicWebSearchModel | undefined;
5
+ export declare function supportsNativeAnthropicWebSearch(target: AnthropicWebSearchTarget): boolean;
6
+ export declare function addAnthropicWebSearchToPayload(target: AnthropicWebSearchTarget, payload: unknown): unknown;
4
7
  export declare function isAnthropicWebSearchEnabled(): boolean;
5
8
  export declare const ANTHROPIC_WEB_SEARCH_SECTION = "\n## Web Search\n\nThe native web_search tool is available in this session.\nUse web_search when the user asks for current or online information.\nPrefer web_search over guessing when freshness matters.\n";
6
9
  export default function anthropicWebSearchExtension(pi: ExtensionAPI): void;
10
+ export {};
7
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/anthropic-web-search/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAsFrE,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAwB9F;AAED,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAYD,eAAO,MAAM,4BAA4B,iNAMxC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,2BAA2B,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8B1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/anthropic-web-search/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAGrE,KAAK,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC,CAAC;AAC3F,KAAK,wBAAwB,GAAG,GAAG,GAAG,uBAAuB,GAAG,SAAS,CAAC;AA8D1E,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAQ1F;AAiFD,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,wBAAwB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAkC1G;AAED,wBAAgB,2BAA2B,IAAI,OAAO,CAErD;AAYD,eAAO,MAAM,4BAA4B,iNAMxC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,2BAA2B,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8B1E"}
@@ -25,6 +25,39 @@ function isRecord(value) {
25
25
  function isWebSearchType(value) {
26
26
  return typeof value === "string" && value.startsWith("web_search_");
27
27
  }
28
+ function resolveTarget(target) {
29
+ if (target === undefined) {
30
+ return undefined;
31
+ }
32
+ if (typeof target === "string") {
33
+ return {
34
+ api: target,
35
+ baseUrl: target === "anthropic-messages" ? "https://api.anthropic.com" : "",
36
+ provider: target === "anthropic-messages" ? "anthropic" : "",
37
+ };
38
+ }
39
+ return target;
40
+ }
41
+ function isNativeAnthropicEndpoint(model) {
42
+ try {
43
+ return new URL(model.baseUrl).hostname === "api.anthropic.com";
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ // Mirrors pi-ai's `AnthropicMessagesCompat.supportsWebSearch` default:
50
+ // first-party api.anthropic.com only. Anthropic-compatible endpoints (e.g.,
51
+ // kimi-coding) may execute the server-side search but reject the replayed
52
+ // server_tool_use / web_search_tool_result blocks on the next request.
53
+ export function supportsNativeAnthropicWebSearch(target) {
54
+ const model = resolveTarget(target);
55
+ if (model?.api !== "anthropic-messages") {
56
+ return false;
57
+ }
58
+ const compat = model.compat;
59
+ return compat?.supportsWebSearch ?? isNativeAnthropicEndpoint(model);
60
+ }
28
61
  function parseDomainListEnv(envVar) {
29
62
  const envValue = process.env[envVar];
30
63
  if (!envValue) {
@@ -65,16 +98,51 @@ function sanitizeTools(tools) {
65
98
  }
66
99
  return sanitized;
67
100
  }
68
- export function addAnthropicWebSearchToPayload(api, payload) {
69
- if (api !== "anthropic-messages") {
101
+ function stripNativeAnthropicWebSearch(payload) {
102
+ const tools = payload.tools;
103
+ if (!Array.isArray(tools)) {
70
104
  return payload;
71
105
  }
72
- if (!isAnthropicWebSearchEnabled()) {
106
+ const sanitizedTools = tools.filter((tool) => !(isRecord(tool) && isWebSearchType(tool.type)));
107
+ if (sanitizedTools.length === tools.length) {
108
+ return payload;
109
+ }
110
+ const sanitizedPayload = { ...payload };
111
+ if (sanitizedTools.length > 0) {
112
+ sanitizedPayload.tools = sanitizedTools;
113
+ }
114
+ else {
115
+ delete sanitizedPayload.tools;
116
+ }
117
+ if (isRecord(sanitizedPayload.tool_choice)) {
118
+ const selectedName = sanitizedPayload.tool_choice.name;
119
+ const hasSelectedTool = typeof selectedName === "string" &&
120
+ sanitizedTools.some((tool) => isRecord(tool) && tool.name === selectedName);
121
+ if (sanitizedTools.length === 0 || (typeof selectedName === "string" && !hasSelectedTool)) {
122
+ delete sanitizedPayload.tool_choice;
123
+ }
124
+ }
125
+ return sanitizedPayload;
126
+ }
127
+ export function addAnthropicWebSearchToPayload(target, payload) {
128
+ const model = resolveTarget(target);
129
+ if (model?.api !== "anthropic-messages") {
73
130
  return payload;
74
131
  }
75
132
  if (!isRecord(payload)) {
76
133
  return payload;
77
134
  }
135
+ if (!supportsNativeAnthropicWebSearch(model)) {
136
+ // Anthropic-compatible endpoints (e.g., kimi-coding) may execute the
137
+ // server-side search but reject the replayed server_tool_use /
138
+ // web_search_tool_result blocks on the next request. Strip any native
139
+ // web_search_* variant so the session never wedges; a function-tool
140
+ // web_search (pi-websearch) is left untouched as the working fallback.
141
+ return stripNativeAnthropicWebSearch(payload);
142
+ }
143
+ if (!isAnthropicWebSearchEnabled()) {
144
+ return payload;
145
+ }
78
146
  const tools = Array.isArray(payload.tools) ? payload.tools : [];
79
147
  const sanitizedTools = sanitizeTools(tools);
80
148
  const hasNativeWebSearch = sanitizedTools.some((tool) => isWebSearchType(tool.type));
@@ -107,7 +175,7 @@ Prefer web_search over guessing when freshness matters.
107
175
  `;
108
176
  export default function anthropicWebSearchExtension(pi) {
109
177
  pi.on("before_provider_request", (event, ctx) => {
110
- return addAnthropicWebSearchToPayload(ctx.model?.api, event.payload);
178
+ return addAnthropicWebSearchToPayload(ctx.model, event.payload);
111
179
  });
112
180
  pi.on("session_start", async (_event, ctx) => {
113
181
  syncUi(ctx);
@@ -119,7 +187,7 @@ export default function anthropicWebSearchExtension(pi) {
119
187
  clearUi(ctx);
120
188
  });
121
189
  pi.on("before_agent_start", async (event, ctx) => {
122
- if (ctx.model?.api !== "anthropic-messages") {
190
+ if (!supportsNativeAnthropicWebSearch(ctx.model)) {
123
191
  return undefined;
124
192
  }
125
193
  if (!isAnthropicWebSearchEnabled()) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/anthropic-web-search/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api } from \"@earendil-works/pi-ai\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\n\ntype ToolDefinition = Record<string, unknown>;\n\nconst WEB_SEARCH_MAX_USES = 8;\nconst ENABLE_ENV = \"PI_ANTHROPIC_WEB_SEARCH\";\nconst ALLOWED_DOMAINS_ENV = \"PI_ANTHROPIC_WEB_SEARCH_ALLOWED_DOMAINS\";\nconst BLOCKED_DOMAINS_ENV = \"PI_ANTHROPIC_WEB_SEARCH_BLOCKED_DOMAINS\";\nconst STATUS_KEY = \"anthropic-web-search\";\nconst WIDGET_KEY = \"anthropic-web-search\";\n\nfunction parseEnableEnv(envVar: string): boolean {\n\tconst envValue = process.env[envVar];\n\tif (!envValue) {\n\t\treturn true;\n\t}\n\n\tconst normalized = envValue.trim().toLowerCase();\n\tif (normalized === \"0\" || normalized === \"false\" || normalized === \"no\" || normalized === \"off\") {\n\t\treturn false;\n\t}\n\n\tif (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\") {\n\t\treturn true;\n\t}\n\n\t// Unknown values fall back to default-on behavior.\n\treturn true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isWebSearchType(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.startsWith(\"web_search_\");\n}\n\nfunction parseDomainListEnv(envVar: string): string[] | undefined {\n\tconst envValue = process.env[envVar];\n\tif (!envValue) {\n\t\treturn undefined;\n\t}\n\n\tconst domains = envValue\n\t\t.split(\",\")\n\t\t.map((domain) => domain.trim())\n\t\t.filter((domain) => domain.length > 0);\n\n\tif (domains.length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn domains;\n}\n\nfunction makeWebSearchTool(): ToolDefinition {\n\tconst allowedDomains = parseDomainListEnv(ALLOWED_DOMAINS_ENV);\n\tconst blockedDomains = parseDomainListEnv(BLOCKED_DOMAINS_ENV);\n\n\treturn {\n\t\ttype: \"web_search_20250305\",\n\t\tname: \"web_search\",\n\t\t...(allowedDomains ? { allowed_domains: allowedDomains } : {}),\n\t\t...(blockedDomains ? { blocked_domains: blockedDomains } : {}),\n\t\tmax_uses: WEB_SEARCH_MAX_USES,\n\t};\n}\n\nfunction sanitizeTools(tools: unknown[]): ToolDefinition[] {\n\tconst sanitized: ToolDefinition[] = [];\n\tfor (const tool of tools) {\n\t\tif (!isRecord(tool)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst name = tool.name;\n\t\tconst type = tool.type;\n\t\tconst shouldStripFunctionVariant = name === \"web_search\" && !isWebSearchType(type);\n\t\tif (!shouldStripFunctionVariant) {\n\t\t\tsanitized.push(tool);\n\t\t}\n\t}\n\treturn sanitized;\n}\n\nexport function addAnthropicWebSearchToPayload(api: Api | undefined, payload: unknown): unknown {\n\tif (api !== \"anthropic-messages\") {\n\t\treturn payload;\n\t}\n\n\tif (!isAnthropicWebSearchEnabled()) {\n\t\treturn payload;\n\t}\n\n\tif (!isRecord(payload)) {\n\t\treturn payload;\n\t}\n\n\tconst tools = Array.isArray(payload.tools) ? payload.tools : [];\n\tconst sanitizedTools = sanitizeTools(tools);\n\tconst hasNativeWebSearch = sanitizedTools.some((tool) => isWebSearchType(tool.type));\n\tif (!hasNativeWebSearch) {\n\t\tsanitizedTools.push(makeWebSearchTool());\n\t}\n\n\treturn {\n\t\t...payload,\n\t\ttools: sanitizedTools,\n\t};\n}\n\nexport function isAnthropicWebSearchEnabled(): boolean {\n\treturn parseEnableEnv(ENABLE_ENV);\n}\n\nfunction clearUi(ctx: ExtensionContext): void {\n\tif (!ctx.hasUI) return;\n\tctx.ui.setStatus(STATUS_KEY, undefined);\n\tctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n\nfunction syncUi(ctx: ExtensionContext): void {\n\tclearUi(ctx);\n}\n\nexport const ANTHROPIC_WEB_SEARCH_SECTION = `\n## Web Search\n\nThe native web_search tool is available in this session.\nUse web_search when the user asks for current or online information.\nPrefer web_search over guessing when freshness matters.\n`;\n\nexport default function anthropicWebSearchExtension(pi: ExtensionAPI): void {\n\tpi.on(\"before_provider_request\", (event, ctx) => {\n\t\treturn addAnthropicWebSearchToPayload(ctx.model?.api, event.payload);\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsyncUi(ctx);\n\t});\n\n\tpi.on(\"model_select\", async (_event, ctx) => {\n\t\tsyncUi(ctx);\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tclearUi(ctx);\n\t});\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\tif (ctx.model?.api !== \"anthropic-messages\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!isAnthropicWebSearchEnabled()) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tsystemPrompt: `${event.systemPrompt}\\n${ANTHROPIC_WEB_SEARCH_SECTION}`,\n\t\t};\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/anthropic-web-search/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnthropicMessagesCompat, Api, Model } from \"@earendil-works/pi-ai\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\n\ntype ToolDefinition = Record<string, unknown>;\ntype AnthropicWebSearchModel = Pick<Model<Api>, \"api\" | \"provider\" | \"baseUrl\" | \"compat\">;\ntype AnthropicWebSearchTarget = Api | AnthropicWebSearchModel | undefined;\n\nconst WEB_SEARCH_MAX_USES = 8;\nconst ENABLE_ENV = \"PI_ANTHROPIC_WEB_SEARCH\";\nconst ALLOWED_DOMAINS_ENV = \"PI_ANTHROPIC_WEB_SEARCH_ALLOWED_DOMAINS\";\nconst BLOCKED_DOMAINS_ENV = \"PI_ANTHROPIC_WEB_SEARCH_BLOCKED_DOMAINS\";\nconst STATUS_KEY = \"anthropic-web-search\";\nconst WIDGET_KEY = \"anthropic-web-search\";\n\nfunction parseEnableEnv(envVar: string): boolean {\n\tconst envValue = process.env[envVar];\n\tif (!envValue) {\n\t\treturn true;\n\t}\n\n\tconst normalized = envValue.trim().toLowerCase();\n\tif (normalized === \"0\" || normalized === \"false\" || normalized === \"no\" || normalized === \"off\") {\n\t\treturn false;\n\t}\n\n\tif (normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\") {\n\t\treturn true;\n\t}\n\n\t// Unknown values fall back to default-on behavior.\n\treturn true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isWebSearchType(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.startsWith(\"web_search_\");\n}\n\nfunction resolveTarget(target: AnthropicWebSearchTarget): AnthropicWebSearchModel | undefined {\n\tif (target === undefined) {\n\t\treturn undefined;\n\t}\n\tif (typeof target === \"string\") {\n\t\treturn {\n\t\t\tapi: target,\n\t\t\tbaseUrl: target === \"anthropic-messages\" ? \"https://api.anthropic.com\" : \"\",\n\t\t\tprovider: target === \"anthropic-messages\" ? \"anthropic\" : \"\",\n\t\t};\n\t}\n\treturn target;\n}\n\nfunction isNativeAnthropicEndpoint(model: AnthropicWebSearchModel): boolean {\n\ttry {\n\t\treturn new URL(model.baseUrl).hostname === \"api.anthropic.com\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Mirrors pi-ai's `AnthropicMessagesCompat.supportsWebSearch` default:\n// first-party api.anthropic.com only. Anthropic-compatible endpoints (e.g.,\n// kimi-coding) may execute the server-side search but reject the replayed\n// server_tool_use / web_search_tool_result blocks on the next request.\nexport function supportsNativeAnthropicWebSearch(target: AnthropicWebSearchTarget): boolean {\n\tconst model = resolveTarget(target);\n\tif (model?.api !== \"anthropic-messages\") {\n\t\treturn false;\n\t}\n\n\tconst compat = model.compat as AnthropicMessagesCompat | undefined;\n\treturn compat?.supportsWebSearch ?? isNativeAnthropicEndpoint(model);\n}\n\nfunction parseDomainListEnv(envVar: string): string[] | undefined {\n\tconst envValue = process.env[envVar];\n\tif (!envValue) {\n\t\treturn undefined;\n\t}\n\n\tconst domains = envValue\n\t\t.split(\",\")\n\t\t.map((domain) => domain.trim())\n\t\t.filter((domain) => domain.length > 0);\n\n\tif (domains.length === 0) {\n\t\treturn undefined;\n\t}\n\n\treturn domains;\n}\n\nfunction makeWebSearchTool(): ToolDefinition {\n\tconst allowedDomains = parseDomainListEnv(ALLOWED_DOMAINS_ENV);\n\tconst blockedDomains = parseDomainListEnv(BLOCKED_DOMAINS_ENV);\n\n\treturn {\n\t\ttype: \"web_search_20250305\",\n\t\tname: \"web_search\",\n\t\t...(allowedDomains ? { allowed_domains: allowedDomains } : {}),\n\t\t...(blockedDomains ? { blocked_domains: blockedDomains } : {}),\n\t\tmax_uses: WEB_SEARCH_MAX_USES,\n\t};\n}\n\nfunction sanitizeTools(tools: unknown[]): ToolDefinition[] {\n\tconst sanitized: ToolDefinition[] = [];\n\tfor (const tool of tools) {\n\t\tif (!isRecord(tool)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst name = tool.name;\n\t\tconst type = tool.type;\n\t\tconst shouldStripFunctionVariant = name === \"web_search\" && !isWebSearchType(type);\n\t\tif (!shouldStripFunctionVariant) {\n\t\t\tsanitized.push(tool);\n\t\t}\n\t}\n\treturn sanitized;\n}\n\nfunction stripNativeAnthropicWebSearch(payload: Record<string, unknown>): Record<string, unknown> {\n\tconst tools = payload.tools;\n\tif (!Array.isArray(tools)) {\n\t\treturn payload;\n\t}\n\n\tconst sanitizedTools = tools.filter((tool) => !(isRecord(tool) && isWebSearchType(tool.type)));\n\tif (sanitizedTools.length === tools.length) {\n\t\treturn payload;\n\t}\n\n\tconst sanitizedPayload = { ...payload };\n\tif (sanitizedTools.length > 0) {\n\t\tsanitizedPayload.tools = sanitizedTools;\n\t} else {\n\t\tdelete sanitizedPayload.tools;\n\t}\n\n\tif (isRecord(sanitizedPayload.tool_choice)) {\n\t\tconst selectedName = sanitizedPayload.tool_choice.name;\n\t\tconst hasSelectedTool =\n\t\t\ttypeof selectedName === \"string\" &&\n\t\t\tsanitizedTools.some((tool) => isRecord(tool) && tool.name === selectedName);\n\t\tif (sanitizedTools.length === 0 || (typeof selectedName === \"string\" && !hasSelectedTool)) {\n\t\t\tdelete sanitizedPayload.tool_choice;\n\t\t}\n\t}\n\n\treturn sanitizedPayload;\n}\n\nexport function addAnthropicWebSearchToPayload(target: AnthropicWebSearchTarget, payload: unknown): unknown {\n\tconst model = resolveTarget(target);\n\tif (model?.api !== \"anthropic-messages\") {\n\t\treturn payload;\n\t}\n\n\tif (!isRecord(payload)) {\n\t\treturn payload;\n\t}\n\n\tif (!supportsNativeAnthropicWebSearch(model)) {\n\t\t// Anthropic-compatible endpoints (e.g., kimi-coding) may execute the\n\t\t// server-side search but reject the replayed server_tool_use /\n\t\t// web_search_tool_result blocks on the next request. Strip any native\n\t\t// web_search_* variant so the session never wedges; a function-tool\n\t\t// web_search (pi-websearch) is left untouched as the working fallback.\n\t\treturn stripNativeAnthropicWebSearch(payload);\n\t}\n\n\tif (!isAnthropicWebSearchEnabled()) {\n\t\treturn payload;\n\t}\n\n\tconst tools = Array.isArray(payload.tools) ? payload.tools : [];\n\tconst sanitizedTools = sanitizeTools(tools);\n\tconst hasNativeWebSearch = sanitizedTools.some((tool) => isWebSearchType(tool.type));\n\tif (!hasNativeWebSearch) {\n\t\tsanitizedTools.push(makeWebSearchTool());\n\t}\n\n\treturn {\n\t\t...payload,\n\t\ttools: sanitizedTools,\n\t};\n}\n\nexport function isAnthropicWebSearchEnabled(): boolean {\n\treturn parseEnableEnv(ENABLE_ENV);\n}\n\nfunction clearUi(ctx: ExtensionContext): void {\n\tif (!ctx.hasUI) return;\n\tctx.ui.setStatus(STATUS_KEY, undefined);\n\tctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n\nfunction syncUi(ctx: ExtensionContext): void {\n\tclearUi(ctx);\n}\n\nexport const ANTHROPIC_WEB_SEARCH_SECTION = `\n## Web Search\n\nThe native web_search tool is available in this session.\nUse web_search when the user asks for current or online information.\nPrefer web_search over guessing when freshness matters.\n`;\n\nexport default function anthropicWebSearchExtension(pi: ExtensionAPI): void {\n\tpi.on(\"before_provider_request\", (event, ctx) => {\n\t\treturn addAnthropicWebSearchToPayload(ctx.model, event.payload);\n\t});\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsyncUi(ctx);\n\t});\n\n\tpi.on(\"model_select\", async (_event, ctx) => {\n\t\tsyncUi(ctx);\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tclearUi(ctx);\n\t});\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\tif (!supportsNativeAnthropicWebSearch(ctx.model)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!isAnthropicWebSearchEnabled()) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tsystemPrompt: `${event.systemPrompt}\\n${ANTHROPIC_WEB_SEARCH_SECTION}`,\n\t\t};\n\t});\n}\n"]}
@@ -7,6 +7,7 @@ export type GoalCommandRegistrationDeps = {
7
7
  readonly stopAgentGoalAccounting: (goalId: string) => void;
8
8
  readonly clearAgentGoalAccounting: () => void;
9
9
  readonly queueGoalContinuation: (pi: ExtensionAPI, ctx: ExtensionContext, goal: Goal) => void;
10
+ readonly refreshGoalUi: (ctx: ExtensionContext, goal: Goal | null) => void;
10
11
  };
11
12
  export declare function registerGoalCommand(pi: ExtensionAPI, deps: GoalCommandRegistrationDeps): void;
12
13
  //# sourceMappingURL=command-registration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command-registration.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/command-registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIrE,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAS7F,MAAM,MAAM,2BAA2B,GAAG;IACzC,QAAQ,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,YAAY,CAAC;IAC/D,QAAQ,CAAC,uBAAuB,EAAE,CACjC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,kBAAkB,KACpB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1B,QAAQ,CAAC,wBAAwB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,QAAQ,CAAC,uBAAuB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IAC9C,QAAQ,CAAC,qBAAqB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;CAC9F,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,2BAA2B,GAAG,IAAI,CAoD7F"}
1
+ {"version":3,"file":"command-registration.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/command-registration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIrE,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAQ7F,MAAM,MAAM,2BAA2B,GAAG;IACzC,QAAQ,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,YAAY,CAAC;IAC/D,QAAQ,CAAC,uBAAuB,EAAE,CACjC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,kBAAkB,KACpB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1B,QAAQ,CAAC,wBAAwB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,QAAQ,CAAC,uBAAuB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IAC9C,QAAQ,CAAC,qBAAqB,EAAE,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC9F,QAAQ,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,2BAA2B,GAAG,IAAI,CAoD7F"}
@@ -1,7 +1,6 @@
1
1
  import { parseGoalCommand } from "./command.js";
2
2
  import { formatGoalForTool, goalStatusLabel } from "./format.js";
3
3
  import { clearGoal, createGoal, readGoal, updateGoal } from "./store.js";
4
- import { updateGoalUi } from "./ui.js";
5
4
  const GOAL_USAGE = "Usage: /goal <objective>";
6
5
  const GOAL_EMPTY_HINT = "No goal is currently set.";
7
6
  const REPLACE_GOAL_CHOICE = "Replace current goal";
@@ -16,7 +15,7 @@ export function registerGoalCommand(pi, deps) {
16
15
  switch (command.kind) {
17
16
  case "show": {
18
17
  const goal = await readGoal(deps.goalStoreRef(ctx));
19
- updateGoalUi(ctx, goal);
18
+ deps.refreshGoalUi(ctx, goal);
20
19
  ctx.ui.notify(goal === null ? `${GOAL_USAGE}\n${GOAL_EMPTY_HINT}` : formatGoalForTool(goal), goal ? "info" : "warning");
21
20
  return;
22
21
  }
@@ -35,7 +34,7 @@ export function registerGoalCommand(pi, deps) {
35
34
  else {
36
35
  deps.stopAgentGoalAccounting(goal.id);
37
36
  }
38
- updateGoalUi(ctx, goal);
37
+ deps.refreshGoalUi(ctx, goal);
39
38
  ctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\n${formatGoalForTool(goal)}`, "info");
40
39
  deps.queueGoalContinuation(pi, ctx, goal);
41
40
  return;
@@ -44,7 +43,7 @@ export function registerGoalCommand(pi, deps) {
44
43
  await deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, "active");
45
44
  const cleared = await clearGoal(deps.goalStoreRef(ctx));
46
45
  deps.clearAgentGoalAccounting();
47
- updateGoalUi(ctx, null);
46
+ deps.refreshGoalUi(ctx, null);
48
47
  ctx.ui.notify(cleared ? "Goal cleared" : "No goal to clear\nThis thread does not currently have a goal.", cleared ? "info" : "warning");
49
48
  return;
50
49
  }
@@ -70,7 +69,7 @@ async function setGoalObjective(pi, ctx, objective, deps) {
70
69
  const goal = current === null ? await createGoal(ref, objective) : await updateGoal(ref, { objective });
71
70
  if (goal.status === "active")
72
71
  deps.beginAgentGoalAccounting(goal);
73
- updateGoalUi(ctx, goal);
72
+ deps.refreshGoalUi(ctx, goal);
74
73
  ctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\n${formatGoalForTool(goal)}`, "info");
75
74
  deps.queueGoalContinuation(pi, ctx, goal);
76
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"command-registration.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/command-registration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { parseGoalCommand } from \"./command.ts\";\nimport { formatGoalForTool, goalStatusLabel } from \"./format.ts\";\nimport { clearGoal, createGoal, readGoal, updateGoal } from \"./store.ts\";\nimport type { Goal, GoalAccountingMode, GoalStoreRef, TokenUsageSnapshot } from \"./types.ts\";\nimport { updateGoalUi } from \"./ui.ts\";\n\nconst GOAL_USAGE = \"Usage: /goal <objective>\";\nconst GOAL_EMPTY_HINT = \"No goal is currently set.\";\nconst REPLACE_GOAL_CHOICE = \"Replace current goal\";\nconst CANCEL_REPLACE_GOAL_CHOICE = \"Cancel\";\nconst EMPTY_USAGE: TokenUsageSnapshot = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0 };\n\nexport type GoalCommandRegistrationDeps = {\n\treadonly goalStoreRef: (ctx: ExtensionContext) => GoalStoreRef;\n\treadonly accountCurrentAgentTurn: (\n\t\tctx: ExtensionContext,\n\t\tusage: TokenUsageSnapshot,\n\t\tmode: GoalAccountingMode,\n\t) => Promise<Goal | null>;\n\treadonly beginAgentGoalAccounting: (goal: Goal) => void;\n\treadonly stopAgentGoalAccounting: (goalId: string) => void;\n\treadonly clearAgentGoalAccounting: () => void;\n\treadonly queueGoalContinuation: (pi: ExtensionAPI, ctx: ExtensionContext, goal: Goal) => void;\n};\n\nexport function registerGoalCommand(pi: ExtensionAPI, deps: GoalCommandRegistrationDeps): void {\n\tpi.registerCommand(\"goal\", {\n\t\tdescription: \"Set, inspect, pause, resume, or clear the persistent goal\",\n\t\thandler: async (rawArgs, ctx) => {\n\t\t\tconst command = parseGoalCommand(rawArgs);\n\t\t\ttry {\n\t\t\t\tswitch (command.kind) {\n\t\t\t\t\tcase \"show\": {\n\t\t\t\t\t\tconst goal = await readGoal(deps.goalStoreRef(ctx));\n\t\t\t\t\t\tupdateGoalUi(ctx, goal);\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\tgoal === null ? `${GOAL_USAGE}\\n${GOAL_EMPTY_HINT}` : formatGoalForTool(goal),\n\t\t\t\t\t\t\tgoal ? \"info\" : \"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"setObjective\": {\n\t\t\t\t\t\tawait setGoalObjective(pi, ctx, command.objective, deps);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"setStatus\": {\n\t\t\t\t\t\tif (command.status === \"paused\") {\n\t\t\t\t\t\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst goal = await updateGoal(deps.goalStoreRef(ctx), { status: command.status });\n\t\t\t\t\t\tif (goal.status === \"active\") {\n\t\t\t\t\t\t\tdeps.beginAgentGoalAccounting(goal);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdeps.stopAgentGoalAccounting(goal.id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tupdateGoalUi(ctx, goal);\n\t\t\t\t\t\tctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\\n${formatGoalForTool(goal)}`, \"info\");\n\t\t\t\t\t\tdeps.queueGoalContinuation(pi, ctx, goal);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"clear\": {\n\t\t\t\t\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t\t\t\t\t\tconst cleared = await clearGoal(deps.goalStoreRef(ctx));\n\t\t\t\t\t\tdeps.clearAgentGoalAccounting();\n\t\t\t\t\t\tupdateGoalUi(ctx, null);\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\tcleared ? \"Goal cleared\" : \"No goal to clear\\nThis thread does not currently have a goal.\",\n\t\t\t\t\t\t\tcleared ? \"info\" : \"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n\nasync function setGoalObjective(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tobjective: string,\n\tdeps: GoalCommandRegistrationDeps,\n): Promise<void> {\n\tconst ref = deps.goalStoreRef(ctx);\n\tconst current = await readGoal(ref);\n\tif (current !== null) {\n\t\tconst shouldReplace = await confirmReplaceGoal(ctx, objective);\n\t\tif (!shouldReplace) return;\n\t}\n\n\tif (current?.status === \"active\") {\n\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t}\n\tconst goal = current === null ? await createGoal(ref, objective) : await updateGoal(ref, { objective });\n\tif (goal.status === \"active\") deps.beginAgentGoalAccounting(goal);\n\tupdateGoalUi(ctx, goal);\n\tctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\\n${formatGoalForTool(goal)}`, \"info\");\n\tdeps.queueGoalContinuation(pi, ctx, goal);\n}\n\nasync function confirmReplaceGoal(ctx: ExtensionContext, objective: string): Promise<boolean> {\n\tif (!ctx.hasUI) return true;\n\tconst choice = await ctx.ui.select(`Replace goal?\\nNew objective: ${objective}`, [\n\t\tREPLACE_GOAL_CHOICE,\n\t\tCANCEL_REPLACE_GOAL_CHOICE,\n\t]);\n\treturn choice === REPLACE_GOAL_CHOICE;\n}\n"]}
1
+ {"version":3,"file":"command-registration.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/command-registration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { parseGoalCommand } from \"./command.ts\";\nimport { formatGoalForTool, goalStatusLabel } from \"./format.ts\";\nimport { clearGoal, createGoal, readGoal, updateGoal } from \"./store.ts\";\nimport type { Goal, GoalAccountingMode, GoalStoreRef, TokenUsageSnapshot } from \"./types.ts\";\n\nconst GOAL_USAGE = \"Usage: /goal <objective>\";\nconst GOAL_EMPTY_HINT = \"No goal is currently set.\";\nconst REPLACE_GOAL_CHOICE = \"Replace current goal\";\nconst CANCEL_REPLACE_GOAL_CHOICE = \"Cancel\";\nconst EMPTY_USAGE: TokenUsageSnapshot = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0 };\n\nexport type GoalCommandRegistrationDeps = {\n\treadonly goalStoreRef: (ctx: ExtensionContext) => GoalStoreRef;\n\treadonly accountCurrentAgentTurn: (\n\t\tctx: ExtensionContext,\n\t\tusage: TokenUsageSnapshot,\n\t\tmode: GoalAccountingMode,\n\t) => Promise<Goal | null>;\n\treadonly beginAgentGoalAccounting: (goal: Goal) => void;\n\treadonly stopAgentGoalAccounting: (goalId: string) => void;\n\treadonly clearAgentGoalAccounting: () => void;\n\treadonly queueGoalContinuation: (pi: ExtensionAPI, ctx: ExtensionContext, goal: Goal) => void;\n\treadonly refreshGoalUi: (ctx: ExtensionContext, goal: Goal | null) => void;\n};\n\nexport function registerGoalCommand(pi: ExtensionAPI, deps: GoalCommandRegistrationDeps): void {\n\tpi.registerCommand(\"goal\", {\n\t\tdescription: \"Set, inspect, pause, resume, or clear the persistent goal\",\n\t\thandler: async (rawArgs, ctx) => {\n\t\t\tconst command = parseGoalCommand(rawArgs);\n\t\t\ttry {\n\t\t\t\tswitch (command.kind) {\n\t\t\t\t\tcase \"show\": {\n\t\t\t\t\t\tconst goal = await readGoal(deps.goalStoreRef(ctx));\n\t\t\t\t\t\tdeps.refreshGoalUi(ctx, goal);\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\tgoal === null ? `${GOAL_USAGE}\\n${GOAL_EMPTY_HINT}` : formatGoalForTool(goal),\n\t\t\t\t\t\t\tgoal ? \"info\" : \"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"setObjective\": {\n\t\t\t\t\t\tawait setGoalObjective(pi, ctx, command.objective, deps);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"setStatus\": {\n\t\t\t\t\t\tif (command.status === \"paused\") {\n\t\t\t\t\t\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst goal = await updateGoal(deps.goalStoreRef(ctx), { status: command.status });\n\t\t\t\t\t\tif (goal.status === \"active\") {\n\t\t\t\t\t\t\tdeps.beginAgentGoalAccounting(goal);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdeps.stopAgentGoalAccounting(goal.id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdeps.refreshGoalUi(ctx, goal);\n\t\t\t\t\t\tctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\\n${formatGoalForTool(goal)}`, \"info\");\n\t\t\t\t\t\tdeps.queueGoalContinuation(pi, ctx, goal);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"clear\": {\n\t\t\t\t\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t\t\t\t\t\tconst cleared = await clearGoal(deps.goalStoreRef(ctx));\n\t\t\t\t\t\tdeps.clearAgentGoalAccounting();\n\t\t\t\t\t\tdeps.refreshGoalUi(ctx, null);\n\t\t\t\t\t\tctx.ui.notify(\n\t\t\t\t\t\t\tcleared ? \"Goal cleared\" : \"No goal to clear\\nThis thread does not currently have a goal.\",\n\t\t\t\t\t\t\tcleared ? \"info\" : \"warning\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tctx.ui.notify(error instanceof Error ? error.message : String(error), \"error\");\n\t\t\t}\n\t\t},\n\t});\n}\n\nasync function setGoalObjective(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tobjective: string,\n\tdeps: GoalCommandRegistrationDeps,\n): Promise<void> {\n\tconst ref = deps.goalStoreRef(ctx);\n\tconst current = await readGoal(ref);\n\tif (current !== null) {\n\t\tconst shouldReplace = await confirmReplaceGoal(ctx, objective);\n\t\tif (!shouldReplace) return;\n\t}\n\n\tif (current?.status === \"active\") {\n\t\tawait deps.accountCurrentAgentTurn(ctx, EMPTY_USAGE, \"active\");\n\t}\n\tconst goal = current === null ? await createGoal(ref, objective) : await updateGoal(ref, { objective });\n\tif (goal.status === \"active\") deps.beginAgentGoalAccounting(goal);\n\tdeps.refreshGoalUi(ctx, goal);\n\tctx.ui.notify(`Goal ${goalStatusLabel(goal.status)}\\n${formatGoalForTool(goal)}`, \"info\");\n\tdeps.queueGoalContinuation(pi, ctx, goal);\n}\n\nasync function confirmReplaceGoal(ctx: ExtensionContext, objective: string): Promise<boolean> {\n\tif (!ctx.hasUI) return true;\n\tconst choice = await ctx.ui.select(`Replace goal?\\nNew objective: ${objective}`, [\n\t\tREPLACE_GOAL_CHOICE,\n\t\tCANCEL_REPLACE_GOAL_CHOICE,\n\t]);\n\treturn choice === REPLACE_GOAL_CHOICE;\n}\n"]}