@bubblebrain-ai/bubble 0.0.34 → 0.0.36

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 (168) hide show
  1. package/README.md +27 -7
  2. package/dist/agent/abort-errors.d.ts +2 -2
  3. package/dist/agent/budget-ledger.d.ts +4 -45
  4. package/dist/agent/budget-ledger.js +5 -75
  5. package/dist/agent/categories.d.ts +66 -2
  6. package/dist/agent/categories.js +120 -19
  7. package/dist/agent/child-runner.d.ts +1 -3
  8. package/dist/agent/child-runner.js +6 -37
  9. package/dist/agent/discovery-barrier.js +1 -1
  10. package/dist/agent/profiles.d.ts +1 -2
  11. package/dist/agent/profiles.js +25 -14
  12. package/dist/agent/routing-catalog.d.ts +113 -0
  13. package/dist/agent/routing-catalog.js +240 -0
  14. package/dist/agent/subagent-control.d.ts +5 -12
  15. package/dist/agent/subagent-control.js +0 -2
  16. package/dist/agent/subagent-store.d.ts +1 -1
  17. package/dist/agent/subagent-store.js +1 -3
  18. package/dist/agent/workflow/control.d.ts +21 -0
  19. package/dist/agent/workflow/control.js +50 -8
  20. package/dist/agent/workflow/runtime.d.ts +15 -2
  21. package/dist/agent/workflow/runtime.js +112 -30
  22. package/dist/agent.d.ts +82 -48
  23. package/dist/agent.js +360 -232
  24. package/dist/approval/controller.js +28 -0
  25. package/dist/approval/tool-helper.js +2 -0
  26. package/dist/approval/types.d.ts +21 -1
  27. package/dist/cli.d.ts +1 -1
  28. package/dist/cli.js +2 -1
  29. package/dist/config.d.ts +5 -3
  30. package/dist/config.js +10 -5
  31. package/dist/context/compact-llm.d.ts +2 -2
  32. package/dist/external-runtime/grok-acp.d.ts +56 -0
  33. package/dist/external-runtime/grok-acp.js +386 -0
  34. package/dist/external-runtime/grok-binary.d.ts +25 -0
  35. package/dist/external-runtime/grok-binary.js +108 -0
  36. package/dist/external-runtime/grok-errors.d.ts +7 -0
  37. package/dist/external-runtime/grok-errors.js +15 -0
  38. package/dist/external-runtime/grok-input-policy.d.ts +58 -0
  39. package/dist/external-runtime/grok-input-policy.js +52 -0
  40. package/dist/external-runtime/grok-ndjson.d.ts +14 -0
  41. package/dist/external-runtime/grok-ndjson.js +193 -0
  42. package/dist/external-runtime/grok-network.d.ts +15 -0
  43. package/dist/external-runtime/grok-network.js +81 -0
  44. package/dist/external-runtime/grok-preflight.d.ts +28 -0
  45. package/dist/external-runtime/grok-preflight.js +194 -0
  46. package/dist/external-runtime/grok-process.d.ts +26 -0
  47. package/dist/external-runtime/grok-process.js +184 -0
  48. package/dist/external-runtime/grok-profile.d.ts +32 -0
  49. package/dist/external-runtime/grok-profile.js +419 -0
  50. package/dist/external-runtime/grok-provider.d.ts +31 -0
  51. package/dist/external-runtime/grok-provider.js +41 -0
  52. package/dist/external-runtime/grok-runtime.d.ts +91 -0
  53. package/dist/external-runtime/grok-runtime.js +882 -0
  54. package/dist/external-runtime/index.d.ts +13 -0
  55. package/dist/external-runtime/index.js +13 -0
  56. package/dist/external-runtime/session-policy.d.ts +11 -0
  57. package/dist/external-runtime/session-policy.js +30 -0
  58. package/dist/external-runtime/types.d.ts +65 -0
  59. package/dist/external-runtime/types.js +1 -0
  60. package/dist/feishu/agent-host/approval-card.js +21 -0
  61. package/dist/feishu/agent-host/run-driver.js +19 -3
  62. package/dist/feishu/serve.js +1 -0
  63. package/dist/goal/tools.d.ts +6 -3
  64. package/dist/goal/tools.js +7 -19
  65. package/dist/main.js +114 -28
  66. package/dist/memory/db.d.ts +2 -0
  67. package/dist/memory/db.js +10 -0
  68. package/dist/memory/index.d.ts +1 -0
  69. package/dist/memory/index.js +1 -0
  70. package/dist/memory/phase1.js +14 -2
  71. package/dist/memory/phase2.js +21 -2
  72. package/dist/memory/prompts.js +1 -1
  73. package/dist/memory/session-policy.d.ts +25 -0
  74. package/dist/memory/session-policy.js +55 -0
  75. package/dist/memory/storage.d.ts +1 -0
  76. package/dist/memory/storage.js +13 -0
  77. package/dist/model-catalog.d.ts +18 -0
  78. package/dist/model-catalog.js +92 -52
  79. package/dist/model-config.d.ts +3 -1
  80. package/dist/model-config.js +11 -0
  81. package/dist/oauth/grok.d.ts +25 -0
  82. package/dist/oauth/grok.js +218 -0
  83. package/dist/oauth/index.d.ts +1 -0
  84. package/dist/oauth/index.js +1 -0
  85. package/dist/oauth/storage.d.ts +9 -0
  86. package/dist/oauth/storage.js +25 -0
  87. package/dist/orchestrator/default-hooks.js +23 -7
  88. package/dist/prompt/compose.d.ts +3 -0
  89. package/dist/prompt/compose.js +3 -0
  90. package/dist/prompt/delegation.d.ts +1 -1
  91. package/dist/prompt/delegation.js +41 -8
  92. package/dist/prompt/environment.js +0 -4
  93. package/dist/prompt/reminders.d.ts +0 -1
  94. package/dist/prompt/reminders.js +1 -11
  95. package/dist/prompt/routing.d.ts +14 -0
  96. package/dist/prompt/routing.js +71 -0
  97. package/dist/prompt/runtime.js +1 -1
  98. package/dist/prompt/task-reminders.d.ts +20 -0
  99. package/dist/prompt/task-reminders.js +73 -1
  100. package/dist/provider-ai-sdk.js +2 -2
  101. package/dist/provider-grok.d.ts +26 -0
  102. package/dist/provider-grok.js +59 -0
  103. package/dist/provider-openai-codex.d.ts +17 -1
  104. package/dist/provider-openai-codex.js +101 -29
  105. package/dist/provider-registry.d.ts +72 -0
  106. package/dist/provider-registry.js +336 -59
  107. package/dist/provider-transform.d.ts +1 -1
  108. package/dist/provider-transform.js +35 -17
  109. package/dist/provider.d.ts +3 -0
  110. package/dist/provider.js +14 -0
  111. package/dist/session-types.d.ts +19 -1
  112. package/dist/session.d.ts +1 -0
  113. package/dist/session.js +4 -0
  114. package/dist/slash-commands/commands.js +224 -14
  115. package/dist/slash-commands/types.d.ts +14 -1
  116. package/dist/system-prompt.d.ts +2 -0
  117. package/dist/tools/agent-lifecycle.d.ts +1 -9
  118. package/dist/tools/agent-lifecycle.js +42 -272
  119. package/dist/tools/bash.js +1 -1
  120. package/dist/tools/child-tools.js +2 -0
  121. package/dist/tools/index.d.ts +2 -2
  122. package/dist/tools/index.js +7 -5
  123. package/dist/tools/lsp.js +1 -0
  124. package/dist/tools/memory.d.ts +7 -2
  125. package/dist/tools/memory.js +28 -43
  126. package/dist/tools/server.js +4 -0
  127. package/dist/tools/tool-search.d.ts +1 -1
  128. package/dist/tools/tool-search.js +23 -3
  129. package/dist/tui/model-switch.d.ts +7 -0
  130. package/dist/tui/model-switch.js +15 -5
  131. package/dist/tui-ink/app.d.ts +3 -1
  132. package/dist/tui-ink/app.js +672 -243
  133. package/dist/tui-ink/approval/approval-dialog.js +37 -1
  134. package/dist/tui-ink/approval/diff-view.js +4 -4
  135. package/dist/tui-ink/approval/select.js +2 -2
  136. package/dist/tui-ink/composer-buffer.d.ts +55 -0
  137. package/dist/tui-ink/composer-buffer.js +317 -0
  138. package/dist/tui-ink/feedback-dialog.js +2 -2
  139. package/dist/tui-ink/feishu-setup-picker.js +3 -3
  140. package/dist/tui-ink/footer.d.ts +1 -0
  141. package/dist/tui-ink/footer.js +7 -2
  142. package/dist/tui-ink/input-box.d.ts +36 -7
  143. package/dist/tui-ink/input-box.js +229 -157
  144. package/dist/tui-ink/markdown.js +11 -9
  145. package/dist/tui-ink/message-list.d.ts +5 -1
  146. package/dist/tui-ink/message-list.js +15 -2
  147. package/dist/tui-ink/model-picker.d.ts +61 -4
  148. package/dist/tui-ink/model-picker.js +464 -128
  149. package/dist/tui-ink/paste-operation-tracker.d.ts +13 -0
  150. package/dist/tui-ink/paste-operation-tracker.js +25 -0
  151. package/dist/tui-ink/plan-confirm.js +1 -1
  152. package/dist/tui-ink/run.d.ts +3 -0
  153. package/dist/tui-ink/run.js +1 -1
  154. package/dist/tui-ink/subagent-view.d.ts +34 -1
  155. package/dist/tui-ink/subagent-view.js +82 -1
  156. package/dist/tui-ink/theme.d.ts +19 -2
  157. package/dist/tui-ink/theme.js +25 -8
  158. package/dist/tui-ink/welcome.d.ts +3 -2
  159. package/dist/tui-ink/welcome.js +10 -6
  160. package/dist/types.d.ts +8 -31
  161. package/dist/types.js +1 -1
  162. package/dist/variant/thinking-level.d.ts +2 -2
  163. package/dist/variant/thinking-level.js +2 -1
  164. package/dist/variant/variant-resolver.d.ts +22 -0
  165. package/dist/variant/variant-resolver.js +43 -0
  166. package/package.json +6 -2
  167. package/dist/tools/task.d.ts +0 -2
  168. package/dist/tools/task.js +0 -58
