@wrongstack/core 0.270.0 → 0.272.1

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 (76) hide show
  1. package/dist/{agent-bridge-PcHQl_UQ.d.ts → agent-bridge-DFQYEeXf.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-SHJW7t8q.d.ts → agent-subagent-runner-BZa_IEcd.d.ts} +7 -7
  3. package/dist/{brain-BYcK__Ym.d.ts → brain-etbcbRwV.d.ts} +95 -2
  4. package/dist/{compactor-C2RKEBtC.d.ts → compactor-72ug-ZRB.d.ts} +1 -1
  5. package/dist/{config-C_ae2k86.d.ts → config-rRS8yorV.d.ts} +71 -2
  6. package/dist/{context-Dp87Bcaq.d.ts → context-Dw55zZ_Q.d.ts} +110 -1
  7. package/dist/coordination/index.d.ts +181 -17
  8. package/dist/coordination/index.js +1018 -166
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +804 -222
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +23 -18
  14. package/dist/execution/index.js +136 -41
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +36 -6
  17. package/dist/execution/prompt-enhancer.js +35 -9
  18. package/dist/execution/prompt-enhancer.js.map +1 -1
  19. package/dist/extension/index.d.ts +6 -6
  20. package/dist/{global-mailbox-Bvrz1P3f.d.ts → global-mailbox-DJ4EoRr0.d.ts} +145 -5
  21. package/dist/{goal-preamble-CA_4yiGQ.d.ts → goal-preamble-hM8BH7TK.d.ts} +9 -9
  22. package/dist/{goal-store-DhuJoUNG.d.ts → goal-store-CWlbT0TO.d.ts} +1 -1
  23. package/dist/hq/index.d.ts +95 -6
  24. package/dist/hq/index.js +628 -50
  25. package/dist/hq/index.js.map +1 -1
  26. package/dist/{index-whDfTANu.d.ts → index-2Lhk5v0o.d.ts} +2 -2
  27. package/dist/{index-W4VJCzHa.d.ts → index-DWm_PE9L.d.ts} +5 -5
  28. package/dist/{index-CZQ6Pwbs.d.ts → index-DqW4o62H.d.ts} +8 -8
  29. package/dist/index.d.ts +96 -56
  30. package/dist/index.js +2464 -519
  31. package/dist/index.js.map +1 -1
  32. package/dist/infrastructure/index.d.ts +6 -6
  33. package/dist/infrastructure/index.js +5 -3
  34. package/dist/infrastructure/index.js.map +1 -1
  35. package/dist/kernel/index.d.ts +9 -9
  36. package/dist/kernel/index.js.map +1 -1
  37. package/dist/{mcp-servers-DJdZiRcv.d.ts → mcp-servers-BpWHTKlE.d.ts} +3 -3
  38. package/dist/models/index.d.ts +5 -5
  39. package/dist/models/index.js +28 -5
  40. package/dist/models/index.js.map +1 -1
  41. package/dist/{models-registry-C3a-2-Yd.d.ts → models-registry-CXQFUn5t.d.ts} +1 -1
  42. package/dist/{multi-agent-coordinator-CJSpTe5O.d.ts → multi-agent-coordinator-jyimfo7D.d.ts} +1 -1
  43. package/dist/{null-fleet-bus-QVshIsDx.d.ts → null-fleet-bus-DOGQcvrY.d.ts} +6 -6
  44. package/dist/observability/index.d.ts +2 -2
  45. package/dist/{parallel-eternal-engine-D9y5Pkcc.d.ts → parallel-eternal-engine-rItJBYp9.d.ts} +9 -9
  46. package/dist/{path-resolver-CnQ8SIfh.d.ts → path-resolver-DrpF5MGK.d.ts} +3 -3
  47. package/dist/{permission-CvYQNUqZ.d.ts → permission-CC7XFYWG.d.ts} +1 -1
  48. package/dist/{permission-policy-D5Ss8j4B.d.ts → permission-policy-cYR4RJmw.d.ts} +2 -2
  49. package/dist/{pipeline-l_zzFRh3.d.ts → pipeline-Ckkn3AOA.d.ts} +2 -2
  50. package/dist/{plan-templates-NtPgyeJA.d.ts → plan-templates-BvHw5Znw.d.ts} +33 -9
  51. package/dist/{provider-model-resolve-d5poT5y0.d.ts → provider-model-resolve-nZqnCeaR.d.ts} +3 -3
  52. package/dist/{provider-runner-gkctlQV_.d.ts → provider-runner-zVOn1p67.d.ts} +3 -3
  53. package/dist/{retry-policy-CtFhfwa8.d.ts → retry-policy-BV7nzeAd.d.ts} +1 -1
  54. package/dist/sdd/index.d.ts +8 -8
  55. package/dist/sdd/index.js +2 -0
  56. package/dist/sdd/index.js.map +1 -1
  57. package/dist/{secret-vault-BLsVmTIK.d.ts → secret-vault-eMBKfheR.d.ts} +9 -1
  58. package/dist/security/index.d.ts +5 -5
  59. package/dist/security/index.js +137 -10
  60. package/dist/security/index.js.map +1 -1
  61. package/dist/{selector-CXl2_y9W.d.ts → selector-C4ORTOid.d.ts} +1 -1
  62. package/dist/{session-event-bridge-Ccud20CC.d.ts → session-event-bridge-CeNpUL9w.d.ts} +1 -1
  63. package/dist/{session-reader-ZeXQmsmE.d.ts → session-reader-BepLSnGL.d.ts} +1 -1
  64. package/dist/storage/index.d.ts +50 -13
  65. package/dist/storage/index.js +620 -220
  66. package/dist/storage/index.js.map +1 -1
  67. package/dist/tools/index.d.ts +2 -2
  68. package/dist/tools/index.js +9 -2
  69. package/dist/tools/index.js.map +1 -1
  70. package/dist/types/index.d.ts +19 -19
  71. package/dist/types/index.js +202 -41
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/utils/index.d.ts +17 -4
  74. package/dist/utils/index.js +48 -9
  75. package/dist/utils/index.js.map +1 -1
  76. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { P as Provider, M as Message } from '../context-Dp87Bcaq.js';
1
+ import { P as Provider, R as ReasoningRequest, a as ReasoningConfig, M as Message } from '../context-Dw55zZ_Q.js';
2
2
 
3
3
  /**
4
4
  * Prompt refinement ("did you mean this?").
@@ -13,7 +13,7 @@ import { P as Provider, M as Message } from '../context-Dp87Bcaq.js';
13
13
  * `provider.complete()` with a dedicated system prompt — and is deliberately
14
14
  * free of React / TUI dependencies so it can be unit-tested in isolation.
15
15
  */
16
- declare const ENHANCER_SYSTEM_PROMPT = "You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.\n\nRules:\n- Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.\n- Do NOT answer, solve, or perform the request. Only restate it more clearly.\n- Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.\n- Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.\n- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.\n- If the message is already clear and complete, return it essentially unchanged.\n\nYou MUST output TWO versions of the refined request, separated by a line containing only \"---\".\n- First version: refined in the SAME LANGUAGE the user wrote in (if Turkish \u2192 Turkish, if Spanish \u2192 Spanish, etc.).\n- Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).\n\nOutput format:\n<refined in user's language>\n---\n<refined in English>\n\nWhen earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 \"it\", \"that\", \"the same\", \"the other one\", \"this file\", \"again\" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.\n\nOutput ONLY the two versions separated by \"---\" \u2014 nothing else.";
16
+ declare const ENHANCER_SYSTEM_PROMPT = "You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.\n\nRules:\n- Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.\n- Do NOT answer, solve, or perform the request. Only restate it more clearly.\n- Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.\n- Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.\n- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.\n- If the message is already clear and complete, return it essentially unchanged.\n\nDetect the language of the user's LATEST message and output accordingly:\n\n- If that message is ALREADY in English: output exactly ONE refined version, in English. Nothing else \u2014 no \"---\" line, no second copy.\n- If that message is in ANY OTHER language (Turkish, Spanish, \u2026): output TWO versions separated by a line containing only \"---\":\n - First version: refined in the SAME LANGUAGE the user wrote in.\n - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).\n\nOutput format for non-English input:\n<refined in user's language>\n---\n<refined in English>\n\nWhen earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 \"it\", \"that\", \"the same\", \"the other one\", \"this file\", \"again\" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation. The conversation language does NOT decide the output language \u2014 only the language of the latest message does.\n\nOutput ONLY the refined request(s) in the format above \u2014 nothing else.";
17
17
  /**
18
18
  * Heuristic gate: should this raw input be sent through the refiner at all?
19
19
  * Pure + exported for unit testing. Returns false for inputs where refinement
@@ -21,6 +21,24 @@ declare const ENHANCER_SYSTEM_PROMPT = "You are a request refiner embedded in a
21
21
  * short text, bare numbers).
22
22
  */
