osborn 0.9.146 → 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.
@@ -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
  /**
@@ -556,6 +556,9 @@ export class ClaudeLLM extends llm.LLM {
556
556
  // Active queries — multiple can be running (SDK queues them internally).
557
557
  // We keep ALL references so interrupt() can stop whatever is currently executing.
558
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();
559
562
  // Dedup guard — prevents double-firing reviewer/gate if SubagentStop fires
560
563
  // more than once for the same agent_id (e.g. retry edge cases).
561
564
  #dispatchedFor = new Set();
@@ -889,6 +892,7 @@ export class ClaudeLLM extends llm.LLM {
889
892
  catch { }
890
893
  }
891
894
  this.#activeQueries.clear();
895
+ this.#activeQueriesById.clear();
892
896
  console.log('🛑 All queries aborted (Ctrl+C equivalent)');
893
897
  }
894
898
  /**
@@ -943,6 +947,23 @@ export class ClaudeLLM extends llm.LLM {
943
947
  removeActiveQuery(q) {
944
948
  this.#activeQueries.delete(q);
945
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
+ }
946
967
  // ============================================================
947
968
  // PERSISTENT SESSION — V1 query() with AsyncIterable<SDKUserMessage>
948
969
  // Single subprocess per voice session. First chat() does JSONL cold
@@ -1200,6 +1221,7 @@ export class ClaudeLLM extends llm.LLM {
1200
1221
  console.log(`[DISPATCH] spawning reviewer for agentId=${agentId.slice(0, 8)}`);
1201
1222
  const reviewerQuery = query({ prompt, options: reviewerOptions });
1202
1223
  this.#activeQueries.add(reviewerQuery);
1224
+ this.#activeQueriesById.set(agentId, reviewerQuery);
1203
1225
  let reviewerText = '';
1204
1226
  try {
1205
1227
  for await (const msg of reviewerQuery) {
@@ -1211,6 +1233,7 @@ export class ClaudeLLM extends llm.LLM {
1211
1233
  }
1212
1234
  finally {
1213
1235
  this.#activeQueries.delete(reviewerQuery);
1236
+ this.#activeQueriesById.delete(agentId);
1214
1237
  }
1215
1238
  const verdictMatch = reviewerText.match(/VERDICT:\s*(ACCEPT|REJECT)/i);
1216
1239
  const verdict = verdictMatch ? verdictMatch[1].toUpperCase() : null;
@@ -1284,6 +1307,7 @@ export class ClaudeLLM extends llm.LLM {
1284
1307
  console.log(`[DISPATCH] spawning research-gate for agentId=${agentId.slice(0, 8)}`);
1285
1308
  const gateQuery = query({ prompt, options: gateOptions });
1286
1309
  this.#activeQueries.add(gateQuery);
1310
+ this.#activeQueriesById.set(agentId, gateQuery);
1287
1311
  let review = '';
1288
1312
  try {
1289
1313
  for await (const msg of gateQuery) {
@@ -1295,6 +1319,7 @@ export class ClaudeLLM extends llm.LLM {
1295
1319
  }
1296
1320
  finally {
1297
1321
  this.#activeQueries.delete(gateQuery);
1322
+ this.#activeQueriesById.delete(agentId);
1298
1323
  }
1299
1324
  const gateMatch = review.match(/GATE:\s*(PASS|NEEDS-MORE)/i);
1300
1325
  const gateVerdict = gateMatch ? gateMatch[1].toUpperCase() : null;
@@ -1781,6 +1806,7 @@ class ClaudeLLMStream extends llm.LLMStream {
1781
1806
  matcher: '.*',
1782
1807
  hooks: [async (input) => {
1783
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 });
1784
1810
  return {};
1785
1811
  }]
1786
1812
  }],
@@ -1792,6 +1818,7 @@ class ClaudeLLMStream extends llm.LLMStream {
1792
1818
  const msg = String(input?.last_assistant_message ?? '');
1793
1819
  const aid = input?.agent_id ?? ('sa-' + Date.now());
1794
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) });
1795
1822
  // Infinite-loop guard — never re-dispatch the reviewer or reasoner.
1796
1823
  if (at === 'reviewer' || at === 'reasoner')
1797
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)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.146",
3
+ "version": "0.9.147",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {