osborn 0.9.219 → 0.9.220
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.
- package/dist/claude-llm.d.ts +16 -26
- package/dist/claude-llm.js +73 -104
- package/package.json +1 -1
package/dist/claude-llm.d.ts
CHANGED
|
@@ -46,10 +46,6 @@ export declare const NAMED_AGENTS: {
|
|
|
46
46
|
description: string;
|
|
47
47
|
tools: string[];
|
|
48
48
|
grounded: boolean;
|
|
49
|
-
coordination: {
|
|
50
|
-
then: string[];
|
|
51
|
-
startNote: string;
|
|
52
|
-
};
|
|
53
49
|
model: string;
|
|
54
50
|
prompt: string;
|
|
55
51
|
};
|
|
@@ -63,28 +59,13 @@ export declare const NAMED_AGENTS: {
|
|
|
63
59
|
description: string;
|
|
64
60
|
tools: string[];
|
|
65
61
|
grounded: boolean;
|
|
66
|
-
|
|
67
|
-
write: "anywhere";
|
|
68
|
-
};
|
|
69
|
-
reminder: string;
|
|
70
|
-
coordination: {
|
|
71
|
-
then: string[];
|
|
72
|
-
mode: "parallel";
|
|
73
|
-
startNote: string;
|
|
74
|
-
};
|
|
62
|
+
criticalSystemReminder_EXPERIMENTAL: string;
|
|
75
63
|
model: string;
|
|
76
64
|
prompt: string;
|
|
77
65
|
};
|
|
78
66
|
tester: {
|
|
79
67
|
description: string;
|
|
80
68
|
tools: string[];
|
|
81
|
-
policy: {
|
|
82
|
-
write: {
|
|
83
|
-
extensions: RegExp;
|
|
84
|
-
label: string;
|
|
85
|
-
matchBasename: boolean;
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
69
|
model: string;
|
|
89
70
|
prompt: string;
|
|
90
71
|
};
|
|
@@ -98,12 +79,6 @@ export declare const NAMED_AGENTS: {
|
|
|
98
79
|
reviewer: {
|
|
99
80
|
description: string;
|
|
100
81
|
tools: string[];
|
|
101
|
-
policy: {
|
|
102
|
-
write: {
|
|
103
|
-
extensions: RegExp;
|
|
104
|
-
label: string;
|
|
105
|
-
};
|
|
106
|
-
};
|
|
107
82
|
model: string;
|
|
108
83
|
prompt: string;
|
|
109
84
|
};
|
|
@@ -134,6 +109,21 @@ export declare function applyTurbo(agents: Record<string, any>, turbo: boolean):
|
|
|
134
109
|
* Adversarial agents (reviewer/tester) leave `grounded` unset → untouched, stay blind.
|
|
135
110
|
*/
|
|
136
111
|
export declare function applyGrounding(agents: Record<string, any>, sessionId: string | null, workingDir: string | undefined): Record<string, any>;
|
|
112
|
+
/**
|
|
113
|
+
* Decide a Write/Edit/MultiEdit for the acting agent (null = main orchestrator).
|
|
114
|
+
* Pure lookup against WRITE_RULES. Returns:
|
|
115
|
+
* 'allow' → fall through (→ canUseTool workspace auto-approve, or {}).
|
|
116
|
+
* 'defer' → PreToolUse 'ask' (canUseTool decides).
|
|
117
|
+
* 'deny' → hard block with reason.
|
|
118
|
+
*/
|
|
119
|
+
export declare function decideWrite(agentType: string | null, filePath: string): {
|
|
120
|
+
decision: 'allow' | 'defer' | 'deny';
|
|
121
|
+
reason?: string;
|
|
122
|
+
};
|
|
123
|
+
export declare const VERIFIER_CHAIN: Record<string, {
|
|
124
|
+
then: string[];
|
|
125
|
+
mode?: 'parallel' | 'sequential';
|
|
126
|
+
}>;
|
|
137
127
|
/**
|
|
138
128
|
* Claude LLM - Wraps Claude Agent SDK for LiveKit
|
|
139
129
|
* Research mode: reads anything, writes only to session workspace
|
package/dist/claude-llm.js
CHANGED
|
@@ -366,12 +366,6 @@ export const NAMED_AGENTS = {
|
|
|
366
366
|
].join(' '),
|
|
367
367
|
tools: ['Read', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch', 'Task'],
|
|
368
368
|
grounded: true, // applyGrounding() injects the osborn-recall command + ensures Bash
|
|
369
|
-
// Declarative flow. == today: after the researcher finishes, a reasoner-based
|
|
370
|
-
// research gate judges completeness and may send it back for more.
|
|
371
|
-
coordination: {
|
|
372
|
-
then: ['reasoner'],
|
|
373
|
-
startNote: 'When you finish, a reasoner-based gate judges whether your findings are COMPLETE and well-sourced against the task; thin or unsourced findings get sent back to you. Cite file paths + line numbers and explicitly note what you looked for but did NOT find.',
|
|
374
|
-
},
|
|
375
369
|
model: 'sonnet',
|
|
376
370
|
prompt: [
|
|
377
371
|
'You are Osborn\'s research agent. Your job is information gathering — thorough, structured, factual.',
|
|
@@ -408,6 +402,7 @@ export const NAMED_AGENTS = {
|
|
|
408
402
|
'Return findings to the orchestrator — never directly to the user.',
|
|
409
403
|
'Run several researchers in parallel when there are independent threads to investigate.',
|
|
410
404
|
'Hand off back to the orchestrator; it decides whether to invoke planner or writer next.',
|
|
405
|
+
'After you return, a reasoner-based gate judges whether your findings are COMPLETE and well-sourced; thin or unsourced findings get sent back to you — so cite paths/line numbers and state what you looked for but did NOT find.',
|
|
411
406
|
].join('\n'),
|
|
412
407
|
},
|
|
413
408
|
reasoner: {
|
|
@@ -474,17 +469,8 @@ export const NAMED_AGENTS = {
|
|
|
474
469
|
].join(' '),
|
|
475
470
|
tools: ['Read', 'Write', 'Edit', 'MultiEdit', 'Bash', 'Glob', 'Grep', 'NotebookRead', 'NotebookEdit'],
|
|
476
471
|
grounded: true, // applyGrounding() injects the osborn-recall command
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
// Pinned into the writer's system prompt as a hard-to-ignore reminder.
|
|
480
|
-
reminder: 'BEFORE you write implementation code: make sure a test exists for the behavior you are about to change. If none exists, say so explicitly in your report so the tester can cover it — the tester is the agent that writes tests. NEVER weaken, skip, or delete a test to make your change pass.',
|
|
481
|
-
// Declarative flow (drives SubagentStop dispatch + SubagentStart injection).
|
|
482
|
-
// == today: after the writer finishes, reviewer AND tester run in parallel.
|
|
483
|
-
coordination: {
|
|
484
|
-
then: ['reviewer', 'tester'],
|
|
485
|
-
mode: 'parallel',
|
|
486
|
-
startNote: 'When you finish, your change is automatically verified in parallel: a reviewer checks correctness against the git diff, and a tester runs the suite. Make the change review-ready and leave the tree in a runnable state — do not skip cleanup expecting a second pass.',
|
|
487
|
-
},
|
|
472
|
+
// SDK-native soft-behavior field — pinned into the writer's system prompt.
|
|
473
|
+
criticalSystemReminder_EXPERIMENTAL: 'BEFORE you write implementation code: make sure a test exists for the behavior you are about to change. If none exists, say so explicitly in your report so the tester can cover it — the tester is the agent that writes tests. NEVER weaken, skip, or delete a test to make your change pass.',
|
|
488
474
|
model: 'opus',
|
|
489
475
|
prompt: [
|
|
490
476
|
'You are Osborn\'s writer agent. You execute file changes with a verify-first approach.',
|
|
@@ -527,6 +513,7 @@ export const NAMED_AGENTS = {
|
|
|
527
513
|
'Invoked AFTER the planner produces a written plan — the writer is the SOLE agent that edits files.',
|
|
528
514
|
'Do not invoke writer until a plan exists for any multi-step change.',
|
|
529
515
|
'When the writer returns, the orchestrator invokes tester AND reviewer in parallel before surfacing results.',
|
|
516
|
+
'Because that verification runs automatically after you finish, make the change review-ready and leave the tree in a runnable state — do not skip cleanup expecting a second pass.',
|
|
530
517
|
].join('\n'),
|
|
531
518
|
},
|
|
532
519
|
tester: {
|
|
@@ -536,9 +523,7 @@ export const NAMED_AGENTS = {
|
|
|
536
523
|
'Returns structured pass/fail results with exact output — does NOT edit files.',
|
|
537
524
|
'NOT GROUNDED: no session index access — runs tests with fresh eyes, adversarial validation.',
|
|
538
525
|
].join(' '),
|
|
539
|
-
tools: ['Bash', 'Read', 'Glob', 'Grep', 'Write', 'Edit'],
|
|
540
|
-
// Fail-closed: may ONLY write test files (basename .test/.spec.[jt]sx?).
|
|
541
|
-
policy: { write: { extensions: /\.(test|spec)\.[jt]sx?$/, label: 'test', matchBasename: true } },
|
|
526
|
+
tools: ['Bash', 'Read', 'Glob', 'Grep', 'Write', 'Edit'], // write-gate: test files only (WRITE_RULES.tester)
|
|
542
527
|
model: 'sonnet',
|
|
543
528
|
prompt: [
|
|
544
529
|
'You are Osborn\'s tester agent. Your job is running tests and builds, then reporting results.',
|
|
@@ -687,9 +672,7 @@ export const NAMED_AGENTS = {
|
|
|
687
672
|
'and returns an ACCEPT or REJECT verdict with specific, actionable feedback. May write documentation files (.md etc.) only.',
|
|
688
673
|
'NOT GROUNDED: no session index access — reviews with fresh eyes for unbiased adversarial check.',
|
|
689
674
|
].join(' '),
|
|
690
|
-
tools: ['Read', 'Glob', 'Grep', 'Bash', 'Write', 'Edit'],
|
|
691
|
-
// Fail-closed: may ONLY write documentation files.
|
|
692
|
-
policy: { write: { extensions: /\.(md|markdown|mdx|txt|rst|adoc)$/i, label: 'documentation' } },
|
|
675
|
+
tools: ['Read', 'Glob', 'Grep', 'Bash', 'Write', 'Edit'], // write-gate: docs only (WRITE_RULES.reviewer)
|
|
693
676
|
model: 'sonnet',
|
|
694
677
|
prompt: [
|
|
695
678
|
'You are Osborn\'s reviewer agent. You are the VERIFY step in a generator-verifier loop.',
|
|
@@ -857,64 +840,53 @@ export function applyGrounding(agents, sessionId, workingDir) {
|
|
|
857
840
|
}
|
|
858
841
|
return out;
|
|
859
842
|
}
|
|
860
|
-
const
|
|
843
|
+
const WRITE_RULES = {
|
|
844
|
+
writer: 'anywhere',
|
|
845
|
+
tester: { extensions: /\.(test|spec)\.[jt]sx?$/, label: 'test', matchBasename: true },
|
|
846
|
+
reviewer: { extensions: /\.(md|markdown|mdx|txt|rst|adoc)$/i, label: 'documentation' },
|
|
847
|
+
};
|
|
861
848
|
/** Path is inside the per-session sandbox workspace. */
|
|
862
849
|
function isWorkspacePath(filePath) {
|
|
863
850
|
return !!filePath && (filePath.includes('/osb/') ||
|
|
864
851
|
filePath.includes('.osborn/sessions/') ||
|
|
865
852
|
filePath.includes('.osborn/research/'));
|
|
866
853
|
}
|
|
867
|
-
/** Effective write policy for the acting agent (null agentType = main orchestrator). */
|
|
868
|
-
function resolveWritePolicy(agentType, roster) {
|
|
869
|
-
const def = agentType ? roster?.[agentType] : null;
|
|
870
|
-
return def?.policy?.write ?? DEFAULT_WRITE_POLICY;
|
|
871
|
-
}
|
|
872
854
|
/**
|
|
873
|
-
* Decide a Write/Edit/MultiEdit
|
|
874
|
-
*
|
|
875
|
-
* '
|
|
876
|
-
* '
|
|
855
|
+
* Decide a Write/Edit/MultiEdit for the acting agent (null = main orchestrator).
|
|
856
|
+
* Pure lookup against WRITE_RULES. Returns:
|
|
857
|
+
* 'allow' → fall through (→ canUseTool workspace auto-approve, or {}).
|
|
858
|
+
* 'defer' → PreToolUse 'ask' (canUseTool decides).
|
|
859
|
+
* 'deny' → hard block with reason.
|
|
877
860
|
*/
|
|
878
|
-
function decideWrite(
|
|
879
|
-
|
|
861
|
+
export function decideWrite(agentType, filePath) {
|
|
862
|
+
const rule = (agentType && WRITE_RULES[agentType]) || 'workspace';
|
|
863
|
+
if (rule === 'anywhere')
|
|
880
864
|
return { decision: 'defer' };
|
|
881
|
-
if (
|
|
865
|
+
if (rule === 'workspace') {
|
|
882
866
|
if (filePath && !isWorkspacePath(filePath)) {
|
|
883
867
|
return { decision: 'deny', reason: 'Research mode: writes restricted to session workspace.' };
|
|
884
868
|
}
|
|
885
869
|
return { decision: 'allow' };
|
|
886
870
|
}
|
|
887
871
|
// Extension whitelist — fail closed (empty/unknown path denied).
|
|
888
|
-
const target =
|
|
889
|
-
if (!filePath || !
|
|
872
|
+
const target = rule.matchBasename ? (filePath ? basename(resolve(filePath)) : '') : filePath;
|
|
873
|
+
if (!filePath || !rule.extensions.test(target)) {
|
|
890
874
|
const reason = filePath
|
|
891
|
-
? `Write denied: ${filePath} is not a ${
|
|
875
|
+
? `Write denied: ${filePath} is not a ${rule.label} file. This agent may only write ${rule.label} files.`
|
|
892
876
|
: 'Write denied: could not determine target file path. Failing closed.';
|
|
893
877
|
return { decision: 'deny', reason };
|
|
894
878
|
}
|
|
895
879
|
return { decision: 'defer' };
|
|
896
880
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
*/
|
|
907
|
-
function finalizeRoster(agents) {
|
|
908
|
-
const out = {};
|
|
909
|
-
for (const [name, agent] of Object.entries(agents)) {
|
|
910
|
-
const { policy, reminder, coordination, ...rest } = agent;
|
|
911
|
-
if (reminder && !rest.criticalSystemReminder_EXPERIMENTAL) {
|
|
912
|
-
rest.criticalSystemReminder_EXPERIMENTAL = reminder;
|
|
913
|
-
}
|
|
914
|
-
out[name] = rest;
|
|
915
|
-
}
|
|
916
|
-
return out;
|
|
917
|
-
}
|
|
881
|
+
// ── Verifier chaining ───────────────────────────────────────────────────────
|
|
882
|
+
// A plain name→verifiers lookup read by the SubagentStop hook. Same wiring the
|
|
883
|
+
// old hardcoded branches had (writer→reviewer+tester, researcher→reasoner gate),
|
|
884
|
+
// gathered in one visible place. Names resolve to spawnReviewer / spawnTester /
|
|
885
|
+
// spawnResearchGate. Any agent not listed chains to nothing.
|
|
886
|
+
export const VERIFIER_CHAIN = {
|
|
887
|
+
writer: { then: ['reviewer', 'tester'], mode: 'parallel' },
|
|
888
|
+
researcher: { then: ['reasoner'] },
|
|
889
|
+
};
|
|
918
890
|
const RESEARCH_TOOLS = [
|
|
919
891
|
'Read', 'Write', 'Edit', 'Glob', 'Grep',
|
|
920
892
|
'Bash', 'WebSearch', 'WebFetch',
|
|
@@ -2034,10 +2006,6 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2034
2006
|
? getSessionWorkspace(this.#opts.workingDirectory, sessionId)
|
|
2035
2007
|
: null;
|
|
2036
2008
|
const allowedTools = this.#opts.allowedTools || [];
|
|
2037
|
-
// Roster the PreToolUse write-gate reads policy from — the PRE-strip view
|
|
2038
|
-
// (retains `policy`), so DB-backed custom agents (set_agents) get their
|
|
2039
|
-
// write policy enforced too. The SDK receives the finalized copy (agents:).
|
|
2040
|
-
const enforcementRoster = this.#opts.agents ?? NAMED_AGENTS;
|
|
2041
2009
|
const sdkOptions = {
|
|
2042
2010
|
cwd: this.#opts.workingDirectory,
|
|
2043
2011
|
permissionMode: this.#opts.permissionMode,
|
|
@@ -2155,35 +2123,22 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2155
2123
|
}
|
|
2156
2124
|
console.log(`🔧 Tool call ${turnToolCallCount}/${TOOL_CALL_BUDGET}: ${toolName}`);
|
|
2157
2125
|
}
|
|
2158
|
-
//
|
|
2159
|
-
//
|
|
2160
|
-
//
|
|
2161
|
-
if (toolName === 'Task') {
|
|
2162
|
-
const targetType = String(toolInput?.subagent_type || '');
|
|
2163
|
-
const note = coordinationFor(targetType || null, enforcementRoster)?.delegationNote;
|
|
2164
|
-
if (note) {
|
|
2165
|
-
this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: agentType || 'main' });
|
|
2166
|
-
console.log(`🤝 Delegation note → subagent_type=${targetType}`);
|
|
2167
|
-
return { hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: note } };
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
// Write/Edit/MultiEdit access control — DATA-DRIVEN by each agent's
|
|
2171
|
-
// declarative policy.write (see AgentWritePolicy). Replaces the old
|
|
2172
|
-
// hardcoded per-role branches; DB-backed custom agents get enforced too.
|
|
2126
|
+
// Write/Edit/MultiEdit access control — plain WRITE_RULES lookup
|
|
2127
|
+
// keyed by agent name (see decideWrite). Same behavior the old
|
|
2128
|
+
// hardcoded per-role branches had, now in one visible place.
|
|
2173
2129
|
if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
|
|
2174
2130
|
const filePath = String(toolInput.file_path || '');
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
console.log(`🔎 Write gate: agent=${agentType ?? 'main'} policy=${JSON.stringify(policy)} path="${filePath || '(none)'}" → ${decision}`);
|
|
2131
|
+
const { decision, reason } = decideWrite(agentType, filePath);
|
|
2132
|
+
console.log(`🔎 Write gate: agent=${agentType ?? 'main'} path="${filePath || '(none)'}" → ${decision}`);
|
|
2178
2133
|
if (decision === 'deny') {
|
|
2179
2134
|
this.#eventEmitter.emit('tool_blocked', { name: toolName, reason });
|
|
2180
2135
|
return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny' }, reason };
|
|
2181
2136
|
}
|
|
2182
2137
|
if (decision === 'defer') {
|
|
2183
|
-
// Writer (
|
|
2184
|
-
//
|
|
2185
|
-
//
|
|
2186
|
-
if (
|
|
2138
|
+
// Writer ('anywhere') surfaces its write in the live trace before
|
|
2139
|
+
// the permission dialog; adversarial agents (reviewer/tester) do
|
|
2140
|
+
// NOT emit here — parity with the prior hardcoded branches.
|
|
2141
|
+
if (agentType === 'writer') {
|
|
2187
2142
|
this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: agentType || 'main' });
|
|
2188
2143
|
}
|
|
2189
2144
|
return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'ask' } };
|
|
@@ -2531,17 +2486,32 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2531
2486
|
return {};
|
|
2532
2487
|
}]
|
|
2533
2488
|
}],
|
|
2489
|
+
// Idle-agent guard / end-of-run self-review. When the main agent tries
|
|
2490
|
+
// to end its turn while background work is still in flight, block ONCE
|
|
2491
|
+
// (stop_hook_active guards against a loop) and force one more turn so it
|
|
2492
|
+
// either waits + synthesizes, or explicitly tells the user what's still
|
|
2493
|
+
// running — never goes silent leaving dispatched work dangling.
|
|
2494
|
+
Stop: [{
|
|
2495
|
+
matcher: '.*',
|
|
2496
|
+
hooks: [async (input) => {
|
|
2497
|
+
const inFlight = Array.isArray(input?.background_tasks) ? input.background_tasks : [];
|
|
2498
|
+
if (input?.stop_hook_active || inFlight.length === 0)
|
|
2499
|
+
return {};
|
|
2500
|
+
const labels = inFlight
|
|
2501
|
+
.map((t) => t?.agent_type || t?.name || t?.command || t?.type || 'task')
|
|
2502
|
+
.slice(0, 6);
|
|
2503
|
+
console.log(`🛑 Stop gate: ${inFlight.length} background task(s) in flight → blocking once for self-review`);
|
|
2504
|
+
return {
|
|
2505
|
+
decision: 'block',
|
|
2506
|
+
reason: `Before you end your turn: ${inFlight.length} background task(s) are still running (${labels.join(', ')}). Do NOT go idle. Either wait for them and synthesize their results into your answer, or explicitly tell the user what is still running and what you will do when it finishes.`,
|
|
2507
|
+
};
|
|
2508
|
+
}]
|
|
2509
|
+
}],
|
|
2534
2510
|
SubagentStart: [{
|
|
2535
2511
|
matcher: '.*',
|
|
2536
2512
|
hooks: [async (input) => {
|
|
2537
2513
|
console.log('[LIFECYCLE-PROBE] SubagentStart', JSON.stringify(input));
|
|
2538
2514
|
this.#eventEmitter.emit('agent_started', { agent_type: input?.agent_type, agent_id: input?.agent_id });
|
|
2539
|
-
// Inject the agent's declarative startNote (who it is paired with) —
|
|
2540
|
-
// reliable, always-seen at boot regardless of what the hub relayed.
|
|
2541
|
-
const startNote = coordinationFor(input?.agent_type ?? null, enforcementRoster)?.startNote;
|
|
2542
|
-
if (startNote) {
|
|
2543
|
-
return { hookSpecificOutput: { hookEventName: 'SubagentStart', additionalContext: startNote } };
|
|
2544
|
-
}
|
|
2545
2515
|
return {};
|
|
2546
2516
|
}]
|
|
2547
2517
|
}],
|
|
@@ -2555,14 +2525,13 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2555
2525
|
statusManager.upsertDispatch(aid, { subagentType: at, dispatchState: 'completed', artifact: msg });
|
|
2556
2526
|
this.#eventEmitter.emit('task_completed', { agent_type: at, agent_id: aid, last_assistant_message: String(msg).slice(0, 400) });
|
|
2557
2527
|
// Infinite-loop guard — verifiers never re-dispatch (they carry no
|
|
2558
|
-
//
|
|
2559
|
-
// DB-backed agent accidentally arming a loop).
|
|
2528
|
+
// VERIFIER_CHAIN entry anyway; defense-in-depth).
|
|
2560
2529
|
if (at === 'reviewer' || at === 'tester' || at === 'reasoner')
|
|
2561
2530
|
return {};
|
|
2562
|
-
//
|
|
2563
|
-
//
|
|
2564
|
-
const
|
|
2565
|
-
if (
|
|
2531
|
+
// Verifier chaining — driven by the plain VERIFIER_CHAIN lookup
|
|
2532
|
+
// (replaces the hardcoded writer/researcher branches).
|
|
2533
|
+
const chain = at ? VERIFIER_CHAIN[at] : undefined;
|
|
2534
|
+
if (chain?.then?.length && msg) {
|
|
2566
2535
|
const spawn = (role) => {
|
|
2567
2536
|
if (role === 'reviewer')
|
|
2568
2537
|
return this.#llmRef.spawnReviewer(aid, msg, this.#eventEmitter);
|
|
@@ -2570,16 +2539,16 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2570
2539
|
return this.#llmRef.spawnTester(aid, msg, this.#eventEmitter);
|
|
2571
2540
|
if (role === 'reasoner' || role === 'gate')
|
|
2572
2541
|
return this.#llmRef.spawnResearchGate(aid, msg, this.#eventEmitter);
|
|
2573
|
-
console.warn(`[DISPATCH] unknown
|
|
2542
|
+
console.warn(`[DISPATCH] unknown chain target '${role}' for ${at} — skipped`);
|
|
2574
2543
|
return Promise.resolve();
|
|
2575
2544
|
};
|
|
2576
|
-
if (
|
|
2545
|
+
if (chain.mode === 'sequential') {
|
|
2577
2546
|
// Await in order WITHOUT blocking the hook return (fire the chain async).
|
|
2578
|
-
void (async () => { for (const r of
|
|
2547
|
+
void (async () => { for (const r of chain.then)
|
|
2579
2548
|
await spawn(r); })();
|
|
2580
2549
|
}
|
|
2581
2550
|
else {
|
|
2582
|
-
for (const r of
|
|
2551
|
+
for (const r of chain.then)
|
|
2583
2552
|
void spawn(r);
|
|
2584
2553
|
}
|
|
2585
2554
|
}
|
|
@@ -2608,9 +2577,9 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2608
2577
|
// opts.agents is undefined — the ?? NAMED_AGENTS fallback would skip
|
|
2609
2578
|
// the override entirely. Explicitly apply applyTurbo(NAMED_AGENTS)
|
|
2610
2579
|
// so built-in agents always get FAST_MODEL when turbo is on.
|
|
2611
|
-
agents:
|
|
2580
|
+
agents: applyGrounding(this.#llmRef.turbo
|
|
2612
2581
|
? applyTurbo(this.#opts.agents ?? NAMED_AGENTS, true)
|
|
2613
|
-
: (this.#opts.agents ?? NAMED_AGENTS), this.#sessionId, this.#opts.workingDirectory)
|
|
2582
|
+
: (this.#opts.agents ?? NAMED_AGENTS), this.#sessionId, this.#opts.workingDirectory),
|
|
2614
2583
|
};
|
|
2615
2584
|
// Run Claude Agent SDK query() and stream results
|
|
2616
2585
|
let hasOutput = false;
|