23
23
  declare function shouldEnhance(text: string): boolean;
24
+ /**
25
+ * Build a reasoning directive for the refiner that minimizes wasted thinking,
26
+ * gated to what the model actually accepts. Refinement is a shallow rewrite
27
+ * task — extended thinking adds latency and (hidden) token cost for little
28
+ * gain — so we ask the model to spend as little reasoning as it safely can.
29
+ *
30
+ * The gating mirrors `resolveReasoningForRequest` so we never send a field the
31
+ * model would reject:
32
+ * - effort-capable model → its lowest advertised effort level;
33
+ * - else disable-capable model → disable thinking (`enabled: false`);
34
+ * - else (always-on / unknown) → `undefined` (leave the provider default).
35
+ *
36
+ * Returns `undefined` whenever nothing can be safely reduced. Callers forward
37
+ * that verbatim to `enhanceUserPrompt`, which then sends no reasoning field —
38
+ * identical to the behavior before this hint existed. Pure + exported for unit
39
+ * testing.
40
+ */
41
+ declare function gatedEnhancerReasoning(rc: ReasoningConfig | undefined): ReasoningRequest | undefined;
24
42
  /**
25
43
  * Normalize for "did the refiner actually change anything?" comparison —
26
44
  * collapse whitespace and lowercase so trivial reformatting doesn't trigger
@@ -33,13 +51,15 @@ interface ConversationTurn {
33
51
  text: string;
34
52
  }
35
53
  /**
36
- * Result of a successful prompt refinement. Contains both the
37
- * original-language and English versions so the UI can offer both.
54
+ * Result of a successful prompt refinement. Carries the original-language and
55
+ * English versions so the UI can offer both. When the input was already in
56
+ * English the refiner emits a single version and both fields hold the same
57
+ * text (the UI then offers two identical choices, which is correct).
38
58
  */
39
59
  interface EnhanceResult {
40
60
  /** Refined in the user's original language. */
41
61
  refined: string;
42
- /** Refined in English. */
62
+ /** Refined in English. Equals `refined` when the input was already English. */
43
63
  english: string;
44
64
  }
