osborn 0.9.240 → 0.9.242
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.js +10 -0
- package/dist/index.js +28 -0
- package/dist/status-manager.d.ts +12 -0
- package/dist/status-manager.js +19 -0
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -2321,6 +2321,16 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2321
2321
|
hooks: [async (input) => {
|
|
2322
2322
|
console.log('[LIFECYCLE-PROBE] SubagentStart', JSON.stringify(input));
|
|
2323
2323
|
this.#eventEmitter.emit('agent_started', { agent_type: input?.agent_type, agent_id: input?.agent_id });
|
|
2324
|
+
// Record the dispatch as LIVE so the frontend can reconcile orphaned
|
|
2325
|
+
// "running" rows against statusManager.getActiveDispatchIds(). Without a
|
|
2326
|
+
// start-time entry the manager only knew about dispatches at completion,
|
|
2327
|
+
// so a reconcile had no authoritative live set to diff against.
|
|
2328
|
+
if (input?.agent_id) {
|
|
2329
|
+
try {
|
|
2330
|
+
statusManager.upsertDispatch(input.agent_id, { subagentType: input?.agent_type, dispatchState: 'running' });
|
|
2331
|
+
}
|
|
2332
|
+
catch { }
|
|
2333
|
+
}
|
|
2324
2334
|
return {};
|
|
2325
2335
|
}]
|
|
2326
2336
|
}],
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { createSTT, createTTS } from './voice-io.js';
|
|
|
39
39
|
import { createClaudeLLM, NAMED_AGENTS, applyTurbo } from './claude-llm.js';
|
|
40
40
|
import { clearPipelineFastBrainSession, prewarmBM25Index } from './pipeline-fastbrain.js';
|
|
41
41
|
import { ensureClaudeAuth, applyAuthFallback, replayInFlightAuth } from './claude-auth.js';
|
|
42
|
+
import { statusManager } from './status-manager.js';
|
|
42
43
|
import { createSmitheryProxy, destroySmitheryProxy, parseSmitheryUrl, isSmitheryUrl, SmitheryAuthorizationError } from './smithery-proxy.js';
|
|
43
44
|
import { DIRECT_MODE_PROMPT } from './prompts.js';
|
|
44
45
|
import { MCP_CATALOG } from './config.js';
|
|
@@ -2299,6 +2300,16 @@ async function main() {
|
|
|
2299
2300
|
bgEnv[k] = v;
|
|
2300
2301
|
}
|
|
2301
2302
|
bgEnv['CLAUDE_CODE_DISABLE_AUTO_MEMORY'] = '1';
|
|
2303
|
+
// Claude Code refuses bypassPermissions / --dangerously-skip-permissions when the
|
|
2304
|
+
// process runs as root — and every Fly machine runs Osborn as root, so without this
|
|
2305
|
+
// EVERY headless background spawn dies on launch. IS_SANDBOX=1 is Claude Code's
|
|
2306
|
+
// sanctioned "this is an isolated container, root is acceptable" signal — accurate
|
|
2307
|
+
// for a per-user Fly VM. Set on the CHILD's env copy ONLY (bgEnv); the main voice
|
|
2308
|
+
// agent's process.env is never touched. Panel-spawned instances are user-initiated
|
|
2309
|
+
// and intentionally run on the machine's Claude subscription (Tier 1), same as the
|
|
2310
|
+
// foreground session — the "never use the subscription" rule is reserved for the
|
|
2311
|
+
// future autonomous cron/event background primitive, not these.
|
|
2312
|
+
bgEnv['IS_SANDBOX'] = '1';
|
|
2302
2313
|
const bgSdkOptions = {
|
|
2303
2314
|
cwd: slotWorkingDir,
|
|
2304
2315
|
resume: sessionId,
|
|
@@ -5851,6 +5862,17 @@ async function main() {
|
|
|
5851
5862
|
error: `Session ${resolvedSessionId.substring(0, 8)} is already running as a background slot`,
|
|
5852
5863
|
});
|
|
5853
5864
|
}
|
|
5865
|
+
else if (resolvedSessionId === (currentLLM?.sessionId || currentResumeSessionId)) {
|
|
5866
|
+
// Never background the session that's LIVE in the foreground. Pre-IS_SANDBOX this
|
|
5867
|
+
// failed harmlessly on the root check; post-fix it would SUCCEED, putting two
|
|
5868
|
+
// Claude processes appending to the same session JSONL concurrently → transcript
|
|
5869
|
+
// corruption. Refuse it explicitly.
|
|
5870
|
+
await sendToFrontend({
|
|
5871
|
+
type: 'background_session_error',
|
|
5872
|
+
slotId: resolvedSessionId,
|
|
5873
|
+
error: `Session ${resolvedSessionId.substring(0, 8)} is the active foreground session — pick a different session to background`,
|
|
5874
|
+
});
|
|
5875
|
+
}
|
|
5854
5876
|
else {
|
|
5855
5877
|
spawnBackgroundSession(resolvedSessionId, bgDir).catch((err) => {
|
|
5856
5878
|
console.error('❌ spawnBackgroundSession failed:', err instanceof Error ? err.message : err);
|
|
@@ -5952,6 +5974,12 @@ async function main() {
|
|
|
5952
5974
|
processes: topProcesses,
|
|
5953
5975
|
totalCount,
|
|
5954
5976
|
memory: { usedMb, totalMb, freeMb },
|
|
5977
|
+
// Authoritative set of still-live sub-agent dispatches. The frontend
|
|
5978
|
+
// reconciles its "running" background-flow rows against this and clears
|
|
5979
|
+
// any orphan whose completion/stop event was lost (or belonged to a
|
|
5980
|
+
// now-dead agent process — this list is per-process, so it's empty
|
|
5981
|
+
// after a restart and every stale row gets swept on the next poll).
|
|
5982
|
+
activeDispatchIds: statusManager.getActiveDispatchIds(),
|
|
5955
5983
|
});
|
|
5956
5984
|
}
|
|
5957
5985
|
catch (err) {
|
package/dist/status-manager.d.ts
CHANGED
|
@@ -90,6 +90,18 @@ export declare class StatusManager {
|
|
|
90
90
|
* Get context summary for the brain
|
|
91
91
|
*/
|
|
92
92
|
getContextSummary(): string;
|
|
93
|
+
/**
|
|
94
|
+
* Dispatcher reconciliation — the agent_ids of dispatches that are still LIVE
|
|
95
|
+
* (a real sub-agent dispatch, i.e. has a subagentType, whose dispatchState has
|
|
96
|
+
* not reached a terminal 'completed'/'failed'/'rejected'). The frontend
|
|
97
|
+
* cross-checks its "running" background-flow rows against this authoritative
|
|
98
|
+
* set on each process_list poll and clears any row NOT present — self-healing
|
|
99
|
+
* the orphaned "running" cards left when a task_completed / agent_stopped event
|
|
100
|
+
* was lost to a disconnect. Because this manager is a per-process singleton, an
|
|
101
|
+
* agent restart (the common session-drop case) resets it to empty, so every
|
|
102
|
+
* stale row from the dead process is reconciled away on the next poll.
|
|
103
|
+
*/
|
|
104
|
+
getActiveDispatchIds(): string[];
|
|
93
105
|
/**
|
|
94
106
|
* Dispatcher v1 — create or update a dispatch entry keyed by tool_use_id.
|
|
95
107
|
* Creates a minimal TaskStatus shell if the id doesn't yet exist so callers
|
package/dist/status-manager.js
CHANGED
|
@@ -182,6 +182,25 @@ export class StatusManager {
|
|
|
182
182
|
getContextSummary() {
|
|
183
183
|
return this.conversationContext.slice(-5).join(' | ');
|
|
184
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Dispatcher reconciliation — the agent_ids of dispatches that are still LIVE
|
|
187
|
+
* (a real sub-agent dispatch, i.e. has a subagentType, whose dispatchState has
|
|
188
|
+
* not reached a terminal 'completed'/'failed'/'rejected'). The frontend
|
|
189
|
+
* cross-checks its "running" background-flow rows against this authoritative
|
|
190
|
+
* set on each process_list poll and clears any row NOT present — self-healing
|
|
191
|
+
* the orphaned "running" cards left when a task_completed / agent_stopped event
|
|
192
|
+
* was lost to a disconnect. Because this manager is a per-process singleton, an
|
|
193
|
+
* agent restart (the common session-drop case) resets it to empty, so every
|
|
194
|
+
* stale row from the dead process is reconciled away on the next poll.
|
|
195
|
+
*/
|
|
196
|
+
getActiveDispatchIds() {
|
|
197
|
+
return Array.from(this.tasks.values())
|
|
198
|
+
.filter(t => !!t.subagentType &&
|
|
199
|
+
t.dispatchState !== 'completed' &&
|
|
200
|
+
t.dispatchState !== 'failed' &&
|
|
201
|
+
t.dispatchState !== 'rejected')
|
|
202
|
+
.map(t => t.id);
|
|
203
|
+
}
|
|
185
204
|
/**
|
|
186
205
|
* Dispatcher v1 — create or update a dispatch entry keyed by tool_use_id.
|
|
187
206
|
* Creates a minimal TaskStatus shell if the id doesn't yet exist so callers
|