osborn 0.9.145 → 0.9.147
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 +6 -0
- package/dist/claude-llm.js +61 -2
- package/dist/index.js +17 -0
- package/dist/pipeline-direct-llm.d.ts +1 -0
- package/dist/pipeline-direct-llm.js +1 -0
- package/package.json +1 -1
package/dist/claude-llm.d.ts
CHANGED
|
@@ -210,6 +210,12 @@ export declare class ClaudeLLM extends llm.LLM {
|
|
|
210
210
|
setActiveQuery(q: any): void;
|
|
211
211
|
/** Remove an active query (called from ClaudeLLMStream when query completes) */
|
|
212
212
|
removeActiveQuery(q: any): void;
|
|
213
|
+
/**
|
|
214
|
+
* Stop a single dispatch flow by agent_id.
|
|
215
|
+
* Returns true if the agent was found and stopped; false if not found.
|
|
216
|
+
* Does NOT affect other active queries (use abortQuery() to kill all).
|
|
217
|
+
*/
|
|
218
|
+
stopAgent(agentId: string): boolean;
|
|
213
219
|
/** Whether a persistent session is alive and consuming messages */
|
|
214
220
|
hasSession(): boolean;
|
|
215
221
|
/**
|
package/dist/claude-llm.js
CHANGED
|
@@ -12,6 +12,7 @@ import { EventEmitter } from 'events';
|
|
|
12
12
|
import { saveSessionMetadata, getSessionWorkspace } from './config.js';
|
|
13
13
|
import { statusManager } from './status-manager.js';
|
|
14
14
|
import { getResearchSystemPrompt, getDirectModeResearchPrompt } from './prompts.js';
|
|
15
|
+
import { getIndexPath } from './summary-index.js';
|
|
15
16
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
16
17
|
import { join, dirname } from 'node:path';
|
|
17
18
|
import { fileURLToPath } from 'node:url';
|
|
@@ -555,6 +556,9 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
555
556
|
// Active queries — multiple can be running (SDK queues them internally).
|
|
556
557
|
// We keep ALL references so interrupt() can stop whatever is currently executing.
|
|
557
558
|
#activeQueries = new Set();
|
|
559
|
+
// Per-agent-id query map — allows targeted stop of a single dispatch flow.
|
|
560
|
+
// Strictly additive; abortQuery/interruptQuery still iterate #activeQueries (kill-all).
|
|
561
|
+
#activeQueriesById = new Map();
|
|
558
562
|
// Dedup guard — prevents double-firing reviewer/gate if SubagentStop fires
|
|
559
563
|
// more than once for the same agent_id (e.g. retry edge cases).
|
|
560
564
|
#dispatchedFor = new Set();
|
|
@@ -888,6 +892,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
888
892
|
catch { }
|
|
889
893
|
}
|
|
890
894
|
this.#activeQueries.clear();
|
|
895
|
+
this.#activeQueriesById.clear();
|
|
891
896
|
console.log('🛑 All queries aborted (Ctrl+C equivalent)');
|
|
892
897
|
}
|
|
893
898
|
/**
|
|
@@ -942,6 +947,23 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
942
947
|
removeActiveQuery(q) {
|
|
943
948
|
this.#activeQueries.delete(q);
|
|
944
949
|
}
|
|
950
|
+
/**
|
|
951
|
+
* Stop a single dispatch flow by agent_id.
|
|
952
|
+
* Returns true if the agent was found and stopped; false if not found.
|
|
953
|
+
* Does NOT affect other active queries (use abortQuery() to kill all).
|
|
954
|
+
*/
|
|
955
|
+
stopAgent(agentId) {
|
|
956
|
+
const q = this.#activeQueriesById.get(agentId);
|
|
957
|
+
if (!q)
|
|
958
|
+
return false;
|
|
959
|
+
try {
|
|
960
|
+
q.return?.();
|
|
961
|
+
}
|
|
962
|
+
catch { }
|
|
963
|
+
this.#activeQueriesById.delete(agentId);
|
|
964
|
+
this.#activeQueries.delete(q);
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
945
967
|
// ============================================================
|
|
946
968
|
// PERSISTENT SESSION — V1 query() with AsyncIterable<SDKUserMessage>
|
|
947
969
|
// Single subprocess per voice session. First chat() does JSONL cold
|
|
@@ -1133,9 +1155,13 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1133
1155
|
return;
|
|
1134
1156
|
this.#dispatchedFor.add(agentId);
|
|
1135
1157
|
try {
|
|
1158
|
+
const idxPathReviewer = (this.#sessionId && this.#opts.workingDirectory)
|
|
1159
|
+
? getIndexPath(this.#sessionId, this.#opts.workingDirectory)
|
|
1160
|
+
: null;
|
|
1136
1161
|
const prompt = [
|
|
1137
1162
|
'Use the reviewer sub-agent to review this writer output for correctness/spec-adherence/obvious bugs.',
|
|
1138
1163
|
'End your reply with exactly `VERDICT: ACCEPT` or `VERDICT: REJECT`.',
|
|
1164
|
+
...(idxPathReviewer ? [`The session index is at ${idxPathReviewer} — you MUST read it before reviewing.`] : []),
|
|
1139
1165
|
'',
|
|
1140
1166
|
'<writer_output>',
|
|
1141
1167
|
writerOutput.slice(0, 8000),
|
|
@@ -1148,8 +1174,11 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1148
1174
|
const reviewerOptions = {
|
|
1149
1175
|
cwd: this.#opts.workingDirectory,
|
|
1150
1176
|
permissionMode: 'default',
|
|
1177
|
+
systemPrompt: NAMED_AGENTS.reviewer.prompt,
|
|
1178
|
+
allowedTools: ['Read', 'Glob', 'Grep', 'Bash', 'Write', 'Edit'],
|
|
1151
1179
|
hooks: {
|
|
1152
|
-
PreToolUse: [
|
|
1180
|
+
PreToolUse: [
|
|
1181
|
+
{
|
|
1153
1182
|
matcher: '.*',
|
|
1154
1183
|
hooks: [async (input) => {
|
|
1155
1184
|
const toolName = input?.tool_name || 'unknown';
|
|
@@ -1157,7 +1186,26 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1157
1186
|
emitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: 'reviewer' });
|
|
1158
1187
|
return {};
|
|
1159
1188
|
}],
|
|
1160
|
-
}
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
matcher: '.*',
|
|
1192
|
+
hooks: [async (input) => {
|
|
1193
|
+
const toolName = input?.tool_name || '';
|
|
1194
|
+
const toolInput = input?.tool_input;
|
|
1195
|
+
if ((toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') &&
|
|
1196
|
+
!/\.(md|markdown|mdx|txt|rst|adoc)$/i.test(String(toolInput?.file_path))) {
|
|
1197
|
+
return {
|
|
1198
|
+
hookSpecificOutput: {
|
|
1199
|
+
hookEventName: 'PreToolUse',
|
|
1200
|
+
permissionDecision: 'deny',
|
|
1201
|
+
},
|
|
1202
|
+
reason: 'reviewer may only write documentation files (.md/.markdown/.mdx/.txt/.rst/.adoc)',
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
return {};
|
|
1206
|
+
}],
|
|
1207
|
+
},
|
|
1208
|
+
],
|
|
1161
1209
|
PostToolUse: [{
|
|
1162
1210
|
matcher: '.*',
|
|
1163
1211
|
hooks: [async (input) => {
|
|
@@ -1173,6 +1221,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1173
1221
|
console.log(`[DISPATCH] spawning reviewer for agentId=${agentId.slice(0, 8)}`);
|
|
1174
1222
|
const reviewerQuery = query({ prompt, options: reviewerOptions });
|
|
1175
1223
|
this.#activeQueries.add(reviewerQuery);
|
|
1224
|
+
this.#activeQueriesById.set(agentId, reviewerQuery);
|
|
1176
1225
|
let reviewerText = '';
|
|
1177
1226
|
try {
|
|
1178
1227
|
for await (const msg of reviewerQuery) {
|
|
@@ -1184,6 +1233,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1184
1233
|
}
|
|
1185
1234
|
finally {
|
|
1186
1235
|
this.#activeQueries.delete(reviewerQuery);
|
|
1236
|
+
this.#activeQueriesById.delete(agentId);
|
|
1187
1237
|
}
|
|
1188
1238
|
const verdictMatch = reviewerText.match(/VERDICT:\s*(ACCEPT|REJECT)/i);
|
|
1189
1239
|
const verdict = verdictMatch ? verdictMatch[1].toUpperCase() : null;
|
|
@@ -1211,10 +1261,14 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1211
1261
|
return;
|
|
1212
1262
|
this.#dispatchedFor.add(agentId);
|
|
1213
1263
|
try {
|
|
1264
|
+
const idxPathGate = (this.#sessionId && this.#opts.workingDirectory)
|
|
1265
|
+
? getIndexPath(this.#sessionId, this.#opts.workingDirectory)
|
|
1266
|
+
: null;
|
|
1214
1267
|
const prompt = [
|
|
1215
1268
|
'Use the reasoner sub-agent to vet this research output.',
|
|
1216
1269
|
'Determine whether the research is sufficient to answer the original question.',
|
|
1217
1270
|
'End your reply with exactly `GATE: PASS` or `GATE: NEEDS-MORE`.',
|
|
1271
|
+
...(idxPathGate ? [`The session index is at ${idxPathGate} — you MUST read it before reviewing.`] : []),
|
|
1218
1272
|
'',
|
|
1219
1273
|
'<research_output>',
|
|
1220
1274
|
researchOutput.slice(0, 8000),
|
|
@@ -1227,6 +1281,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1227
1281
|
const gateOptions = {
|
|
1228
1282
|
cwd: this.#opts.workingDirectory,
|
|
1229
1283
|
permissionMode: 'default',
|
|
1284
|
+
systemPrompt: NAMED_AGENTS.reasoner.prompt,
|
|
1230
1285
|
hooks: {
|
|
1231
1286
|
PreToolUse: [{
|
|
1232
1287
|
matcher: '.*',
|
|
@@ -1252,6 +1307,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1252
1307
|
console.log(`[DISPATCH] spawning research-gate for agentId=${agentId.slice(0, 8)}`);
|
|
1253
1308
|
const gateQuery = query({ prompt, options: gateOptions });
|
|
1254
1309
|
this.#activeQueries.add(gateQuery);
|
|
1310
|
+
this.#activeQueriesById.set(agentId, gateQuery);
|
|
1255
1311
|
let review = '';
|
|
1256
1312
|
try {
|
|
1257
1313
|
for await (const msg of gateQuery) {
|
|
@@ -1263,6 +1319,7 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1263
1319
|
}
|
|
1264
1320
|
finally {
|
|
1265
1321
|
this.#activeQueries.delete(gateQuery);
|
|
1322
|
+
this.#activeQueriesById.delete(agentId);
|
|
1266
1323
|
}
|
|
1267
1324
|
const gateMatch = review.match(/GATE:\s*(PASS|NEEDS-MORE)/i);
|
|
1268
1325
|
const gateVerdict = gateMatch ? gateMatch[1].toUpperCase() : null;
|
|
@@ -1749,6 +1806,7 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1749
1806
|
matcher: '.*',
|
|
1750
1807
|
hooks: [async (input) => {
|
|
1751
1808
|
console.log('[LIFECYCLE-PROBE] SubagentStart', JSON.stringify(input));
|
|
1809
|
+
this.#eventEmitter.emit('agent_started', { agent_type: input?.agent_type, agent_id: input?.agent_id });
|
|
1752
1810
|
return {};
|
|
1753
1811
|
}]
|
|
1754
1812
|
}],
|
|
@@ -1760,6 +1818,7 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1760
1818
|
const msg = String(input?.last_assistant_message ?? '');
|
|
1761
1819
|
const aid = input?.agent_id ?? ('sa-' + Date.now());
|
|
1762
1820
|
statusManager.upsertDispatch(aid, { subagentType: at, dispatchState: 'completed', artifact: msg });
|
|
1821
|
+
this.#eventEmitter.emit('task_completed', { agent_type: at, agent_id: aid, last_assistant_message: String(msg).slice(0, 400) });
|
|
1763
1822
|
// Infinite-loop guard — never re-dispatch the reviewer or reasoner.
|
|
1764
1823
|
if (at === 'reviewer' || at === 'reasoner')
|
|
1765
1824
|
return {};
|
package/dist/index.js
CHANGED
|
@@ -3096,6 +3096,19 @@ async function main() {
|
|
|
3096
3096
|
review: data.review,
|
|
3097
3097
|
});
|
|
3098
3098
|
});
|
|
3099
|
+
// Dispatcher v1 — sub-agent completed → frontend (Feature A)
|
|
3100
|
+
directLLM.events.on('task_completed', (d) => sendToFrontend({
|
|
3101
|
+
type: 'task_completed',
|
|
3102
|
+
agent_type: d.agent_type,
|
|
3103
|
+
agent_id: d.agent_id,
|
|
3104
|
+
last_assistant_message: d.last_assistant_message,
|
|
3105
|
+
}));
|
|
3106
|
+
// Dispatcher v1 — sub-agent started → frontend stop-key signal (Feature A2)
|
|
3107
|
+
directLLM.events.on('agent_started', (d) => sendToFrontend({
|
|
3108
|
+
type: 'agent_started',
|
|
3109
|
+
agent_type: d.agent_type,
|
|
3110
|
+
agent_id: d.agent_id,
|
|
3111
|
+
}));
|
|
3099
3112
|
// Create the Agent with instructions, STT, LLM, TTS
|
|
3100
3113
|
// VAD (Silero ONNX) removed — caused 2-5s inference lag on CPU, making interruption detection worse
|
|
3101
3114
|
// Turn detection is server-side (Deepgram endpointing), interruptions handled by STT
|
|
@@ -5515,6 +5528,10 @@ async function main() {
|
|
|
5515
5528
|
}
|
|
5516
5529
|
}
|
|
5517
5530
|
}
|
|
5531
|
+
// Feature B — per-flow stop (does NOT affect other running flows)
|
|
5532
|
+
else if (data.type === 'stop_dispatch' && currentLLM) {
|
|
5533
|
+
currentLLM.stopAgent?.(String(data.agentId));
|
|
5534
|
+
}
|
|
5518
5535
|
else if (data.type === 'join_meeting') {
|
|
5519
5536
|
const meetingUrl = data.url;
|
|
5520
5537
|
if (meetingUrl) {
|
|
@@ -67,6 +67,7 @@ export declare class PipelineDirectLLM extends llm.LLM {
|
|
|
67
67
|
abortAgent(): void;
|
|
68
68
|
rewindAgent(checkpointId?: string): Promise<boolean>;
|
|
69
69
|
hasActiveAgent(): boolean;
|
|
70
|
+
stopAgent(agentId: string): boolean;
|
|
70
71
|
/** Send a new prompt to Claude via direct chat() — event listeners stay attached */
|
|
71
72
|
sendPrompt(prompt: string): void;
|
|
72
73
|
enableMcpServer(k: string, c: any): void;
|
|
@@ -120,6 +120,7 @@ export class PipelineDirectLLM extends llm.LLM {
|
|
|
120
120
|
abortAgent() { this.#claudeLLM.abortQuery(); }
|
|
121
121
|
async rewindAgent(checkpointId) { return this.#claudeLLM.rewindToCheckpoint(checkpointId); }
|
|
122
122
|
hasActiveAgent() { return this.#claudeLLM.hasActiveQuery(); }
|
|
123
|
+
stopAgent(agentId) { return this.#claudeLLM.stopAgent(agentId); }
|
|
123
124
|
/** Send a new prompt to Claude via direct chat() — event listeners stay attached */
|
|
124
125
|
sendPrompt(prompt) {
|
|
125
126
|
console.log(`📋 [pipeline] Sending prompt to Claude (${prompt.length} chars)`);
|