osborn 0.9.237 → 0.9.239
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 +8 -0
- package/dist/claude-llm.js +57 -21
- package/dist/env-keys.js +1 -0
- package/package.json +1 -1
package/dist/claude-llm.d.ts
CHANGED
|
@@ -120,6 +120,7 @@ export declare function decideWrite(agentType: string | null, filePath: string):
|
|
|
120
120
|
decision: 'allow' | 'defer' | 'deny';
|
|
121
121
|
reason?: string;
|
|
122
122
|
};
|
|
123
|
+
export declare const SILENT_VERIFY_PREFIX: string;
|
|
123
124
|
export declare const VERIFIER_CHAIN: Record<string, {
|
|
124
125
|
parallelAtStart?: string[];
|
|
125
126
|
afterReminder?: string;
|
|
@@ -278,6 +279,13 @@ export declare class ClaudeLLM extends llm.LLM {
|
|
|
278
279
|
stopAgent(agentId: string): boolean;
|
|
279
280
|
/** Whether a persistent session is alive and consuming messages */
|
|
280
281
|
hasSession(): boolean;
|
|
282
|
+
/**
|
|
283
|
+
* Verifier-chain dedup: records that agent_id has triggered its chain dispatch and returns
|
|
284
|
+
* true only the FIRST time (so a double-fired SubagentStop can't double-dispatch). A NEW
|
|
285
|
+
* agent_id (e.g. a fresh writer spawned for a fix round) is not in the set → returns true,
|
|
286
|
+
* correctly allowing the re-verify loop. Cleared when the persistent session closes.
|
|
287
|
+
*/
|
|
288
|
+
markChainDispatched(agentId: string): boolean;
|
|
281
289
|
/**
|
|
282
290
|
* Close the persistent session (kills subprocess).
|
|
283
291
|
* Call on disconnect, session switch, or recovery.
|
package/dist/claude-llm.js
CHANGED
|
@@ -120,10 +120,17 @@ function extractSpeedMarker(text) {
|
|
|
120
120
|
}
|
|
121
121
|
function stripMarkdownForTTS(text) {
|
|
122
122
|
return text
|
|
123
|
-
// Remove code blocks (``` ... ```)
|
|
123
|
+
// Remove PAIRED code blocks (``` ... ```)
|
|
124
124
|
.replace(/```[\s\S]*?```/g, ' [code block] ')
|
|
125
|
+
// Remove an UNCLOSED/partial fence to end-of-string. Assistant text streams as multiple
|
|
126
|
+
// blocks per turn and each block is stripped in isolation, so a fence that opens in one
|
|
127
|
+
// block (or is never closed) would otherwise leak raw code + literal backticks to TTS.
|
|
128
|
+
.replace(/```[\s\S]*$/g, ' [code block] ')
|
|
125
129
|
// Remove inline code (` ... `)
|
|
126
130
|
.replace(/`([^`]+)`/g, '$1')
|
|
131
|
+
// Sweep any remaining stray backtick runs (unbalanced ticks from split chunks) so they're
|
|
132
|
+
// never spoken aloud.
|
|
133
|
+
.replace(/`+/g, ' ')
|
|
127
134
|
// Remove bold (**text** or __text__)
|
|
128
135
|
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
129
136
|
.replace(/__([^_]+)__/g, '$1')
|
|
@@ -912,6 +919,11 @@ export function decideWrite(agentType, filePath) {
|
|
|
912
919
|
// when the keyed sub-agent's Task completes, so the main agent dispatches the
|
|
913
920
|
// next verifier(s) as real Tasks (verdicts return inline). Includes RUNNING the
|
|
914
921
|
// tester's tests against the finished code. Any agent not listed chains to nothing.
|
|
922
|
+
// Prepended to a verifier-chain reminder pushed into the MAIN agent at SubagentStop. Keeps the
|
|
923
|
+
// dispatch SILENT — the model should emit ONLY the Task tool call(s), no spoken/streamed text —
|
|
924
|
+
// so the chain advances without a robotic "now running the review…" narration on the voice path.
|
|
925
|
+
export const SILENT_VERIFY_PREFIX = '[SYSTEM — SILENT CONTROL MESSAGE. Do NOT narrate, speak, or emit any assistant text this turn. ' +
|
|
926
|
+
'Silently perform the following via the Task tool only.] ';
|
|
915
927
|
export const VERIFIER_CHAIN = {
|
|
916
928
|
writer: {
|
|
917
929
|
parallelAtStart: ['tester'],
|
|
@@ -1473,6 +1485,18 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1473
1485
|
hasSession() {
|
|
1474
1486
|
return this.#persistentQuery !== null && !this.#messageChannel?.closed;
|
|
1475
1487
|
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Verifier-chain dedup: records that agent_id has triggered its chain dispatch and returns
|
|
1490
|
+
* true only the FIRST time (so a double-fired SubagentStop can't double-dispatch). A NEW
|
|
1491
|
+
* agent_id (e.g. a fresh writer spawned for a fix round) is not in the set → returns true,
|
|
1492
|
+
* correctly allowing the re-verify loop. Cleared when the persistent session closes.
|
|
1493
|
+
*/
|
|
1494
|
+
markChainDispatched(agentId) {
|
|
1495
|
+
if (!agentId || this.#dispatchedFor.has(agentId))
|
|
1496
|
+
return false;
|
|
1497
|
+
this.#dispatchedFor.add(agentId);
|
|
1498
|
+
return true;
|
|
1499
|
+
}
|
|
1476
1500
|
/**
|
|
1477
1501
|
* Close the persistent session (kills subprocess).
|
|
1478
1502
|
* Call on disconnect, session switch, or recovery.
|
|
@@ -1948,23 +1972,12 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1948
1972
|
console.log('[LIFECYCLE-PROBE] PostToolUse on', toolName, 'agent_id=', agentId, 'ts=', Date.now());
|
|
1949
1973
|
}
|
|
1950
1974
|
this.#eventEmitter.emit('tool_result', { name: toolName, input: toolInput, response: toolResponse, agentRole: agentTypePost || 'main' });
|
|
1951
|
-
// Pattern #1 verifier chaining
|
|
1952
|
-
//
|
|
1953
|
-
//
|
|
1954
|
-
//
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
const link = subType ? VERIFIER_CHAIN[subType] : undefined;
|
|
1958
|
-
if (link?.afterReminder) {
|
|
1959
|
-
console.log(`[DISPATCH] Pattern#1 chain: ${subType} done → reminding main agent to verify`);
|
|
1960
|
-
return {
|
|
1961
|
-
hookSpecificOutput: {
|
|
1962
|
-
hookEventName: 'PostToolUse',
|
|
1963
|
-
additionalContext: link.afterReminder,
|
|
1964
|
-
},
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1975
|
+
// Pattern #1 verifier chaining has MOVED to the SubagentStop hook below. Reason:
|
|
1976
|
+
// sub-agents run with run_in_background:true, so PostToolUse-on-Task fires at
|
|
1977
|
+
// DISPATCH (placeholder tool_response), not completion — injecting the "now verify"
|
|
1978
|
+
// reminder here fired while the writer/researcher was still running (mistimed).
|
|
1979
|
+
// SubagentStop is the true completion signal; it pushes the reminder into the main
|
|
1980
|
+
// agent via pushMessage.
|
|
1968
1981
|
return {};
|
|
1969
1982
|
}]
|
|
1970
1983
|
}],
|
|
@@ -2320,9 +2333,32 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2320
2333
|
const aid = input?.agent_id ?? ('sa-' + Date.now());
|
|
2321
2334
|
statusManager.upsertDispatch(aid, { subagentType: at, dispatchState: 'completed', artifact: msg });
|
|
2322
2335
|
this.#eventEmitter.emit('task_completed', { agent_type: at, agent_id: aid, last_assistant_message: String(msg).slice(0, 400) });
|
|
2323
|
-
//
|
|
2324
|
-
//
|
|
2325
|
-
//
|
|
2336
|
+
// Pattern #1 verifier chaining — CORRECT timing (moved here from PostToolUse).
|
|
2337
|
+
// SubagentStop fires at REAL completion. When a chained producer (writer/researcher)
|
|
2338
|
+
// finishes, push its afterReminder into the MAIN agent's streaming input so the main
|
|
2339
|
+
// agent dispatches the next verifier(s) as real Task sub-agents (verdicts return
|
|
2340
|
+
// inline). Only writer/researcher are VERIFIER_CHAIN keys with afterReminder, so
|
|
2341
|
+
// verifiers (reviewer/tester/reasoner) never re-chain. Dedup on agent_id guards a
|
|
2342
|
+
// double-fired SubagentStop; a fresh fix-round writer has a NEW id → re-chains (the
|
|
2343
|
+
// intended bounded loop). Guarded on a live session so we never cold-start a query
|
|
2344
|
+
// just to deliver a reminder. Silent-dispatch prefix keeps it off the voice.
|
|
2345
|
+
const chainLink = at ? VERIFIER_CHAIN[at] : undefined;
|
|
2346
|
+
// Dedup MUST key on the REAL agent_id, never the fabricated 'sa-<ts>' fallback used
|
|
2347
|
+
// above for status/emit — a synthetic id is unique every call and would defeat the
|
|
2348
|
+
// double-fire guard. If the SDK omits agent_id we can't dedup safely, so skip chaining.
|
|
2349
|
+
if (chainLink?.afterReminder && input?.agent_id && this.#llmRef.hasSession() && this.#llmRef.markChainDispatched(input.agent_id)) {
|
|
2350
|
+
console.log(`[DISPATCH] Pattern#1 chain (SubagentStop): ${at} done → pushing verify reminder to main agent`);
|
|
2351
|
+
try {
|
|
2352
|
+
this.#llmRef.pushMessage(SILENT_VERIFY_PREFIX + chainLink.afterReminder, sdkOptions, {
|
|
2353
|
+
onSessionId: this.#onSessionId,
|
|
2354
|
+
onCheckpoint: this.#onCheckpoint,
|
|
2355
|
+
eventEmitter: this.#eventEmitter,
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
catch (e) {
|
|
2359
|
+
console.warn('Pattern#1 chain push failed:', e);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2326
2362
|
return {};
|
|
2327
2363
|
}]
|
|
2328
2364
|
}],
|
package/dist/env-keys.js
CHANGED
|
@@ -35,6 +35,7 @@ export const AGENT_ENV_KEYS = [
|
|
|
35
35
|
{ key: 'OSBORN_CWD', required: false, explicit: true, description: 'Agent working directory.' },
|
|
36
36
|
{ key: 'OSBORN_SYNC_TOKEN', required: false, explicit: true, description: 'Session export/import auth token.' },
|
|
37
37
|
{ key: 'OSBORN_FRONTEND_URL', required: false, explicit: true, description: 'Frontend base URL for artifact upload.' },
|
|
38
|
+
{ key: 'OSBORN_HOUSEKEEPING_DRYRUN', required: false, explicit: true, description: 'Set "0" to enable live housekeeping deletion; any other value / unset = dry-run (log only).' },
|
|
38
39
|
{ key: 'DEV_DOMAIN', required: false, explicit: true, description: 'Wildcard dev-routing domain.' },
|
|
39
40
|
];
|
|
40
41
|
/** Keys the agent treats as required — absence should be surfaced at startup. */
|