osborn 0.9.244 → 0.9.246
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 +69 -10
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -1002,25 +1002,63 @@ class MessageChannel {
|
|
|
1002
1002
|
* refuses, caps, or blocks a dispatch — the agent reads it and decides. Returns
|
|
1003
1003
|
* '' on any error so it can never break the turn.
|
|
1004
1004
|
*/
|
|
1005
|
-
|
|
1005
|
+
/**
|
|
1006
|
+
* Instant CPU busy% from /proc/stat (Linux). One aggregate 'cpu' line: idle+iowait
|
|
1007
|
+
* vs total. Two reads a short interval apart give real-time utilization across all
|
|
1008
|
+
* cores — unlike the 1-min loadavg, which lags a fresh burst of heavy commands by
|
|
1009
|
+
* ~30-60s. Returns null off-Linux / on any parse error (caller falls back to loadavg).
|
|
1010
|
+
*/
|
|
1011
|
+
function readProcStatCpu() {
|
|
1012
|
+
try {
|
|
1013
|
+
const line = readFileSync('/proc/stat', 'utf8').split('\n', 1)[0];
|
|
1014
|
+
const n = line.trim().split(/\s+/).slice(1).map(Number); // drop the "cpu" label
|
|
1015
|
+
if (n.length < 5 || n.some(Number.isNaN))
|
|
1016
|
+
return null;
|
|
1017
|
+
const idle = n[3] + (n[4] || 0); // idle + iowait
|
|
1018
|
+
const total = n.reduce((a, b) => a + b, 0);
|
|
1019
|
+
return { idle, total };
|
|
1020
|
+
}
|
|
1021
|
+
catch {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
async function buildMachineNudge() {
|
|
1006
1026
|
try {
|
|
1007
1027
|
const ncpu = Math.max(1, cpus().length);
|
|
1008
|
-
|
|
1028
|
+
// #3: prefer an INSTANT CPU read (proc/stat delta over ~200ms) over the lagging
|
|
1029
|
+
// 1-min loadavg, so a fresh storm of tsc/build/test shows up on THIS turn, not a
|
|
1030
|
+
// minute later. Falls back to loadavg÷cores off-Linux or if the delta is unusable.
|
|
1031
|
+
let cpuPct;
|
|
1032
|
+
const a = readProcStatCpu();
|
|
1033
|
+
if (a) {
|
|
1034
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1035
|
+
const b = readProcStatCpu();
|
|
1036
|
+
const dTotal = b ? b.total - a.total : 0;
|
|
1037
|
+
cpuPct = b && dTotal > 0
|
|
1038
|
+
? Math.round(((dTotal - (b.idle - a.idle)) / dTotal) * 100)
|
|
1039
|
+
: Math.round((loadavg()[0] / ncpu) * 100);
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
cpuPct = Math.round((loadavg()[0] / ncpu) * 100);
|
|
1043
|
+
}
|
|
1009
1044
|
const memPct = Math.round((1 - freemem() / totalmem()) * 100);
|
|
1010
1045
|
const subagents = statusManager.getActiveDispatchIds().length;
|
|
1011
|
-
// Recommended concurrent heavy sub-agents, adapted to live headroom.
|
|
1012
|
-
//
|
|
1013
|
-
// otherwise — surfaced as an actual number/target, never enforced.
|
|
1046
|
+
// Recommended concurrent heavy sub-agents, adapted to live headroom. A concrete
|
|
1047
|
+
// DEFAULT the agent follows unless a task warrants otherwise — never enforced.
|
|
1014
1048
|
const recMax = (cpuPct >= 80 || memPct >= 90) ? Math.max(1, ncpu - 1) // hot: pull back below cores
|
|
1015
1049
|
: (cpuPct >= 60) ? ncpu // busy: ~cores
|
|
1016
1050
|
: ncpu + 1; // calm: a little headroom
|
|
1017
1051
|
const over = subagents > recMax;
|
|
1018
1052
|
const warn = over || cpuPct >= 80;
|
|
1019
|
-
|
|
1053
|
+
// #4: name the actual CPU culprits — the spikes come from heavy COMMANDS sub-agents
|
|
1054
|
+
// run, not their count. One tsc/build/test pegs a core for 30-120s; ten file-reading
|
|
1055
|
+
// agents barely register. Tell the agent WHAT to throttle.
|
|
1056
|
+
const culprits = 'CPU cost is heavy commands (tsc/typecheck, build, test, npm/pnpm install, eslint) — not agent count. Prefer scoped/incremental runs, cache, and never fire several at once.';
|
|
1057
|
+
const head = `[MACHINE${warn ? ' ⚠️' : ''}] CPU ${cpuPct}% · MEM ${memPct}% · sub-agents ${subagents}/${recMax} recommended`;
|
|
1020
1058
|
if (over)
|
|
1021
|
-
return `${head} — over the recommended concurrency; wind down to ≤${recMax} (finish or stop in-flight sub-agents) unless a task must continue. (recommendation — the agent decides, not enforced)`;
|
|
1059
|
+
return `${head} — over the recommended concurrency; wind down to ≤${recMax} (finish or stop in-flight sub-agents) unless a task must continue. ${culprits} (recommendation — the agent decides, not enforced)`;
|
|
1022
1060
|
if (cpuPct >= 80)
|
|
1023
|
-
return `${head} — load high; hold new sub-agents beyond ${recMax} unless needed. (recommendation — the agent decides, not enforced)`;
|
|
1061
|
+
return `${head} — load high; hold new sub-agents beyond ${recMax} unless needed. ${culprits} (recommendation — the agent decides, not enforced)`;
|
|
1024
1062
|
return head;
|
|
1025
1063
|
}
|
|
1026
1064
|
catch {
|
|
@@ -1779,6 +1817,10 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1779
1817
|
#abortController;
|
|
1780
1818
|
#llmRef;
|
|
1781
1819
|
#approvedWriterToolUseIds = new Set();
|
|
1820
|
+
// Last Task/Agent dispatch description per subagent_type, captured at PreToolUse so
|
|
1821
|
+
// SubagentStart can label the Running row with WHAT the sub-agent is doing even if the
|
|
1822
|
+
// SDK hook input omits the prompt. Cleared when consumed by the matching start.
|
|
1823
|
+
#pendingTaskByType = new Map();
|
|
1782
1824
|
constructor(llmInstance, { chatCtx, toolCtx, connOptions, opts, sessionId, onSessionId, eventEmitter, onCheckpoint, onPermissionRequest, abortController, }) {
|
|
1783
1825
|
super(llmInstance, { chatCtx, toolCtx, connOptions });
|
|
1784
1826
|
this.#llmRef = llmInstance;
|
|
@@ -1988,6 +2030,14 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1988
2030
|
// tool_use, returns {} → canUseTool workspace auto-approve).
|
|
1989
2031
|
}
|
|
1990
2032
|
console.log(`🔧 Claude: ${toolName}`);
|
|
2033
|
+
// Capture the dispatch description/prompt per subagent_type so SubagentStart
|
|
2034
|
+
// can show WHAT a sub-agent is working on (see #pendingTaskByType).
|
|
2035
|
+
if (toolName === 'Task' || toolName === 'Agent') {
|
|
2036
|
+
const st = String(toolInput?.subagent_type || toolInput?.agent_type || '');
|
|
2037
|
+
const d = String(toolInput?.description || toolInput?.prompt || '');
|
|
2038
|
+
if (st && d)
|
|
2039
|
+
this.#pendingTaskByType.set(st, d);
|
|
2040
|
+
}
|
|
1991
2041
|
this.#eventEmitter.emit('tool_use', { name: toolName, input: toolInput, agentRole: agentType || 'main' });
|
|
1992
2042
|
return {};
|
|
1993
2043
|
}]
|
|
@@ -2064,7 +2114,7 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2064
2114
|
// Lightweight machine-load nudge (main agent only). Cheap synchronous
|
|
2065
2115
|
// snapshot, prepended so it's present even when recall degrades under load.
|
|
2066
2116
|
// Advisory only — never caps/refuses a dispatch.
|
|
2067
|
-
const machineNudge = fromSubagent ? '' : buildMachineNudge();
|
|
2117
|
+
const machineNudge = fromSubagent ? '' : await buildMachineNudge();
|
|
2068
2118
|
const additionalContext = [machineNudge, reminder, recalled].filter(Boolean).join('\n\n');
|
|
2069
2119
|
console.log(`📌 UserPromptSubmit: injected turn-shape reminder (${reminder.length} chars)${recalled ? ` + recall (${recalled.length} chars)` : ''}${machineNudge ? ` + machine(${machineNudge.includes('⚠️') ? 'HOT' : 'ok'})` : ''} for prompt="${promptPreview}..." [tool budget reset to 0/${TOOL_CALL_BUDGET}]`);
|
|
2070
2120
|
return {
|
|
@@ -2357,7 +2407,16 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2357
2407
|
matcher: '.*',
|
|
2358
2408
|
hooks: [async (input) => {
|
|
2359
2409
|
console.log('[LIFECYCLE-PROBE] SubagentStart', JSON.stringify(input));
|
|
2360
|
-
|
|
2410
|
+
// Surface WHAT the sub-agent was dispatched to do (not just its type) so the
|
|
2411
|
+
// Running panel shows the task while it's in flight, before any result lands.
|
|
2412
|
+
// The SDK hook may carry the initial prompt/description; fall back to the last
|
|
2413
|
+
// Task/Agent dispatch of this type captured at PreToolUse. Best-effort.
|
|
2414
|
+
const at0 = input?.agent_type;
|
|
2415
|
+
const rawTask = input?.prompt ?? input?.description ?? (at0 ? this.#pendingTaskByType.get(String(at0)) : undefined);
|
|
2416
|
+
if (at0)
|
|
2417
|
+
this.#pendingTaskByType.delete(String(at0));
|
|
2418
|
+
const task = rawTask ? String(rawTask).replace(/\s+/g, ' ').trim().slice(0, 160) : undefined;
|
|
2419
|
+
this.#eventEmitter.emit('agent_started', { agent_type: input?.agent_type, agent_id: input?.agent_id, task });
|
|
2361
2420
|
// Record the dispatch as LIVE so the frontend can reconcile orphaned
|
|
2362
2421
|
// "running" rows against statusManager.getActiveDispatchIds(). Without a
|
|
2363
2422
|
// start-time entry the manager only knew about dispatches at completion,
|