45
65
  interface EnhanceUserPromptOptions {
@@ -60,6 +80,16 @@ interface EnhanceUserPromptOptions {
60
80
  timeoutMs?: number | undefined;
61
81
  /** Max tokens for the refined output. Default 2048. */
62
82
  maxTokens?: number | undefined;
83
+ /**
84
+ * Reasoning directive for the refiner call. Refinement is a shallow
85
+ * restate-this-more-clearly task that does not benefit from extended
86
+ * thinking, so callers pass a low-effort / thinking-disabled hint here to
87
+ * cut latency and (hidden) reasoning-token cost — most impactful on slow
88
+ * reasoning models. Build it with `gatedEnhancerReasoning(rc)` so the field
89
+ * is gated to what the model accepts. Omit (undefined) to send no reasoning
90
+ * directive at all (the provider's own default applies).
91
+ */
92
+ reasoning?: ReasoningRequest | undefined;
63
93
  /**
64
94
  * Called with a short reason when refinement fails (provider error, timeout,
65
95
  * empty response). NOT called when the caller cancels via `signal`. Lets the
@@ -83,4 +113,4 @@ declare function enhanceUserPrompt(opts: EnhanceUserPromptOptions): Promise<Enha
83
113
  */
84
114
  declare function recentTextTurns(messages: Message[], maxTurns?: number, maxChars?: number): ConversationTurn[];
85
115
 
86
- export { type ConversationTurn, ENHANCER_SYSTEM_PROMPT, type EnhanceResult, type EnhanceUserPromptOptions, enhanceUserPrompt, normalizedEqual, recentTextTurns, shouldEnhance };
116
+ export { type ConversationTurn, ENHANCER_SYSTEM_PROMPT, type EnhanceResult, type EnhanceUserPromptOptions, enhanceUserPrompt, gatedEnhancerReasoning, normalizedEqual, recentTextTurns, shouldEnhance };
@@ -19,18 +19,21 @@ Rules:
19
19
  - Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
20
20
  - If the message is already clear and complete, return it essentially unchanged.
21
21
 
22
- You MUST output TWO versions of the refined request, separated by a line containing only "---".
23
- - First version: refined in the SAME LANGUAGE the user wrote in (if Turkish \u2192 Turkish, if Spanish \u2192 Spanish, etc.).
24
- - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
22
+ Detect the language of the user's LATEST message and output accordingly:
25
23
 
26
- Output format:
24
+ - If that message is ALREADY in English: output exactly ONE refined version, in English. Nothing else \u2014 no "---" line, no second copy.
25
+ - If that message is in ANY OTHER language (Turkish, Spanish, \u2026): output TWO versions separated by a line containing only "---":
26
+ - First version: refined in the SAME LANGUAGE the user wrote in.
27
+ - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
28
+
29
+ Output format for non-English input:
27
30
  <refined in user's language>
28
31
  ---
29
32
  <refined in English>
30
33
 
31
- When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.
34
+ When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation. The conversation language does NOT decide the output language \u2014 only the language of the latest message does.
32
35
 
33
- Output ONLY the two versions separated by "---" \u2014 nothing else.`;
36
+ Output ONLY the refined request(s) in the format above \u2014 nothing else.`;
34
37
  var AFFIRMATION_RE = /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\b[.! ]*$/i;
35
38
  function shouldEnhance(text) {
36
39
  const t = text.trim();
@@ -43,6 +46,24 @@ function shouldEnhance(text) {
43
46
  if (words.length < 3) return false;
44
47
  return true;
45
48
  }
49
+ var EFFORT_PREFERENCE = [
50
+ "low",
51
+ "minimal",
52
+ "medium",
53
+ "high",
54
+ "xhigh",
55
+ "max",
56
+ "none"
57
+ ];
58
+ function gatedEnhancerReasoning(rc) {
59
+ if (!rc) return void 0;
60
+ if (rc.effortSupported && rc.effortLevels.length > 0) {
61
+ const lowest = EFFORT_PREFERENCE.find((e) => rc.effortLevels.includes(e)) ?? rc.effortLevels[0];
62
+ if (lowest) return { effort: lowest };
63
+ }
64
+ if (rc.disableSupported) return { enabled: false };
65
+ return void 0;
66
+ }
46
67
  function normalizedEqual(a, b) {
47
68
  const norm = (s) => s.trim().replace(/\s+/g, " ").toLowerCase();
48
69
  return norm(a) === norm(b);
@@ -66,11 +87,17 @@ async function enhanceUserPrompt(opts) {
66
87
  model,
67
88
  system: [{ type: "text", text: ENHANCER_SYSTEM_PROMPT }],
68
89
  messages: [{ role: "user", content: buildRefinerInput(text, opts.history) }],
69
- maxTokens
90
+ maxTokens,
70
91
  // NOTE: deliberately NO `temperature`. The main agent loop never sets it,
71
92
  // and reasoning models (DeepSeek reasoner, o1/o3, …) return HTTP 400 when
72
93
  // `temperature` is present — which would make every refine call fail and
73
94
  // silently fall back to the original (no panel shown).
95
+ //
96
+ // A reasoning hint is forwarded ONLY when the caller supplies one (it must
97
+ // already be gated to the model's advertised support — see
98
+ // `gatedEnhancerReasoning`). Absent it, no reasoning field is sent, which
99
+ // is identical to the original behavior.
100
+ ...opts.reasoning ? { reasoning: opts.reasoning } : {}
74
101
  };
75
102
  const timer = new AbortController();
76
103
  const to = setTimeout(() => timer.abort(new Error("enhancer timeout")), timeoutMs);
@@ -84,7 +111,6 @@ async function enhanceUserPrompt(opts) {
84
111
  }
85
112
  const sepIdx = raw.indexOf("\n---\n");
86
113
  if (sepIdx === -1) {
87
- opts.onError?.("model did not produce two versions");
88
114
  return { refined: raw, english: raw };
89
115
  }
90
116
  const refined = raw.slice(0, sepIdx).trim();
@@ -126,6 +152,6 @@ function recentTextTurns(messages, maxTurns = 6, maxChars = 1500) {
126
152
  return turns;
127
153
  }
128
154
 
129
- export { ENHANCER_SYSTEM_PROMPT, enhanceUserPrompt, normalizedEqual, recentTextTurns, shouldEnhance };
155
+ export { ENHANCER_SYSTEM_PROMPT, enhanceUserPrompt, gatedEnhancerReasoning, normalizedEqual, recentTextTurns, shouldEnhance };
130
156
  //# sourceMappingURL=prompt-enhancer.js.map
131
157
  //# sourceMappingURL=prompt-enhancer.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/blocks.ts","../../src/utils/error.ts","../../src/execution/prompt-enhancer.ts"],"names":[],"mappings":"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","file":"prompt-enhancer.js","sourcesContent":["export interface TextBlock {\n type: 'text';\n text: string;\n cache_control?: { type: 'ephemeral' | undefined };\n}\n\nexport interface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n /**\n * Provider-specific opaque metadata captured from the wire response.\n * Echoed back verbatim in the next request so providers that bind\n * extra state to function calls keep working. Example: Gemini's\n * `thoughtSignature` — required for tool-use turns with thinking\n * models, otherwise the next request fails with 400 \"Function call\n * is missing a thought_signature in functionCall parts\".\n *\n * Keys are namespaced by intent so multiple wires can coexist:\n * - `google.thoughtSignature` — Gemini signed-thought blob\n * Other providers can add their own keys without colliding.\n */\n providerMeta?: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n /**\n * The original tool name. Useful for providers like Google Gemini that\n * need the tool name in `functionResponse.name` — the tool_use_id is\n * only a session-local identifier and is not stable across replays.\n * Always set by ToolExecutor; may be absent on manually-constructed blocks.\n */\n name?: string | undefined;\n content: string;\n is_error?: boolean | undefined;\n}\n\nexport interface ImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string | undefined;\n data?: string | undefined;\n url?: string | undefined;\n };\n}\n\n/**\n * Chain-of-thought / extended-thinking content emitted by the model.\n *\n * Both Anthropic extended thinking (`{type:'thinking', thinking, signature}`)\n * and DeepSeek reasoning mode (top-level `reasoning_content` on the assistant\n * message) require this content to be echoed back verbatim on the next\n * request, otherwise the provider returns 400:\n * - Anthropic: \"The `content[].thinking` in the thinking mode must be passed back\"\n * - DeepSeek: \"The `reasoning_content` in the thinking mode must be passed back\"\n *\n * `signature` is Anthropic-specific (an opaque integrity blob). DeepSeek\n * doesn't issue a signature — the field is absent for that provider.\n *\n * Per Anthropic, thinking blocks MUST appear before any text/tool_use blocks\n * in an assistant message. Stream builders preserve that order.\n */\nexport interface ThinkingBlock {\n type: 'thinking';\n thinking: string;\n signature?: string | undefined;\n providerMeta?: Record<string, unknown>;\n}\n\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock | ThinkingBlock;\n\nexport function isTextBlock(b: ContentBlock): b is TextBlock {\n return b.type === 'text';\n}\nexport function isToolUseBlock(b: ContentBlock): b is ToolUseBlock {\n return b.type === 'tool_use';\n}\nexport function isToolResultBlock(b: ContentBlock): b is ToolResultBlock {\n return b.type === 'tool_result';\n}\nexport function isImageBlock(b: ContentBlock): b is ImageBlock {\n return b.type === 'image';\n}\nexport function isThinkingBlock(b: ContentBlock): b is ThinkingBlock {\n return b.type === 'thinking';\n}\n","/**\n * Converts an unknown error value to a human-readable string.\n * Used in 40+ files across the codebase to normalize error messaging.\n */\nexport function toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import { isTextBlock } from '../types/blocks.js';\nimport type { ContentBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport type { Provider, Request } from '../types/provider.js';\nimport { toErrorMessage } from '../utils/error.js';\n\n/**\n * Prompt refinement (\"did you mean this?\").\n *\n * Runs a one-shot LLM call in a SEPARATE context (its own system prompt, no\n * conversation history, no tools) that rewrites a raw user message into a\n * clearer, more complete instruction BEFORE the main agent sees it. The goal\n * is to make the main context start from a well-understood request rather than\n * guessing intent from terse input like \"fix the bug\".\n *\n * This mirrors `IntelligentCompactor.callSummarizer` — a plain\n * `provider.complete()` with a dedicated system prompt — and is deliberately\n * free of React / TUI dependencies so it can be unit-tested in isolation.\n */\n\nexport const ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.\n\nRules:\n- Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.\n- Do NOT answer, solve, or perform the request. Only restate it more clearly.\n- Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.\n- Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.\n- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.\n- If the message is already clear and complete, return it essentially unchanged.\n\nYou MUST output TWO versions of the refined request, separated by a line containing only \"---\".\n- First version: refined in the SAME LANGUAGE the user wrote in (if Turkish → Turkish, if Spanish → Spanish, etc.).\n- Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).\n\nOutput format:\n<refined in user's language>\n---\n<refined in English>\n\nWhen earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message — \"it\", \"that\", \"the same\", \"the other one\", \"this file\", \"again\" — so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.\n\nOutput ONLY the two versions separated by \"---\" — nothing else.`;\n\n/** Words/phrases that are control answers, not refinable requests. */\nconst AFFIRMATION_RE =\n /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\\b[.! ]*$/i;\n\n/**\n * Heuristic gate: should this raw input be sent through the refiner at all?\n * Pure + exported for unit testing. Returns false for inputs where refinement\n * is pointless or unwanted (slash commands, one-word affirmations, trivially\n * short text, bare numbers).\n */\nexport function shouldEnhance(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (t.startsWith('/')) return false; // slash command\n if (t.length < 12) return false; // too short to be worth refining\n if (AFFIRMATION_RE.test(t)) return false; // \"yes\" / \"continue\" / ...\n if (/^[\\d\\s.,]+$/.test(t)) return false; // bare numbers (menu picks, etc.)\n const words = t.split(/\\s+/).filter(Boolean);\n if (words.length < 3) return false; // 1–2 words rarely benefit\n return true;\n}\n\n/**\n * Normalize for \"did the refiner actually change anything?\" comparison —\n * collapse whitespace and lowercase so trivial reformatting doesn't trigger\n * the confirmation panel.\n */\nexport function normalizedEqual(a: string, b: string): boolean {\n const norm = (s: string) => s.trim().replace(/\\s+/g, ' ').toLowerCase();\n return norm(a) === norm(b);\n}\n\n/** A single text-only conversation turn used as refiner context. */\nexport interface ConversationTurn {\n role: 'user' | 'assistant';\n text: string;\n}\n\n/**\n * Result of a successful prompt refinement. Contains both the\n * original-language and English versions so the UI can offer both.\n */\nexport interface EnhanceResult {\n /** Refined in the user's original language. */\n refined: string;\n /** Refined in English. */\n english: string;\n}\n\nexport interface EnhanceUserPromptOptions {\n provider: Provider;\n model: string;\n text: string;\n /**\n * Recent conversation turns (oldest→newest), text only, used purely as\n * CONTEXT so the refiner can resolve references in a follow-up message\n * (\"it\", \"the same\", \"that file\"). Without this, the refiner is blind to\n * the conversation and can only refine self-contained prompts. Build with\n * `recentTextTurns(ctx.messages)`.\n */\n history?: ConversationTurn[] | undefined;\n /** Parent abort signal (e.g. the run controller / Esc). */\n signal?: AbortSignal | undefined;\n /** Hard cap on how long to wait for the refiner before giving up. Default 90s. */\n timeoutMs?: number | undefined;\n /** Max tokens for the refined output. Default 2048. */\n maxTokens?: number | undefined;\n /**\n * Called with a short reason when refinement fails (provider error, timeout,\n * empty response). NOT called when the caller cancels via `signal`. Lets the\n * UI surface *why* a refine fell through instead of a generic message.\n */\n onError?: ((reason: string) => void) | undefined;\n}\n\n/**\n * Compose the single user message sent to the refiner: the recent\n * conversation embedded as plain text (so we never trip provider\n * role-alternation rules) followed by the latest message to refine.\n */\nfunction buildRefinerInput(text: string, history?: ConversationTurn[]): string {\n if (!history || history.length === 0) return text;\n const lines = history.map((t) => `${t.role === 'user' ? 'User' : 'Assistant'}: ${t.text}`);\n return [\n 'Recent conversation (context only — do not act on it):',\n ...lines,\n '',\n 'Latest message to refine:',\n text,\n ].join('\\n');\n}\n\n/**\n * Refine a raw user prompt. Returns the refined text, or `null` when the\n * caller should fall back to the original (refiner errored, timed out, was\n * aborted, or returned nothing useful). NEVER throws — refinement is a\n * best-effort convenience and must never block the user from sending.\n */\nexport async function enhanceUserPrompt(\n opts: EnhanceUserPromptOptions,\n): Promise<EnhanceResult | null> {\n const { provider, model, text } = opts;\n // Reasoning models (\"thinking\" models like DeepSeek reasoner / o1) take\n // longer to first token, so give a generous default window.\n const timeoutMs = opts.timeoutMs ?? 90000;\n // Generous default: on some endpoints the model's hidden \"thinking\" tokens\n // count against this budget, so a small cap can leave NO room for the actual\n // refined text (→ empty completion → null). 2048 keeps the output room ample.\n const maxTokens = opts.maxTokens ?? 2048;\n\n const req: Request = {\n model,\n system: [{ type: 'text', text: ENHANCER_SYSTEM_PROMPT }],\n messages: [{ role: 'user', content: buildRefinerInput(text, opts.history) }],\n maxTokens,\n // NOTE: deliberately NO `temperature`. The main agent loop never sets it,\n // and reasoning models (DeepSeek reasoner, o1/o3, …) return HTTP 400 when\n // `temperature` is present — which would make every refine call fail and\n // silently fall back to the original (no panel shown).\n };\n\n // Link a local timeout to the parent signal so a stuck provider call can't\n // hang the submit path. AbortSignal.any keeps both cancellation sources.\n const timer = new AbortController();\n const to = setTimeout(() => timer.abort(new Error('enhancer timeout')), timeoutMs);\n const signal = opts.signal\n ? AbortSignal.any([opts.signal, timer.signal])\n : timer.signal;\n\n try {\n const res = await provider.complete(req, { signal });\n const raw = res.content\n .filter(isTextBlock)\n .map((b) => b.text)\n .join('\\n')\n .trim();\n if (!raw) {\n opts.onError?.('model returned no text');\n return null;\n }\n\n // The model outputs two versions separated by a line with only \"---\".\n // Split on the first occurrence so the delimiter can appear in the text.\n const sepIdx = raw.indexOf('\\n---\\n');\n if (sepIdx === -1) {\n // Model didn't follow the format — treat the whole response as a\n // single refined version (best-effort fallback).\n opts.onError?.('model did not produce two versions');\n return { refined: raw, english: raw };\n }\n const refined = raw.slice(0, sepIdx).trim();\n const english = raw.slice(sepIdx + 5).trim(); // skip \"\\n---\\n\"\n if (!refined || !english) {\n opts.onError?.('one or both versions empty');\n return null;\n }\n return { refined, english };\n } catch (err) {\n // User-initiated cancel → stay silent (they chose to send the original).\n if (opts.signal?.aborted) return null;\n if (timer.signal.aborted) {\n opts.onError?.(`timed out after ${Math.round(timeoutMs / 1000)}s`);\n return null;\n }\n opts.onError?.(toErrorMessage(err));\n return null;\n } finally {\n // Idempotent — abort() after signal already fired is a no-op, so this is\n // always safe regardless of whether the timeout fired first.\n timer.abort();\n clearTimeout(to);\n }\n}\n\n/** Pull the visible text out of a message's content (ignores tool blocks). */\nfunction messageText(content: string | ContentBlock[]): string {\n if (typeof content === 'string') return content;\n return content\n .filter(isTextBlock)\n .map((b) => b.text)\n .join('\\n')\n .trim();\n}\n\n/**\n * Extract the last few user/assistant TEXT turns from a conversation, newest\n * last, for use as refiner context. Skips system messages and tool-only turns\n * (tool_use / tool_result carry no useful natural-language context and bloat\n * the call). Each turn is truncated to `maxChars`; at most `maxTurns` are\n * returned. Pure + exported for unit testing.\n */\nexport function recentTextTurns(\n messages: Message[],\n maxTurns = 6,\n maxChars = 1500,\n): ConversationTurn[] {\n const turns: ConversationTurn[] = [];\n for (let i = messages.length - 1; i >= 0 && turns.length < maxTurns; i--) {\n const m = messages[i];\n if (!m || (m.role !== 'user' && m.role !== 'assistant')) continue;\n const text = messageText(m.content);\n if (!text) continue;\n turns.unshift({\n role: m.role,\n text: text.length > maxChars ? `${text.slice(0, maxChars - 1)}…` : text,\n });\n }\n return turns;\n}\n"]}
1
+ {"version":3,"sources":["../../src/types/blocks.ts","../../src/utils/error.ts","../../src/execution/prompt-enhancer.ts"],"names":[],"mappings":"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","file":"prompt-enhancer.js","sourcesContent":["export interface TextBlock {\n type: 'text';\n text: string;\n cache_control?: { type: 'ephemeral' | undefined };\n}\n\nexport interface ToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n /**\n * Provider-specific opaque metadata captured from the wire response.\n * Echoed back verbatim in the next request so providers that bind\n * extra state to function calls keep working. Example: Gemini's\n * `thoughtSignature` — required for tool-use turns with thinking\n * models, otherwise the next request fails with 400 \"Function call\n * is missing a thought_signature in functionCall parts\".\n *\n * Keys are namespaced by intent so multiple wires can coexist:\n * - `google.thoughtSignature` — Gemini signed-thought blob\n * Other providers can add their own keys without colliding.\n */\n providerMeta?: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n /**\n * The original tool name. Useful for providers like Google Gemini that\n * need the tool name in `functionResponse.name` — the tool_use_id is\n * only a session-local identifier and is not stable across replays.\n * Always set by ToolExecutor; may be absent on manually-constructed blocks.\n */\n name?: string | undefined;\n content: string;\n is_error?: boolean | undefined;\n}\n\nexport interface ImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string | undefined;\n data?: string | undefined;\n url?: string | undefined;\n };\n}\n\n/**\n * Chain-of-thought / extended-thinking content emitted by the model.\n *\n * Both Anthropic extended thinking (`{type:'thinking', thinking, signature}`)\n * and DeepSeek reasoning mode (top-level `reasoning_content` on the assistant\n * message) require this content to be echoed back verbatim on the next\n * request, otherwise the provider returns 400:\n * - Anthropic: \"The `content[].thinking` in the thinking mode must be passed back\"\n * - DeepSeek: \"The `reasoning_content` in the thinking mode must be passed back\"\n *\n * `signature` is Anthropic-specific (an opaque integrity blob). DeepSeek\n * doesn't issue a signature — the field is absent for that provider.\n *\n * Per Anthropic, thinking blocks MUST appear before any text/tool_use blocks\n * in an assistant message. Stream builders preserve that order.\n */\nexport interface ThinkingBlock {\n type: 'thinking';\n thinking: string;\n signature?: string | undefined;\n providerMeta?: Record<string, unknown>;\n}\n\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock | ThinkingBlock;\n\nexport function isTextBlock(b: ContentBlock): b is TextBlock {\n return b.type === 'text';\n}\nexport function isToolUseBlock(b: ContentBlock): b is ToolUseBlock {\n return b.type === 'tool_use';\n}\nexport function isToolResultBlock(b: ContentBlock): b is ToolResultBlock {\n return b.type === 'tool_result';\n}\nexport function isImageBlock(b: ContentBlock): b is ImageBlock {\n return b.type === 'image';\n}\nexport function isThinkingBlock(b: ContentBlock): b is ThinkingBlock {\n return b.type === 'thinking';\n}\n","/**\n * Converts an unknown error value to a human-readable string.\n * Used in 40+ files across the codebase to normalize error messaging.\n */\nexport function toErrorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","import type { ContentBlock } from '../types/blocks.js';\nimport { isTextBlock } from '../types/blocks.js';\nimport type { Message } from '../types/messages.js';\nimport type {\n Provider,\n ReasoningConfig,\n ReasoningEffort,\n ReasoningRequest,\n Request,\n} from '../types/provider.js';\nimport { toErrorMessage } from '../utils/error.js';\n\n/**\n * Prompt refinement (\"did you mean this?\").\n *\n * Runs a one-shot LLM call in a SEPARATE context (its own system prompt, no\n * conversation history, no tools) that rewrites a raw user message into a\n * clearer, more complete instruction BEFORE the main agent sees it. The goal\n * is to make the main context start from a well-understood request rather than\n * guessing intent from terse input like \"fix the bug\".\n *\n * This mirrors `IntelligentCompactor.callSummarizer` — a plain\n * `provider.complete()` with a dedicated system prompt — and is deliberately\n * free of React / TUI dependencies so it can be unit-tested in isolation.\n */\n\nexport const ENHANCER_SYSTEM_PROMPT = `You are a request refiner embedded in a coding agent. Your ONLY job is to rewrite the user's message into clearer, unambiguous instructions that the coding agent can act on confidently.\n\nRules:\n- Preserve the user's intent and scope EXACTLY. Do not add new requirements, features, constraints, or steps the user did not ask for. Do not remove anything they did ask for.\n- Do NOT answer, solve, or perform the request. Only restate it more clearly.\n- Keep all concrete details verbatim: file paths, identifiers, code, error text, numbers, names, URLs.\n- Resolve obvious ambiguity by making the implied subject explicit, not by inventing specifics. If something is genuinely unspecified, leave it general rather than guessing.\n- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.\n- If the message is already clear and complete, return it essentially unchanged.\n\nDetect the language of the user's LATEST message and output accordingly:\n\n- If that message is ALREADY in English: output exactly ONE refined version, in English. Nothing else — no \"---\" line, no second copy.\n- If that message is in ANY OTHER language (Turkish, Spanish, …): output TWO versions separated by a line containing only \"---\":\n - First version: refined in the SAME LANGUAGE the user wrote in.\n - Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).\n\nOutput format for non-English input:\n<refined in user's language>\n---\n<refined in English>\n\nWhen earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message — \"it\", \"that\", \"the same\", \"the other one\", \"this file\", \"again\" — so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation. The conversation language does NOT decide the output language — only the language of the latest message does.\n\nOutput ONLY the refined request(s) in the format above — nothing else.`;\n\n/** Words/phrases that are control answers, not refinable requests. */\nconst AFFIRMATION_RE =\n /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\\b[.! ]*$/i;\n\n/**\n * Heuristic gate: should this raw input be sent through the refiner at all?\n * Pure + exported for unit testing. Returns false for inputs where refinement\n * is pointless or unwanted (slash commands, one-word affirmations, trivially\n * short text, bare numbers).\n */\nexport function shouldEnhance(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n if (t.startsWith('/')) return false; // slash command\n if (t.length < 12) return false; // too short to be worth refining\n if (AFFIRMATION_RE.test(t)) return false; // \"yes\" / \"continue\" / ...\n if (/^[\\d\\s.,]+$/.test(t)) return false; // bare numbers (menu picks, etc.)\n const words = t.split(/\\s+/).filter(Boolean);\n if (words.length < 3) return false; // 1–2 words rarely benefit\n return true;\n}\n\n/**\n * Preference order when picking an effort level: the cheapest level that still\n * does SOME reasoning first ('low', then 'minimal'), then up the ladder, with\n * fully-off ('none') last so it's only chosen when it's the sole advertised\n * option. This keeps the refiner cheap without dropping reasoning entirely when\n * a little still helps. 'low' leads because it's the most widely accepted low\n * level across adapters (e.g. OpenAI's reasoning_effort set).\n */\nconst EFFORT_PREFERENCE: ReasoningEffort[] = [\n 'low',\n 'minimal',\n 'medium',\n 'high',\n 'xhigh',\n 'max',\n 'none',\n];\n\n/**\n * Build a reasoning directive for the refiner that minimizes wasted thinking,\n * gated to what the model actually accepts. Refinement is a shallow rewrite\n * task — extended thinking adds latency and (hidden) token cost for little\n * gain — so we ask the model to spend as little reasoning as it safely can.\n *\n * The gating mirrors `resolveReasoningForRequest` so we never send a field the\n * model would reject:\n * - effort-capable model → its lowest advertised effort level;\n * - else disable-capable model → disable thinking (`enabled: false`);\n * - else (always-on / unknown) → `undefined` (leave the provider default).\n *\n * Returns `undefined` whenever nothing can be safely reduced. Callers forward\n * that verbatim to `enhanceUserPrompt`, which then sends no reasoning field —\n * identical to the behavior before this hint existed. Pure + exported for unit\n * testing.\n */\nexport function gatedEnhancerReasoning(\n rc: ReasoningConfig | undefined,\n): ReasoningRequest | undefined {\n // Capabilities unknown → don't risk an unsupported field (matches the\n // conservative \"capabilities unknown\" branch in resolveReasoningForRequest).\n if (!rc) return undefined;\n if (rc.effortSupported && rc.effortLevels.length > 0) {\n const lowest = EFFORT_PREFERENCE.find((e) => rc.effortLevels.includes(e)) ?? rc.effortLevels[0];\n if (lowest) return { effort: lowest };\n }\n if (rc.disableSupported) return { enabled: false };\n return undefined;\n}\n\n/**\n * Normalize for \"did the refiner actually change anything?\" comparison —\n * collapse whitespace and lowercase so trivial reformatting doesn't trigger\n * the confirmation panel.\n */\nexport function normalizedEqual(a: string, b: string): boolean {\n const norm = (s: string) => s.trim().replace(/\\s+/g, ' ').toLowerCase();\n return norm(a) === norm(b);\n}\n\n/** A single text-only conversation turn used as refiner context. */\nexport interface ConversationTurn {\n role: 'user' | 'assistant';\n text: string;\n}\n\n/**\n * Result of a successful prompt refinement. Carries the original-language and\n * English versions so the UI can offer both. When the input was already in\n * English the refiner emits a single version and both fields hold the same\n * text (the UI then offers two identical choices, which is correct).\n */\nexport interface EnhanceResult {\n /** Refined in the user's original language. */\n refined: string;\n /** Refined in English. Equals `refined` when the input was already English. */\n english: string;\n}\n\nexport interface EnhanceUserPromptOptions {\n provider: Provider;\n model: string;\n text: string;\n /**\n * Recent conversation turns (oldest→newest), text only, used purely as\n * CONTEXT so the refiner can resolve references in a follow-up message\n * (\"it\", \"the same\", \"that file\"). Without this, the refiner is blind to\n * the conversation and can only refine self-contained prompts. Build with\n * `recentTextTurns(ctx.messages)`.\n */\n history?: ConversationTurn[] | undefined;\n /** Parent abort signal (e.g. the run controller / Esc). */\n signal?: AbortSignal | undefined;\n /** Hard cap on how long to wait for the refiner before giving up. Default 90s. */\n timeoutMs?: number | undefined;\n /** Max tokens for the refined output. Default 2048. */\n maxTokens?: number | undefined;\n /**\n * Reasoning directive for the refiner call. Refinement is a shallow\n * restate-this-more-clearly task that does not benefit from extended\n * thinking, so callers pass a low-effort / thinking-disabled hint here to\n * cut latency and (hidden) reasoning-token cost — most impactful on slow\n * reasoning models. Build it with `gatedEnhancerReasoning(rc)` so the field\n * is gated to what the model accepts. Omit (undefined) to send no reasoning\n * directive at all (the provider's own default applies).\n */\n reasoning?: ReasoningRequest | undefined;\n /**\n * Called with a short reason when refinement fails (provider error, timeout,\n * empty response). NOT called when the caller cancels via `signal`. Lets the\n * UI surface *why* a refine fell through instead of a generic message.\n */\n onError?: ((reason: string) => void) | undefined;\n}\n\n/**\n * Compose the single user message sent to the refiner: the recent\n * conversation embedded as plain text (so we never trip provider\n * role-alternation rules) followed by the latest message to refine.\n */\nfunction buildRefinerInput(text: string, history?: ConversationTurn[]): string {\n if (!history || history.length === 0) return text;\n const lines = history.map((t) => `${t.role === 'user' ? 'User' : 'Assistant'}: ${t.text}`);\n return [\n 'Recent conversation (context only — do not act on it):',\n ...lines,\n '',\n 'Latest message to refine:',\n text,\n ].join('\\n');\n}\n\n/**\n * Refine a raw user prompt. Returns the refined text, or `null` when the\n * caller should fall back to the original (refiner errored, timed out, was\n * aborted, or returned nothing useful). NEVER throws — refinement is a\n * best-effort convenience and must never block the user from sending.\n */\nexport async function enhanceUserPrompt(\n opts: EnhanceUserPromptOptions,\n): Promise<EnhanceResult | null> {\n const { provider, model, text } = opts;\n // Reasoning models (\"thinking\" models like DeepSeek reasoner / o1) take\n // longer to first token, so give a generous default window.\n const timeoutMs = opts.timeoutMs ?? 90000;\n // Generous default: on some endpoints the model's hidden \"thinking\" tokens\n // count against this budget, so a small cap can leave NO room for the actual\n // refined text (→ empty completion → null). 2048 keeps the output room ample.\n const maxTokens = opts.maxTokens ?? 2048;\n\n const req: Request = {\n model,\n system: [{ type: 'text', text: ENHANCER_SYSTEM_PROMPT }],\n messages: [{ role: 'user', content: buildRefinerInput(text, opts.history) }],\n maxTokens,\n // NOTE: deliberately NO `temperature`. The main agent loop never sets it,\n // and reasoning models (DeepSeek reasoner, o1/o3, …) return HTTP 400 when\n // `temperature` is present — which would make every refine call fail and\n // silently fall back to the original (no panel shown).\n //\n // A reasoning hint is forwarded ONLY when the caller supplies one (it must\n // already be gated to the model's advertised support — see\n // `gatedEnhancerReasoning`). Absent it, no reasoning field is sent, which\n // is identical to the original behavior.\n ...(opts.reasoning ? { reasoning: opts.reasoning } : {}),\n };\n\n // Link a local timeout to the parent signal so a stuck provider call can't\n // hang the submit path. AbortSignal.any keeps both cancellation sources.\n const timer = new AbortController();\n const to = setTimeout(() => timer.abort(new Error('enhancer timeout')), timeoutMs);\n const signal = opts.signal ? AbortSignal.any([opts.signal, timer.signal]) : timer.signal;\n\n try {\n const res = await provider.complete(req, { signal });\n const raw = res.content\n .filter(isTextBlock)\n .map((b) => b.text)\n .join('\\n')\n .trim();\n if (!raw) {\n opts.onError?.('model returned no text');\n return null;\n }\n\n // English input → ONE version (no \"---\"); other languages → two versions\n // separated by a line with only \"---\". Split on the first occurrence so the\n // delimiter can still appear inside the second version's text.\n const sepIdx = raw.indexOf('\\n---\\n');\n if (sepIdx === -1) {\n // Single version: the input was already English (or the model chose not\n // to translate). Use it for both fields — the UI offers identical\n // \"refined\" / \"english\" options, which is correct and saves the model\n // from generating a redundant second copy. NOT an error.\n return { refined: raw, english: raw };\n }\n const refined = raw.slice(0, sepIdx).trim();\n const english = raw.slice(sepIdx + 5).trim(); // skip \"\\n---\\n\"\n if (!refined || !english) {\n opts.onError?.('one or both versions empty');\n return null;\n }\n return { refined, english };\n } catch (err) {\n // User-initiated cancel → stay silent (they chose to send the original).\n if (opts.signal?.aborted) return null;\n if (timer.signal.aborted) {\n opts.onError?.(`timed out after ${Math.round(timeoutMs / 1000)}s`);\n return null;\n }\n opts.onError?.(toErrorMessage(err));\n return null;\n } finally {\n // Idempotent — abort() after signal already fired is a no-op, so this is\n // always safe regardless of whether the timeout fired first.\n timer.abort();\n clearTimeout(to);\n }\n}\n\n/** Pull the visible text out of a message's content (ignores tool blocks). */\nfunction messageText(content: string | ContentBlock[]): string {\n if (typeof content === 'string') return content;\n return content\n .filter(isTextBlock)\n .map((b) => b.text)\n .join('\\n')\n .trim();\n}\n\n/**\n * Extract the last few user/assistant TEXT turns from a conversation, newest\n * last, for use as refiner context. Skips system messages and tool-only turns\n * (tool_use / tool_result carry no useful natural-language context and bloat\n * the call). Each turn is truncated to `maxChars`; at most `maxTurns` are\n * returned. Pure + exported for unit testing.\n */\nexport function recentTextTurns(\n messages: Message[],\n maxTurns = 6,\n maxChars = 1500,\n): ConversationTurn[] {\n const turns: ConversationTurn[] = [];\n for (let i = messages.length - 1; i >= 0 && turns.length < maxTurns; i--) {\n const m = messages[i];\n if (!m || (m.role !== 'user' && m.role !== 'assistant')) continue;\n const text = messageText(m.content);\n if (!text) continue;\n turns.unshift({\n role: m.role,\n text: text.length > maxChars ? `${text.slice(0, maxChars - 1)}…` : text,\n });\n }\n return turns;\n}\n"]}
@@ -1,9 +1,9 @@
1
- export { i as AfterIterationHook, j as AfterRunHook, k as AfterToolExecutionHook, l as AgentExtension, B as BeforeIterationHook, m as BeforeRunHook, n as BeforeToolExecutionHook, E as ExtensionRegistry, O as OnErrorHook, v as ProviderRunnerFn, t as ProviderRunnerWrapper } from '../index-W4VJCzHa.js';
2
- import '../context-Dp87Bcaq.js';
1
+ export { i as AfterIterationHook, j as AfterRunHook, k as AfterToolExecutionHook, l as AgentExtension, B as BeforeIterationHook, m as BeforeRunHook, n as BeforeToolExecutionHook, E as ExtensionRegistry, O as OnErrorHook, v as ProviderRunnerFn, t as ProviderRunnerWrapper } from '../index-DWm_PE9L.js';
2
+ import '../context-Dw55zZ_Q.js';
3
3
  import '../logger-B63L5bTg.js';
4
- import '../pipeline-l_zzFRh3.js';
4
+ import '../pipeline-Ckkn3AOA.js';
5
5
  import '../mailbox-types-Ct2hJq0P.js';
6
- import '../config-C_ae2k86.js';
6
+ import '../config-rRS8yorV.js';
7
7
  import '../observability-D-HZN_mF.js';
8
- import '../brain-BYcK__Ym.js';
9
- import '../permission-CvYQNUqZ.js';
8
+ import '../brain-etbcbRwV.js';
9
+ import '../permission-CC7XFYWG.js';
@@ -1,4 +1,4 @@
1
- import { E as EventBus } from './brain-BYcK__Ym.js';
1
+ import { E as EventBus } from './brain-etbcbRwV.js';
2
2
  import { M as MailboxMessage, a as MailboxAgentStatus, b as Mailbox, c as MailboxSendInput, d as MailboxQuery, e as MailboxAckInput, f as MailboxAckBatchInput, A as AgentRegistrationInput, g as AgentHeartbeatInput, C as ClientRegistrationInput, h as ClientHeartbeatInput, i as ClientStatus, P as PurgeOptions, j as PurgeResult } from './mailbox-types-Ct2hJq0P.js';
3
3
 
4
4
  declare const HQ_PROTOCOL_VERSION: 1;
@@ -58,7 +58,7 @@ interface HqEventEnvelope<TPayload = unknown> {
58
58
  seq: number;
59
59
  payload: TPayload;
60
60
  }
61
- type HqEventType = 'client.hello' | 'client.heartbeat' | 'session.started' | 'session.status' | 'session.usage' | 'tool.started' | 'tool.completed' | 'fleet.snapshot' | 'fleet.event' | 'mailbox.snapshot' | 'mailbox.event' | 'worklist.snapshot' | 'git.snapshot';
61
+ type HqEventType = 'client.hello' | 'client.heartbeat' | 'session.started' | 'session.status' | 'session.usage' | 'tool.started' | 'tool.completed' | 'fleet.snapshot' | 'fleet.event' | 'mailbox.snapshot' | 'mailbox.event' | 'worklist.snapshot' | 'git.snapshot' | 'agent.message' | 'agent.status' | 'session.snapshot' | 'session.transcript' | 'session.ended';
62
62
  interface HqClientHeartbeatPayload {
63
63
  uptimeMs: number;
64
64
  activeSessionId?: string;
@@ -118,6 +118,26 @@ interface HqFleetSnapshotPayload {
118
118
  totalCostUsd?: number;
119
119
  subagents: readonly HqSubagentSummary[];
120
120
  }
121
+ /** Payload for `agent.message` events — a subagent's conversational output. */
122
+ interface HqAgentMessagePayload {
123
+ subagentId: string;
124
+ agentName: string;
125
+ content: string;
126
+ kind: 'text' | 'tool_use' | 'error' | 'status';
127
+ iteration: number;
128
+ ts: string;
129
+ toolName?: string;
130
+ costUsd?: number;
131
+ }
132
+ /** Payload for `agent.status` events — a subagent's lifecycle transition. */
133
+ interface HqAgentStatusPayload {
134
+ subagentId: string;
135
+ agentName: string;
136
+ status: 'spawned' | 'running' | 'completed' | 'failed' | 'timeout' | 'stopped' | 'budget_exhausted';
137
+ ts: string;
138
+ summary?: string;
139
+ task?: string;
140
+ }
121
141
  interface HqFleetEventPayload {
122
142
  runId: string;
123
143
  subagentId?: string;
@@ -125,6 +145,82 @@ interface HqFleetEventPayload {
125
145
  summary?: string;
126
146
  data?: unknown;
127
147
  }
148
+ type HqSessionLiveStatus = 'active' | 'idle' | 'closing' | 'stale';
149
+ type HqSessionAgentLiveStatus = 'idle' | 'running' | 'streaming' | 'waiting_user' | 'error';
150
+ /** A single live agent inside a session snapshot (mirrors SessionRegistry's AgentEntry). */
151
+ interface HqSessionAgentSummary {
152
+ id: string;
153
+ name: string;
154
+ /** UTC ISO timestamp when this agent/run started, when known. */
155
+ startedAt?: string;
156
+ status: HqSessionAgentLiveStatus;
157
+ currentTool?: string;
158
+ iterations: number;
159
+ toolCalls: number;
160
+ costUsd?: number;
161
+ tokensIn?: number;
162
+ tokensOut?: number;
163
+ ctxPct?: number;
164
+ model?: string;
165
+ /** Throttled tail of the response currently streaming, when known. */
166
+ partialText?: string;
167
+ lastActivityAt: string;
168
+ }
169
+ /** Payload for `session.snapshot` — one connected terminal's live state. */
170
+ interface HqSessionSnapshotPayload {
171
+ sessionId: string;
172
+ clientKind: HqClientKind;
173
+ machineId: string;
174
+ hostname?: string;
175
+ pid?: number;
176
+ projectId: string;
177
+ projectName: string;
178
+ projectRoot: string;
179
+ gitBranch?: string;
180
+ status: HqSessionLiveStatus;
181
+ startedAt: string;
182
+ lastActivityAt: string;
183
+ agentCount: number;
184
+ agents: readonly HqSessionAgentSummary[];
185
+ }
186
+ type HqTranscriptRole = 'user' | 'assistant' | 'tool' | 'system' | 'error';
187
+ /** One rendered conversation turn. Canonical shape shared by client streaming
188
+ * and server-side JSONL replay so both planes agree. */
189
+ interface HqTranscriptEntry {
190
+ ts: string;
191
+ role: HqTranscriptRole;
192
+ text: string;
193
+ tool?: string;
194
+ /** Stringified tool input/arguments, for tool calls (shown alongside the result). */
195
+ toolInput?: string;
196
+ durationMs?: number;
197
+ isError?: boolean;
198
+ toolUseId?: string;
199
+ /** Which agent/subagent produced this turn, when attribution is known. */
200
+ agentId?: string;
201
+ }
202
+ /** Payload for `session.transcript` — an incremental batch of new turns. */
203
+ interface HqTranscriptAppendPayload {
204
+ sessionId: string;
205
+ /** Monotonic sequence index of the FIRST entry in this batch within the session. */
206
+ fromSeq: number;
207
+ entries: readonly HqTranscriptEntry[];
208
+ }
209
+ /** Payload for `session.ended` — a terminal closed. */
210
+ interface HqSessionEndedPayload {
211
+ sessionId: string;
212
+ endedAt: string;
213
+ }
214
+ /** A physical machine, aggregated by HQ from connected clients' machineId. */
215
+ interface HqMachineRecord {
216
+ machineId: string;
217
+ hostname?: string;
218
+ clientCount: number;
219
+ sessionCount: number;
220
+ agentCount: number;
221
+ projectIds: readonly string[];
222
+ lastActivityAt: string;
223
+ }
128
224
  type HqMailboxMessageType = 'note' | 'ask' | 'assign' | 'steer' | 'btw' | 'broadcast' | 'status' | 'result' | 'control';
129
225
  type HqMailboxPriority = 'low' | 'normal' | 'high';
130
226
  type HqMailboxAgentStatus = 'idle' | 'running' | 'streaming' | 'waiting_user' | 'error' | 'offline';
@@ -276,6 +372,10 @@ interface HqSnapshot {
276
372
  sessions: readonly HqSessionSummary[];
277
373
  fleets: readonly HqFleetSummary[];
278
374
  mailboxes: readonly HqMailboxSummary[];
375
+ /** Physical machines aggregated from connected clients (optional, additive). */
376
+ machines?: readonly HqMachineRecord[];
377
+ /** Live terminal sessions with their agents — the spine of the fleet tree. */
378
+ liveSessions?: readonly HqSessionSnapshotPayload[];
279
379
  totals: {
280
380
  activeProjects: number;
281
381
  activeClients: number;
@@ -284,6 +384,10 @@ interface HqSnapshot {
284
384
  unreadMailboxMessages: number;
285
385
  incompleteMailboxMessages: number;
286
386
  totalCostUsd: number;
387
+ /** Distinct physical machines currently connected. */
388
+ activeMachines?: number;
389
+ /** Total live agents across all sessions. */
390
+ activeAgents?: number;
287
391
  };
288
392
  }
289
393
  interface HqBrowserSnapshotMessage {
@@ -501,6 +605,22 @@ declare class HqPublisher {
501
605
  sessionId?: string;
502
606
  timestamp?: string;
503
607
  }): HqEventEnvelope<HqMailboxEventPayload>;
608
+ /** The client identity this publisher announced (clientId, kind, machineId, …). */
609
+ get identity(): HqClientIdentity;
610
+ /** The project identity this publisher is bound to. */
611
+ get project(): HqProjectIdentity;
612
+ /** Publish a live session/terminal snapshot (state + agents). */
613
+ publishSessionSnapshot(payload: HqSessionSnapshotPayload, opts?: {
614
+ timestamp?: string;
615
+ }): HqEventEnvelope<HqSessionSnapshotPayload>;
616
+ /** Publish an incremental batch of transcript turns for a session. */
617
+ publishTranscriptAppend(payload: HqTranscriptAppendPayload, opts?: {
618
+ timestamp?: string;
619
+ }): HqEventEnvelope<HqTranscriptAppendPayload>;
620
+ /** Mark a session/terminal as ended. */
621
+ publishSessionEnded(payload: HqSessionEndedPayload, opts?: {
622
+ timestamp?: string;
623
+ }): HqEventEnvelope<HqSessionEndedPayload>;
504
624
  pollCommands(): void;
505
625
  ackCommand(result: HqPublisherCommandResult): void;
506
626
  private sendHello;
@@ -532,6 +652,7 @@ declare class HqPublisher {
532
652
  * @module GlobalMailbox
533
653
  */
534
654
 
655
+ type HqPublisherRef = HqPublisher | (() => HqPublisher | undefined);
535
656
  /**
536
657
  * Derive the project-level mailbox directory path.
537
658
  *
@@ -593,10 +714,11 @@ declare class GlobalMailbox implements Mailbox {
593
714
  /**
594
715
  * @param projectDir — `~/.wrongstack/projects/<slug>/`
595
716
  * @param events — optional EventBus for real-time TUI/WebUI notifications
596
- * @param hqPublisher — optional HQ publisher for cross-project telemetry
717
+ * @param hqPublisher — optional HQ publisher, or getter, for cross-project telemetry
597
718
  */
598
- constructor(projectDir: string, events?: EventBus, hqPublisher?: HqPublisher);
719
+ constructor(projectDir: string, events?: EventBus, hqPublisher?: HqPublisherRef);
599
720
  private get hqMailboxId();
721
+ private get hqPublisher();
600
722
  private publishHqMailboxEvent;
601
723
  private publishHqMailboxSnapshot;
602
724
  send(input: MailboxSendInput): Promise<MailboxMessage>;
@@ -621,6 +743,20 @@ declare class GlobalMailbox implements Mailbox {
621
743
  * should call this directly (it's what {@link _readMessagesFresh} aliases).
622
744
  */
623
745
  private _readMessages;
746
+ /**
747
+ * Read only newly-appended bytes from the file and append them to the
748
+ * in-memory cache. This avoids re-reading and re-parsing the entire file
749
+ * when another process appended messages since our last read.
750
+ *
751
+ * Only safe when the file grew in size (messages are append-only). When
752
+ * the file was rewritten (ack/purge changed existing content), callers
753
+ * must fall back to {@link _readMessages}.
754
+ *
755
+ * @returns The (now up-to-date) message cache.
756
+ */
757
+ private _readNewMessagesOnly;
758
+ /** Parse a JSONL string into MailboxMessage[], including migration. */
759
+ private _parseLines;
624
760
  /**
625
761
  * Read messages, then adopt the result as the in-memory cache. Use this
626
762
  * from writers that just took the file lock — the read reflects the
@@ -636,6 +772,10 @@ declare class GlobalMailbox implements Mailbox {
636
772
  * stat matches the cached mtime+size we return the cached array — no
637
773
  * file read and no JSON.parse — collapsing the per-iteration query
638
774
  * cost on the mailbox-loop hot path.
775
+ *
776
+ * When the file only grew (new messages appended by another process),
777
+ * we read and parse just the tail bytes instead of the entire file.
778
+ * This avoids re-parsing the full 10K-message history on every check.
639
779
  */
640
780
  private _readMessagesCached;
641
781
  /**
@@ -661,4 +801,4 @@ declare class GlobalMailbox implements Mailbox {
661
801
  private _writeClientRegistry;
662
802
  }
663
803
 
664
- export { type HqSessionStatusPayload as $, type HqMailboxMappingOptions as A, type HqMailboxMessageSummary as B, type HqMailboxMessageType as C, DEFAULT_HQ_REDACTION_POLICY as D, type HqMailboxPriority as E, type HqMailboxSnapshotOptions as F, GlobalMailbox as G, HQ_PROTOCOL_VERSION as H, type HqMailboxSnapshotPayload as I, type HqMailboxSummary as J, type HqParseResult as K, type HqPathPolicy as L, type HqProjectIdentity as M, type HqProjectRecord as N, type HqProjectStatus as O, type HqProtocolVersion as P, type HqPublishEventOptions as Q, HqPublisher as R, type HqPublisherCommandHandler as S, type HqPublisherCommandResult as T, type HqPublisherOptions as U, type HqQueuedCommand as V, type HqRedactionPolicy as W, type HqServerCommandBatchMessage as X, type HqServerMessage as Y, type HqSessionStartedPayload as Z, type HqSessionStatus as _, type HqAlertMessage as a, type HqSessionSummary as a0, type HqSnapshot as a1, type HqSocketFactory as a2, type HqSocketLike as a3, type HqSubagentSummary as a4, type HqToolArgsPolicy as a5, type HqToolCompletedPayload as a6, type HqToolStartedPayload as a7, type HqUsagePayload as a8, type HqWelcomePayload as a9, type HqWorklistCounts as aa, type HqWorklistSnapshotPayload as ab, type HqWorkspaceKind as ac, createHqEventEnvelope as ad, createMailboxEventPayload as ae, createMailboxSnapshotPayload as af, createMailboxSnapshotPayloadFromMailbox as ag, mapMailboxAgentToHqSummary as ah, mapMailboxMessageToHqSummary as ai, parseHqEventPayload as aj, parseHqFrame as ak, resolveProjectDir as al, type HqBrowserEventMessage as b, type HqBrowserMessage as c, type HqBrowserSnapshotMessage as d, type HqClientCapability as e, type HqClientCommandAckMessage as f, type HqClientCommandPollMessage as g, type HqClientEventMessage as h, type HqClientHeartbeatPayload as i, type HqClientHelloMessage as j, type HqClientHelloPayload as k, type HqClientIdentity as l, type HqClientKind as m, type HqClientMessage as n, type HqClientRecord as o, type HqEventEnvelope as p, type HqEventPayloadResult as q, type HqEventType as r, type HqFleetEventPayload as s, type HqFleetSnapshotPayload as t, type HqFleetSummary as u, type HqGitSnapshotPayload as v, type HqMailboxAgentStatus as w, type HqMailboxAgentSummary as x, type HqMailboxEventAction as y, type HqMailboxEventPayload as z };
804
+ export { type HqServerMessage as $, type HqMailboxAgentSummary as A, type HqMailboxEventAction as B, type HqMailboxEventPayload as C, DEFAULT_HQ_REDACTION_POLICY as D, type HqMailboxMappingOptions as E, type HqMailboxMessageSummary as F, GlobalMailbox as G, HQ_PROTOCOL_VERSION as H, type HqMailboxMessageType as I, type HqMailboxPriority as J, type HqMailboxSnapshotOptions as K, type HqMailboxSnapshotPayload as L, type HqMailboxSummary as M, type HqParseResult as N, type HqPathPolicy as O, type HqProjectIdentity as P, type HqProjectRecord as Q, type HqProjectStatus as R, type HqProtocolVersion as S, type HqPublishEventOptions as T, HqPublisher as U, type HqPublisherCommandHandler as V, type HqPublisherCommandResult as W, type HqPublisherOptions as X, type HqQueuedCommand as Y, type HqRedactionPolicy as Z, type HqServerCommandBatchMessage as _, type HqAgentMessagePayload as a, type HqSessionAgentLiveStatus as a0, type HqSessionAgentSummary as a1, type HqSessionEndedPayload as a2, type HqSessionLiveStatus as a3, type HqSessionSnapshotPayload as a4, type HqSessionStartedPayload as a5, type HqSessionStatus as a6, type HqSessionStatusPayload as a7, type HqSessionSummary as a8, type HqSnapshot as a9, type HqSocketFactory as aa, type HqSocketLike as ab, type HqSubagentSummary as ac, type HqToolArgsPolicy as ad, type HqToolCompletedPayload as ae, type HqToolStartedPayload as af, type HqTranscriptAppendPayload as ag, type HqTranscriptEntry as ah, type HqTranscriptRole as ai, type HqUsagePayload as aj, type HqWelcomePayload as ak, type HqWorklistCounts as al, type HqWorklistSnapshotPayload as am, type HqWorkspaceKind as an, createHqEventEnvelope as ao, createMailboxEventPayload as ap, createMailboxSnapshotPayload as aq, createMailboxSnapshotPayloadFromMailbox as ar, mapMailboxAgentToHqSummary as as, mapMailboxMessageToHqSummary as at, parseHqEventPayload as au, parseHqFrame as av, resolveProjectDir as aw, type HqAgentStatusPayload as b, type HqAlertMessage as c, type HqBrowserEventMessage as d, type HqBrowserMessage as e, type HqBrowserSnapshotMessage as f, type HqClientCapability as g, type HqClientCommandAckMessage as h, type HqClientCommandPollMessage as i, type HqClientEventMessage as j, type HqClientHeartbeatPayload as k, type HqClientHelloMessage as l, type HqClientHelloPayload as m, type HqClientIdentity as n, type HqClientKind as o, type HqClientMessage as p, type HqClientRecord as q, type HqEventEnvelope as r, type HqEventPayloadResult as s, type HqEventType as t, type HqFleetEventPayload as u, type HqFleetSnapshotPayload as v, type HqFleetSummary as w, type HqGitSnapshotPayload as x, type HqMachineRecord as y, type HqMailboxAgentStatus as z };
@@ -1,14 +1,14 @@
1
1
  import { S as SkillLoader, a as SkillManifest, b as SkillEntry } from './skill-DGIXCtdv.js';
2
2
  import { W as WstackPaths } from './wstack-paths-hOpNLmvf.js';
3
- import { P as Provider, C as Context } from './context-Dp87Bcaq.js';
4
- import { C as Compactor, a as CompactReport } from './compactor-C2RKEBtC.js';
5
- import { M as MessageSelector } from './selector-CXl2_y9W.js';
6
- import { E as EventBus } from './brain-BYcK__Ym.js';
7
- import { M as MiddlewareHandler } from './pipeline-l_zzFRh3.js';
8
- import { S as SessionEventBridge } from './session-event-bridge-Ccud20CC.js';
9
- import { C as ContextWindowAggressiveOn, h as ContextWindowPolicy } from './config-C_ae2k86.js';
10
- import { g as Agent, D as DoneCondition } from './agent-subagent-runner-SHJW7t8q.js';
11
- import { R as RunResult, S as SystemPromptContributor } from './index-W4VJCzHa.js';
3
+ import { P as Provider, C as Context } from './context-Dw55zZ_Q.js';
4
+ import { C as Compactor, a as CompactReport } from './compactor-72ug-ZRB.js';
5
+ import { M as MessageSelector } from './selector-C4ORTOid.js';
6
+ import { E as EventBus } from './brain-etbcbRwV.js';
7
+ import { M as MiddlewareHandler } from './pipeline-Ckkn3AOA.js';
8
+ import { S as SessionEventBridge } from './session-event-bridge-CeNpUL9w.js';
9
+ import { C as ContextWindowAggressiveOn, h as ContextWindowPolicy } from './config-rRS8yorV.js';
10
+ import { g as Agent, D as DoneCondition } from './agent-subagent-runner-BZa_IEcd.js';
11
+ import { R as RunResult, S as SystemPromptContributor } from './index-DWm_PE9L.js';
12
12
 
13
13
  interface SkillLoaderOptions {
14
14
  paths: WstackPaths;
@@ -1,4 +1,4 @@
1
- import { E as EventBus } from './brain-BYcK__Ym.js';
1
+ import { E as EventBus } from './brain-etbcbRwV.js';
2
2
 
3
3
  /**
4
4
  * Long-running autonomous mission. A goal survives across sessions and