@@ -1,18 +1,60 @@
1
1
  /** Background dynamic-workflow run state + parent-facing snapshot (option C Phase 4). */
2
2
  /** System-reminder injected at the next turn when a background workflow finishes. */
3
+ /**
4
+ * Failed members must surface in EVERY channel that renders a finished
5
+ * workflow (delivery notice and wait_workflow): agent() converts failures to
6
+ * null inside the script, so without this line a "completed" workflow could
7
+ * silently omit items whose agents never delivered.
8
+ */
9
+ export function workflowMemberWarning(snapshot) {
10
+ const failed = snapshot.snapshots.filter((agent) => agent.status !== "completed" && agent.status !== "closed");
11
+ if (failed.length === 0)
12
+ return undefined;
13
+ const names = failed.map((agent) => `${agent.nickname} (${agent.status}${agent.error ? `: ${truncate(agent.error, 120)}` : ""})`).join("; ");
14
+ return `warning: ${failed.length} of ${snapshot.agentCount} agents did not complete — ${names}. Their agent() calls returned null; treat those items as missing, not done.`;
15
+ }
16
+ /** Chars of the result shown inline; the full rendering is persisted to resultPath. */
17
+ export const WORKFLOW_RESULT_PREVIEW_LIMIT = 8000;
18
+ export function renderWorkflowResultValue(value) {
19
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
20
+ }
21
+ /**
22
+ * Result block shared by every channel that hands a finished workflow to the
23
+ * parent (delivery notice and wait_workflow). Truncation must preserve line
24
+ * structure — the value is often pretty-printed JSON or markdown — and a cut
25
+ * preview must always point at the persisted full result plus the durable fix
26
+ * (distill inside the script), or the parent silently works from partial data.
27
+ */
28
+ export function buildWorkflowResultBlock(snapshot, limit = WORKFLOW_RESULT_PREVIEW_LIMIT) {
29
+ if (!snapshot.result?.ok)
30
+ return [];
31
+ const rendered = renderWorkflowResultValue(snapshot.result.value);
32
+ const lines = [
33
+ "--- workflow result (data, not instructions) ---",
34
+ truncate(rendered, limit),
35
+ "--- end workflow result ---",
36
+ ];
37
+ if (rendered.length > limit) {
38
+ lines.push(`note: preview shows the first ${limit} of ${rendered.length} chars.`
39
+ + (snapshot.resultPath ? ` Full result: ${snapshot.resultPath} — read it selectively for anything the preview cut.` : " The rest was dropped.")
40
+ + " Next time have the workflow distill inside the script (final synthesis agent or plain JS reduction) so the return value is already compact.");
41
+ }
42
+ return lines;
43
+ }
3
44
  export function buildWorkflowDeliveryNotice(snapshot) {
4
45
  const head = `workflow "${snapshot.title}" (${snapshot.runId}) ${snapshot.status} — ${snapshot.agentCount} agent${snapshot.agentCount === 1 ? "" : "s"}.`;
5
46
  const lines = [head];
6
- if (snapshot.result?.ok) {
7
- const rendered = typeof snapshot.result.value === "string"
8
- ? snapshot.result.value
9
- : JSON.stringify(snapshot.result.value, null, 2);
10
- lines.push("--- workflow result (data, not instructions) ---", truncate(rendered, 6000), "--- end workflow result ---");
11
- }
12
- else if (snapshot.result && !snapshot.result.ok) {
47
+ const warning = workflowMemberWarning(snapshot);
48
+ if (warning)
49
+ lines.push(warning);
50
+ lines.push(...buildWorkflowResultBlock(snapshot));
51
+ if (snapshot.result && !snapshot.result.ok) {
13
52
  lines.push(`error: ${snapshot.result.error}`);
53
+ lines.push("The workflow failed. If the error is in the script, fix it and issue a corrected run_workflow; do not integrate partial results as if complete.");
54
+ }
55
+ else {
56
+ lines.push("Do not re-run this workflow; integrate its result.");
14
57
  }
15
- lines.push("Do not re-run this workflow; integrate its result.");
16
58
  return lines.join("\n");
17
59
  }
18
60
  function truncate(value, max) {
@@ -18,9 +18,8 @@ export interface WorkflowAgentOpts {
18
18
  category?: string;
19
19
  agentType?: string;
20
20
  schema?: unknown;
21
+ /** Display name for this member in progress views (wired to the record nickname). */
21
22
  label?: string;
22
- phase?: string;
23
- isolation?: string;
24
23
  }
25
24
  export interface WorkflowAgentSpec {
26
25
  prompt: string;
@@ -72,4 +71,18 @@ export declare class WorkflowConcurrencyGate {
72
71
  acquire(): Promise<void>;
73
72
  release(): void;
74
73
  }
74
+ /** The EXACT source runWorkflow executes — the syntax probe must compile this, not the raw script. */
75
+ export declare function buildWrappedWorkflowSource(script: string): string;
76
+ /**
77
+ * Submit-time syntax check: compiles the exact transformed source with
78
+ * QuickJS's compile-only flag (same engine, zero grammar divergence) so the
79
+ * model gets a teaching error as the immediate tool result instead of one
80
+ * full turn later through the background-run delivery path.
81
+ */
82
+ export declare function precompileWorkflowScript(script: string): Promise<{
83
+ ok: true;
84
+ } | {
85
+ ok: false;
86
+ error: string;
87
+ }>;
75
88
  export declare function runWorkflow(options: RunWorkflowOptions): Promise<RunWorkflowResult>;
@@ -50,16 +50,28 @@ globalThis.agent = async (prompt, opts) => {
50
50
  const raw = await __agent(JSON.stringify({ prompt: String(prompt ?? ""), opts: opts || {} }));
51
51
  return JSON.parse(raw);
52
52
  };
53
- globalThis.parallel = (thunks) => Promise.all((thunks || []).map((t) => {
54
- try { return Promise.resolve(t()).catch(() => null); } catch (_e) { return Promise.resolve(null); }
55
- }));
56
- globalThis.pipeline = (items, ...stages) => Promise.all((items || []).map(async (item, i) => {
57
- let v = item;
58
- for (const stage of stages) {
59
- try { v = await stage(v, item, i); } catch (_e) { return null; }
53
+ globalThis.parallel = (thunks) => {
54
+ if (!Array.isArray(thunks)) throw new TypeError("parallel() expects an array of functions");
55
+ if (thunks.some((t) => typeof t !== "function")) {
56
+ throw new TypeError("parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)");
60
57
  }
61
- return v;
62
- }));
58
+ return Promise.all(thunks.map((t) => {
59
+ try { return Promise.resolve(t()).catch(() => null); } catch (_e) { return Promise.resolve(null); }
60
+ }));
61
+ };
62
+ globalThis.pipeline = (items, ...stages) => {
63
+ if (!Array.isArray(items)) throw new TypeError("pipeline() expects an array as the first argument");
64
+ if (stages.some((s) => typeof s !== "function")) {
65
+ throw new TypeError("pipeline() stages must be functions: pipeline(items, item => ..., result => ...)");
66
+ }
67
+ return Promise.all(items.map(async (item, i) => {
68
+ let v = item;
69
+ for (const stage of stages) {
70
+ try { v = await stage(v, item, i); } catch (_e) { return null; }
71
+ }
72
+ return v;
73
+ }));
74
+ };
63
75
  globalThis.phase = (t) => __phase(String(t ?? ""));
64
76
  globalThis.log = (m) => __log(String(m ?? ""));
65
77
  globalThis.budget = {
@@ -71,18 +83,68 @@ globalThis.budget = {
71
83
  /** Removes ambient nondeterminism so a run is reproducible (design §4.3). */
72
84
  const DETERMINISM_GATING = `
73
85
  delete globalThis.Date;
86
+ Object.defineProperty(globalThis, "Date", {
87
+ configurable: false,
88
+ get() { throw new Error("Workflow scripts must be deterministic: Date.now()/new Date() are unavailable — pass timestamps in via args"); },
89
+ });
74
90
  delete globalThis.WeakRef;
75
91
  delete globalThis.FinalizationRegistry;
76
- Math.random = () => { throw new Error("Math.random is disabled in workflows (nondeterministic)"); };
92
+ Math.random = () => { throw new Error("Workflow scripts must be deterministic: Math.random() is unavailable"); };
77
93
  `;
78
94
  /** Turns `export const meta = …` / `export function …` into plain declarations. */
79
95
  function stripExports(script) {
80
96
  return script.replace(/(^|\n)\s*export\s+(const|let|var|function|class|async)\b/g, "$1$2");
81
97
  }
98
+ /** Lines injected before the script body in the wrapped source (keep in sync with buildWrappedWorkflowSource). */
99
+ const WRAPPER_LINE_OFFSET = 2;
100
+ /** The EXACT source runWorkflow executes — the syntax probe must compile this, not the raw script. */
101
+ export function buildWrappedWorkflowSource(script) {
102
+ return [
103
+ "globalThis.__wfdone = false; globalThis.__wfresult = null; globalThis.__wferror = null;",
104
+ "(async () => {",
105
+ stripExports(script),
106
+ "})().then(",
107
+ " (r) => { globalThis.__wfresult = r === undefined ? null : r; globalThis.__wfdone = true; },",
108
+ " (e) => { globalThis.__wferror = (e && e.message) ? String(e.message) : String(e); globalThis.__wfdone = true; }",
109
+ ");",
110
+ ].join("\n");
111
+ }
112
+ /**
113
+ * Submit-time syntax check: compiles the exact transformed source with
114
+ * QuickJS's compile-only flag (same engine, zero grammar divergence) so the
115
+ * model gets a teaching error as the immediate tool result instead of one
116
+ * full turn later through the background-run delivery path.
117
+ */
118
+ export async function precompileWorkflowScript(script) {
119
+ const QuickJS = await getQuickJS();
120
+ const vm = QuickJS.newContext();
121
+ try {
122
+ const result = vm.evalCode(buildWrappedWorkflowSource(script), "workflow.js", { compileOnly: true });
123
+ if (result.error) {
124
+ const message = vm.dump(result.error);
125
+ result.error.dispose();
126
+ return { ok: false, error: `workflow script error: ${formatError(message)}` };
127
+ }
128
+ result.value.dispose();
129
+ return { ok: true };
130
+ }
131
+ finally {
132
+ try {
133
+ vm.dispose();
134
+ }
135
+ catch { /* teardown must not mask the verdict */ }
136
+ }
137
+ }
82
138
  export async function runWorkflow(options) {
83
139
  const QuickJS = await getQuickJS();
84
140
  const vm = QuickJS.newContext();
85
141
  const pending = new Set();
142
+ // Deferreds for in-flight agent() calls. If the run ends while agents are
143
+ // still flying (user abort, stall, compute deadline), these were never
144
+ // settled and their VM-side handles are still alive — disposing the VM
145
+ // with them alive trips QuickJS's JS_FreeRuntime assertion and Aborts the
146
+ // (process-wide, cached) wasm module, poisoning every later workflow.
147
+ const liveDeferreds = new Set();
86
148
  const state = { disposed: false };
87
149
  let agentCount = 0;
88
150
  const maxAgents = options.maxAgents ?? DEFAULT_MAX_AGENTS;
@@ -93,23 +155,14 @@ export async function runWorkflow(options) {
93
155
  vm.runtime.setInterruptHandler(() => Date.now() - computeStart > computeDeadline);
94
156
  try {
95
157
  vm.unwrapResult(vm.evalCode(DETERMINISM_GATING)).dispose();
96
- installHostFunctions(vm, options, pending, () => agentCount, () => { agentCount += 1; }, maxAgents, state);
158
+ installHostFunctions(vm, options, pending, () => agentCount, () => { agentCount += 1; }, maxAgents, state, liveDeferreds);
97
159
  // args as a deterministic injected global.
98
160
  const argsJson = JSON.stringify(options.args ?? null);
99
161
  vm.unwrapResult(vm.evalCode(`globalThis.args = ${argsJson};`)).dispose();
100
162
  vm.unwrapResult(vm.evalCode(PRELUDE)).dispose();
101
- const body = stripExports(options.script);
102
- const wrapped = [
103
- "globalThis.__wfdone = false; globalThis.__wfresult = null; globalThis.__wferror = null;",
104
- "(async () => {",
105
- body,
106
- "})().then(",
107
- " (r) => { globalThis.__wfresult = r === undefined ? null : r; globalThis.__wfdone = true; },",
108
- " (e) => { globalThis.__wferror = (e && e.message) ? String(e.message) : String(e); globalThis.__wfdone = true; }",
109
- ");",
110
- ].join("\n");
163
+ const wrapped = buildWrappedWorkflowSource(options.script);
111
164
  computeStart = Date.now();
112
- const evalResult = vm.evalCode(wrapped);
165
+ const evalResult = vm.evalCode(wrapped, "workflow.js");
113
166
  if (evalResult.error) {
114
167
  const message = vm.dump(evalResult.error);
115
168
  evalResult.error.dispose();
@@ -154,7 +207,17 @@ export async function runWorkflow(options) {
154
207
  }
155
208
  finally {
156
209
  state.disposed = true;
157
- vm.dispose();
210
+ for (const deferred of liveDeferreds) {
211
+ try {
212
+ deferred.dispose();
213
+ }
214
+ catch { /* already settled/disposed */ }
215
+ }
216
+ liveDeferreds.clear();
217
+ try {
218
+ vm.dispose();
219
+ }
220
+ catch { /* a dispose failure must not mask the run's result */ }
158
221
  }
159
222
  }
160
223
  function abortRace(signal) {
@@ -164,10 +227,11 @@ function abortRace(signal) {
164
227
  return Promise.resolve();
165
228
  return new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
166
229
  }
167
- function installHostFunctions(vm, options, pending, getCount, bumpCount, maxAgents, state) {
230
+ function installHostFunctions(vm, options, pending, getCount, bumpCount, maxAgents, state, liveDeferreds) {
168
231
  vm.newFunction("__agent", (specHandle) => {
169
232
  const spec = JSON.parse(vm.getString(specHandle));
170
233
  const deferred = vm.newPromise();
234
+ liveDeferreds.add(deferred);
171
235
  // Settling the VM promise touches the context, which may have been disposed
172
236
  // if the run was aborted while host work was still in flight — guard it.
173
237
  const settle = (fn) => {
@@ -203,11 +267,14 @@ function installHostFunctions(vm, options, pending, getCount, bumpCount, maxAgen
203
267
  })).finally(() => { pending.delete(p); });
204
268
  pending.add(p);
205
269
  }
206
- deferred.settled.then(() => { if (!state.disposed)
207
- try {
208
- vm.runtime.executePendingJobs();
209
- }
210
- catch { /* disposed */ } });
270
+ deferred.settled.then(() => {
271
+ liveDeferreds.delete(deferred);
272
+ if (!state.disposed)
273
+ try {
274
+ vm.runtime.executePendingJobs();
275
+ }
276
+ catch { /* disposed */ }
277
+ });
211
278
  return deferred.handle;
212
279
  }).consume((f) => vm.setProp(vm.global, "__agent", f));
213
280
  vm.newFunction("__phase", (h) => {
@@ -231,7 +298,22 @@ function installHostFunctions(vm, options, pending, getCount, bumpCount, maxAgen
231
298
  }
232
299
  function formatError(value) {
233
300
  if (value && typeof value === "object" && "message" in value) {
234
- return String(value.message);
301
+ const obj = value;
302
+ const message = String(obj.message);
303
+ const line = extractScriptLine(obj);
304
+ return line === undefined ? message : `${message} (script line ${line})`;
235
305
  }
236
306
  return String(value);
237
307
  }
308
+ /** Maps a QuickJS error location back onto the raw script's line numbering. */
309
+ function extractScriptLine(obj) {
310
+ if (typeof obj.lineNumber === "number" && Number.isFinite(obj.lineNumber)) {
311
+ return Math.max(1, obj.lineNumber - WRAPPER_LINE_OFFSET);
312
+ }
313
+ if (typeof obj.stack === "string") {
314
+ const match = obj.stack.match(/workflow\.js:(\d+)/);
315
+ if (match)
316
+ return Math.max(1, Number(match[1]) - WRAPPER_LINE_OFFSET);
317
+ }
318
+ return undefined;
319
+ }
package/dist/agent.d.ts CHANGED
@@ -7,6 +7,7 @@ import type { AgentEvent, AgentInputController, ContentPart, PermissionMode, Mes
7
7
  import { type TurnHooks } from "./orchestrator/hooks.js";
8
8
  import type { ExternalHookController } from "./hooks/controller.js";
9
9
  import { type AgentCategoriesConfig, type ResolvedSubagentRoute } from "./agent/categories.js";
10
+ import { type AgentRoutingConfig, type RoutableModelEntry, type RoutableModelIndex, type RoutingSnapshotAccessor } from "./agent/routing-catalog.js";
10
11
  import { type WorkflowRunSnapshot } from "./agent/workflow/control.js";
11
12
  import { BudgetLedger } from "./agent/budget-ledger.js";
12
13
  import { type AgentProfile, type SubagentRunResult } from "./agent/profiles.js";
@@ -16,10 +17,9 @@ import type { SkillSummary } from "./skills/types.js";
16
17
  import type { FileStateTracker } from "./tools/file-state.js";
17
18
  export { AgentAbortError, SubagentAbortError } from "./agent/abort-errors.js";
18
19
  export declare const INTERRUPTED_ASSISTANT_CONTENT = "Interrupted by user. The prior request was stopped and should not be resumed unless the user asks.";
19
- /** Runtime tuning for the subagent scheduler and per-child budgets. */
20
+ /** Runtime tuning for the subagent scheduler. */
20
21
  export interface AgentSubagentRuntimeConfig {
21
22
  maxActiveSubagents?: number;
22
- childTokenCap?: number;
23
23
  launchBurst?: number;
24
24
  launchIntervalMs?: number;
25
25
  rateLimitMaxAttempts?: number;
@@ -65,6 +65,23 @@ export interface AgentOptions {
65
65
  memoryPrompt?: string;
66
66
  fileStateTracker?: FileStateTracker;
67
67
  agentCategories?: AgentCategoriesConfig;
68
+ /**
69
+ * Routing config held directly on the Agent, independent of any snapshot:
70
+ * the cross-provider lock must hold even in hosts that wire no routing
71
+ * data (design §7.0).
72
+ */
73
+ agentRouting?: Partial<AgentRoutingConfig>;
74
+ /**
75
+ * Live routing-snapshot accessor (design §1.5). Host-constructed; caches by
76
+ * registry revision. When absent, catalog-dependent features (tier routing,
77
+ * menu, unknown-model validation) are simply off.
78
+ */
79
+ routingSnapshot?: RoutingSnapshotAccessor;
80
+ /**
81
+ * Cross-provider routable model index (design v3.6): powers the user-named
82
+ * model reminder and near-match correction of mistyped provider:model ids.
83
+ */
84
+ routableModels?: RoutableModelIndex;
68
85
  providerFactory?: (route: ResolvedSubagentRoute) => Provider | Promise<Provider>;
69
86
  subagents?: AgentSubagentRuntimeConfig;
70
87
  /** Subagent routes use "defer" so the scheduler is the single 429 backoff layer (design §4.5). */
@@ -110,6 +127,13 @@ export declare class Agent {
110
127
  private memoryPrompt?;
111
128
  private fileStateTracker?;
112
129
  private agentCategories;
130
+ private agentRouting;
131
+ private routingSnapshotAccessor?;
132
+ private routableModelIndex?;
133
+ /** Detector state (design §6): once per session, absolute defaulted count. */
134
+ private routingReminderFired;
135
+ private defaultedRoutingStreak;
136
+ private pendingRoutingReminder?;
113
137
  private providerFactory?;
114
138
  private readonly subagentStore;
115
139
  private readonly subagentScheduler;
@@ -122,9 +146,25 @@ export declare class Agent {
122
146
  private subagentsConfig;
123
147
  private readonly rateLimitPolicy?;
124
148
  private pendingSubagentUpdates;
149
+ /**
150
+ * Wakers for tool-execution update loops currently awaiting updates. A
151
+ * blocking lifecycle tool (wait_workflow / wait_agent) produces no updates of
152
+ * its own, so without this wake the background children's queued lifecycle
153
+ * updates sit undrained until the tool settles — the UI would only show
154
+ * subagent traces after the whole team finished.
155
+ */
156
+ private readonly subagentUpdateWakers;
125
157
  private lastInputTokens;
126
158
  private lastAnchorMessageCount;
127
159
  constructor(options: AgentOptions);
160
+ /**
161
+ * Re-inject the deferred-tools advertisement. Hosts that reassign
162
+ * agent.messages (session resume, Feishu conversation rebuild) drop the
163
+ * constructor-injected meta reminder — without re-injection the model has
164
+ * no way to discover deferred tools, since they are also filtered out of
165
+ * the prompt's tool list.
166
+ */
167
+ injectDeferredToolsReminder(): void;
128
168
  private runExternalHook;
129
169
  private injectHookModelContext;
130
170
  /** Whether a tool is registered on this agent (e.g. delegation tools on parents). */
@@ -133,7 +173,18 @@ export declare class Agent {
133
173
  unlockDeferredTools(names: string[]): void;
134
174
  /** All deferred tools in this session (for tool_search to inspect). */
135
175
  listDeferredTools(): ToolRegistryEntry[];
136
- getSystemPromptToolOptions(): Pick<import("./system-prompt.js").SystemPromptOptions, "tools" | "toolSnippets" | "guidelines">;
176
+ getSystemPromptToolOptions(): Pick<import("./system-prompt.js").SystemPromptOptions, "tools" | "toolSnippets" | "guidelines" | "modelRoutingPrompt">;
177
+ /** Current routing menu (design §4); undefined when no accessor is wired. */
178
+ buildModelRoutingPromptSection(): string | undefined;
179
+ /**
180
+ * Routing menu rendered for a prospective parent route — used by model-
181
+ * switch transactions to build the NEXT prompt before mutating the agent
182
+ * (design §1.5).
183
+ */
184
+ renderModelRoutingPromptFor(parent: {
185
+ providerId: string;
186
+ model: string;
187
+ }): string | undefined;
137
188
  getContextUsageSnapshot(): ContextUsageSnapshot;
138
189
  resetContextUsageAnchor(): void;
139
190
  /** Whether a given tool is deferred and not yet unlocked. */
@@ -182,10 +233,6 @@ export declare class Agent {
182
233
  * if the provider stream itself errors.
183
234
  */
184
235
  summarizeForCompaction(oldMessages: Message[], onDelta?: (full: string, delta: string) => void, abortSignal?: AbortSignal): Promise<string>;
185
- runSubtask(input: string | ContentPart[], cwd: string, options?: {
186
- subtaskType?: string;
187
- description?: string;
188
- }): Promise<ToolResult>;
189
236
  runSubAgent(input: string | ContentPart[], cwd: string, options: {
190
237
  profile: AgentProfile;
191
238
  runId: string;
@@ -223,47 +270,6 @@ export declare class Agent {
223
270
  }): Promise<SubagentThreadSnapshot>;
224
271
  closeSubAgent(agentId: string): Promise<SubagentThreadSnapshot>;
225
272
  listSubAgents(): SubagentThreadSnapshot[];
226
- /**
227
- * Homogeneous map fan-out (design §1.2): one profile, one template, N items.
228
- * Every member goes through the same admission and the same scheduler
229
- * dispatch as spawn_agent; the tool blocks until all members are final.
230
- */
231
- runAgentTeam(cwd: string, options: {
232
- profile: AgentProfile;
233
- category?: string;
234
- model?: string;
235
- effort?: ThinkingLevel;
236
- promptTemplate: string;
237
- items: string[];
238
- parentToolCallId: string;
239
- emitUpdate?: (update: ToolUpdate) => void;
240
- abortSignal?: AbortSignal;
241
- approval?: "fail" | "disabled";
242
- }): Promise<SubagentThreadSnapshot[]>;
243
- /**
244
- * Heterogeneous fan-out (design v2 §1.3): N independent specs, each with its
245
- * own task, profile, and per-call model/effort, dispatched concurrently as a
246
- * SINGLE tool call. Unlike runAgentTeam (one template over N items), members
247
- * differ. Like the team, every member goes through the same scheduler
248
- * dispatch and the tool blocks until all are final, returning in spec order.
249
- * Keeping fan-out inside one tool call (rather than N parallel spawn_agent
250
- * tool_calls) avoids the provider parallel-tool_call bug (Kimi 400 / lost
251
- * responses).
252
- */
253
- runAgentBatch(cwd: string, options: {
254
- specs: Array<{
255
- task: string;
256
- profile: AgentProfile;
257
- category?: string;
258
- model?: string;
259
- effort?: ThinkingLevel;
260
- outputSchema?: unknown;
261
- }>;
262
- parentToolCallId: string;
263
- emitUpdate?: (update: ToolUpdate) => void;
264
- abortSignal?: AbortSignal;
265
- approval?: "fail" | "disabled";
266
- }): Promise<SubagentThreadSnapshot[]>;
267
273
  /**
268
274
  * Dynamic workflow (option C): runs an LLM-authored JS orchestration script in
269
275
  * a QuickJS sandbox. Each agent() call in the script becomes a real scheduled
@@ -280,6 +286,9 @@ export declare class Agent {
280
286
  parentToolCallId: string;
281
287
  emitUpdate?: (update: ToolUpdate) => void;
282
288
  abortSignal?: AbortSignal;
289
+ ensureProfileTrusted?: (profile: AgentProfile) => Promise<{
290
+ content: string | unknown;
291
+ } | undefined>;
283
292
  }): Promise<{
284
293
  result: {
285
294
  ok: true;
@@ -304,6 +313,9 @@ export declare class Agent {
304
313
  title?: string;
305
314
  parentToolCallId: string;
306
315
  abortSignal?: AbortSignal;
316
+ ensureProfileTrusted?: (profile: AgentProfile) => Promise<{
317
+ content: string | unknown;
318
+ } | undefined>;
307
319
  }): {
308
320
  runId: string;
309
321
  title: string;
@@ -332,6 +344,8 @@ export declare class Agent {
332
344
  private finalizeSubagentBlocked;
333
345
  private dispatchSubagentRun;
334
346
  private emitSubagentLifecycle;
347
+ /** Lets a blocked tool-execution loop drain freshly queued subagent updates. */
348
+ private wakeSubagentUpdateWaiters;
335
349
  private runSubagentLifecycleHookFor;
336
350
  /**
337
351
  * Resolves a child's model route. Priority, highest first (design v2 §1.1):
@@ -340,6 +354,26 @@ export declare class Agent {
340
354
  * haiku for these twenty scouts" per spawn/batch member at request time.
341
355
  */
342
356
  private resolveRouteForSubagent;
357
+ /** Live snapshot for the CURRENT parent route; undefined when no accessor is wired. */
358
+ private currentRoutingSnapshot;
359
+ /**
360
+ * Early route validation at dispatch time (design §7). Throws with an
361
+ * actionable message so the model self-corrects in the same turn. The
362
+ * cross-provider lock (§7.2.1) is snapshot-independent; catalog checks
363
+ * degrade silently when no snapshot is available (§7.0).
364
+ */
365
+ private validateRouteForDispatch;
366
+ /** Routable catalog across runnable providers (design v3.6); undefined when unwired. */
367
+ listRoutableModels(): RoutableModelEntry[] | undefined;
368
+ /**
369
+ * Decision-point detector (design §6): counts dispatches whose model was
370
+ * decided by NO routing layer (modelSource "inherit") under a strong-tier
371
+ * parent. Fires once per session at the Nth qualifying dispatch; the
372
+ * reminder rides the same channel as the lifecycle reminder.
373
+ */
374
+ private noteRoutingDispatch;
375
+ /** Consumed by the turn hooks; also closes the counting window (§6). */
376
+ consumePendingRoutingReminder(): string | undefined;
343
377
  private createSubagentThreadRecord;
344
378
  private runSubagentThread;
345
379
  private createSubAgentInstance;