osborn 0.9.131 → 0.9.132

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.
@@ -1132,7 +1132,7 @@ class ClaudeLLMStream extends llm.LLMStream {
1132
1132
  // Writer agent: no longer auto-approved — falls through to canUseTool for permission dialog
1133
1133
  if (agentType === 'writer') {
1134
1134
  console.log(`✍️ Writer agent: deferring to canUseTool for permission`);
1135
- this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput });
1135
+ this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: agentType || 'main' });
1136
1136
  return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'ask' } };
1137
1137
  }
1138
1138
  // All other agents (main, researcher, reasoner, etc.): workspace only
@@ -1144,7 +1144,7 @@ class ClaudeLLMStream extends llm.LLMStream {
1144
1144
  }
1145
1145
  }
1146
1146
  console.log(`🔧 Claude: ${toolName}`);
1147
- this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput });
1147
+ this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: agentType || 'main' });
1148
1148
  return {};
1149
1149
  }]
1150
1150
  }],
@@ -1154,8 +1154,9 @@ class ClaudeLLMStream extends llm.LLMStream {
1154
1154
  const toolName = input?.tool_name || 'unknown';
1155
1155
  const toolInput = input?.tool_input || {};
1156
1156
  const toolResponse = input?.tool_response; // Capture actual tool output for fast brain processing
1157
+ const agentTypePost = input?.agent_type || null;
1157
1158
  console.log(`✅ Done: ${toolName}`);
1158
- this.#eventEmitter.emit('tool_result', { name: toolName, input: toolInput, response: toolResponse });
1159
+ this.#eventEmitter.emit('tool_result', { name: toolName, input: toolInput, response: toolResponse, agentRole: agentTypePost || 'main' });
1159
1160
  return {};
1160
1161
  }]
1161
1162
  }],
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { randomUUID } from 'node:crypto';
18
18
  import { createRequire } from 'node:module';
19
19
  // 0.9.71: createRequire for resolving package.json versions inside ESM
20
20
  const __sdkVersionRequire = createRequire(import.meta.url);
21
- import { homedir, tmpdir } from 'node:os';
21
+ import { homedir, tmpdir, totalmem, freemem } from 'node:os';
22
22
  import { PassThrough } from 'node:stream';
23
23
  import { createGunzip } from 'node:zlib';
24
24
  // Resolve __dirname for this ESM module so we can find sibling files (e.g.
@@ -518,6 +518,15 @@ function startApiServer(workingDir, port) {
518
518
  }
519
519
  }
520
520
  catch { /* version optional */ }
521
+ // System memory snapshot — use os.totalmem/freemem for WHOLE-MACHINE
522
+ // stats (the OOM killer watches these, not per-process RSS). processRssMb
523
+ // is additive: useful for spotting leaks but never the headline number.
524
+ const totalMb = Math.round(totalmem() / 1024 / 1024);
525
+ const freeMb = Math.round(freemem() / 1024 / 1024);
526
+ const usedMb = totalMb - freeMb;
527
+ const availableMb = freeMb;
528
+ const usedPct = Math.round((usedMb / totalMb) * 100);
529
+ const processRssMb = Math.round(process.memoryUsage().rss / 1024 / 1024);
521
530
  res.writeHead(200, { 'Content-Type': 'application/json' });
522
531
  res.end(JSON.stringify({
523
532
  status: 'ok',
@@ -538,6 +547,8 @@ function startApiServer(workingDir, port) {
538
547
  attemptCount: livekitState.attemptCount,
539
548
  lastAttemptAt: livekitState.lastAttemptAt,
540
549
  },
550
+ // Whole-machine memory — what the OOM killer watches. All values in MB.
551
+ memory: { totalMb, usedMb, availableMb, usedPct, processRssMb },
541
552
  }));
542
553
  return;
543
554
  }
@@ -2214,7 +2225,7 @@ async function main() {
2214
2225
  const { readSessionHistory } = await import('./session-access.js');
2215
2226
  const history = readSessionHistory(sessionId, workingDir, {
2216
2227
  lastN: 10,
2217
- types: ['assistant', 'tool_use'], // tool_use entries also carry Claude's spoken text
2228
+ types: ['assistant'],
2218
2229
  });
2219
2230
  recentMessages = history
2220
2231
  .filter((m) => m.text)
@@ -2230,14 +2241,8 @@ async function main() {
2230
2241
  // BEFORE the previous TTS completed, and we may have already suppressed in-flight
2231
2242
  // tts_say events that arrived during that overlap).
2232
2243
  const carriedSuppressed = lastInterruption?.suppressedText ?? '';
2233
- lastInterruption = {
2234
- spokenText: fullText,
2235
- recentMessages,
2236
- suppressedText: carriedSuppressed,
2237
- fullBlockText: fullBlockText && fullBlockText !== fullText ? fullBlockText : undefined,
2238
- timestamp: Date.now(),
2239
- };
2240
- console.log(`📋 Interruption context stored (text: ${fullText.length} chars, JSONL: ${recentMessages.length} chars, block: ${fullBlockText?.length ?? 0} chars, suppressed carried: ${carriedSuppressed.length} chars)`);
2244
+ lastInterruption = { spokenText: fullText, recentMessages, suppressedText: carriedSuppressed, timestamp: Date.now() };
2245
+ console.log(`📋 Interruption context stored (text: ${fullText.length} chars, JSONL: ${recentMessages.length} chars, suppressed carried: ${carriedSuppressed.length} chars)`);
2241
2246
  }
2242
2247
  /**
2243
2248
  * Append text the agent tried to say while the user was speaking, but which we
@@ -2277,7 +2282,6 @@ async function main() {
2277
2282
  spokenText: lastInterruption.spokenText,
2278
2283
  recentMessages: lastInterruption.recentMessages,
2279
2284
  suppressedText: lastInterruption.suppressedText,
2280
- fullBlockText: lastInterruption.fullBlockText,
2281
2285
  };
2282
2286
  lastInterruption = null;
2283
2287
  return ctx;
@@ -2689,11 +2693,11 @@ async function main() {
2689
2693
  // Wire up events from the Claude SDK wrapper to frontend
2690
2694
  directLLM.events.on('tool_use', (data) => {
2691
2695
  console.log(`🔧 Claude: ${data.name}`);
2692
- sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', 'direct'));
2696
+ sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', data.agentRole || 'main'));
2693
2697
  });
2694
2698
  directLLM.events.on('tool_result', (data) => {
2695
2699
  console.log(`✅ Done: ${data.name}`);
2696
- sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', 'direct'));
2700
+ sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', data.agentRole || 'main'));
2697
2701
  // Detect research artifact writes (session workspace or legacy research dir)
2698
2702
  if ((data.name === 'Write' || data.name === 'Edit') && data.input?.file_path) {
2699
2703
  const fp = data.input.file_path;
@@ -3128,11 +3132,11 @@ async function main() {
3128
3132
  // Wire up Claude events to frontend
3129
3133
  realtimeClaudeHandler.events.on('tool_use', (data) => {
3130
3134
  console.log(`🔧 Claude: ${data.name}`);
3131
- sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', 'realtime'));
3135
+ sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', data.agentRole || 'main'));
3132
3136
  });
3133
3137
  realtimeClaudeHandler.events.on('tool_result', (data) => {
3134
3138
  console.log(`✅ Done: ${data.name}`);
3135
- sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', 'realtime'));
3139
+ sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', data.agentRole || 'main'));
3136
3140
  // Detect research artifact writes (session workspace or legacy research dir)
3137
3141
  if ((data.name === 'Write' || data.name === 'Edit') && data.input?.file_path) {
3138
3142
  const fp = data.input.file_path;
@@ -21,8 +21,6 @@ export interface InterruptionContext {
21
21
  * and can re-articulate the relevant bits in its next response.
22
22
  */
23
23
  suppressedText: string;
24
- /** Full text of the interrupted TTS block — fallback when JSONL read returns empty */
25
- fullBlockText?: string;
26
24
  }
27
25
  export interface PipelineDirectOptions extends ClaudeLLMOptions {
28
26
  onFastBrainResult?: (result: FastBrainPanelResult) => void;
@@ -122,10 +122,7 @@ export class PipelineDirectLLM extends llm.LLM {
122
122
  `Anything in "Your recent messages" below that appears AFTER the quoted heard text is content the user did not hear. The user has no memory of it.`,
123
123
  ``,
124
124
  `Your recent messages (full untruncated — you wrote these):`,
125
- interruptCtx.recentMessages
126
- || (interruptCtx.fullBlockText
127
- ? `(JSONL unavailable — full TTS block text follows)\n"${interruptCtx.fullBlockText}"`
128
- : '(no recent messages found)'),
125
+ interruptCtx.recentMessages || '(no recent messages found)',
129
126
  suppressedBlock,
130
127
  ``,
131
128
  `User's message: "${userText}"`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.131",
3
+ "version": "0.9.132",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {