osborn 0.9.127 → 0.9.129
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 +15 -5
- package/dist/index.js +58 -6
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -483,7 +483,12 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
483
483
|
return 'claude.agent-sdk';
|
|
484
484
|
}
|
|
485
485
|
get model() {
|
|
486
|
-
|
|
486
|
+
// The [1m] suffix opts into Opus 4.8's 1M context window (Claude Code's
|
|
487
|
+
// context-1m-2025-08-07 beta). WITHOUT it the SDK runs opus at its 200k
|
|
488
|
+
// base, so auto-compaction fired at ~153k every ~10 min (confirmed in the
|
|
489
|
+
// live agent log: compact_boundary pre_tokens≈153k). With [1m] the window
|
|
490
|
+
// is 1M and compaction happens far later. Overridable via opts.model.
|
|
491
|
+
return this.#opts.model || 'claude-opus-4-8[1m]';
|
|
487
492
|
}
|
|
488
493
|
get sessionId() {
|
|
489
494
|
return this.#sessionId;
|
|
@@ -766,9 +771,14 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
766
771
|
pushMessage(userText, sdkOptions, callbacks) {
|
|
767
772
|
// Auto-compact threshold — fires PreCompact when context fills to this %.
|
|
768
773
|
// Higher = uses more of the window before compacting (more context retained
|
|
769
|
-
// per turn, but less headroom for the next reply).
|
|
770
|
-
// before Claude starts hard-capping replies near the limit.
|
|
771
|
-
process.env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = '
|
|
774
|
+
// per turn, but less headroom for the next reply). 92% pushes it as late as
|
|
775
|
+
// is safe before Claude starts hard-capping replies near the 1M limit.
|
|
776
|
+
process.env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = '92';
|
|
777
|
+
// Compaction WINDOW in thousands of tokens (min(actual_context, this)). Now
|
|
778
|
+
// that the model runs with [1m] (1M context), set this to 1000 (=1,000,000)
|
|
779
|
+
// so the compaction budget matches the real window instead of being clamped
|
|
780
|
+
// to opus' 200k base — the actual cause of the ~153k early compaction.
|
|
781
|
+
process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW = '1000';
|
|
772
782
|
const userMessage = {
|
|
773
783
|
type: 'user',
|
|
774
784
|
message: { role: 'user', content: [{ type: 'text', text: userText }] },
|
|
@@ -1002,7 +1012,7 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1002
1012
|
permissionMode: this.#opts.permissionMode,
|
|
1003
1013
|
allowedTools,
|
|
1004
1014
|
// model: this.#opts.model || 'haiku', // haiku for speed with limited tools, sonnet for full research capabilities (including tool use trace in response)
|
|
1005
|
-
model: this.#opts.model || 'claude-opus-4-8', // Opus 4.8
|
|
1015
|
+
model: this.#opts.model || 'claude-opus-4-8[1m]', // Opus 4.8 + [1m] → 1M context (see get model() note); prevents ~153k early compaction
|
|
1006
1016
|
enableFileCheckpointing: true,
|
|
1007
1017
|
settingSources: ['project', 'user'],
|
|
1008
1018
|
extraArgs: { 'replay-user-messages': null },
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,58 @@ import { z } from 'zod';
|
|
|
53
53
|
// - Voice LLM with tool calling (ask_agent, respond_permission)
|
|
54
54
|
// - Routes tasks to Claude agents for execution
|
|
55
55
|
// ============================================================
|
|
56
|
+
// Build an enriched tool-use event for the frontend Logs drawer so it can
|
|
57
|
+
// render Claude-style review cards (Read/Edited/Ran with file names, +/- line
|
|
58
|
+
// counts, and an expandable diff) instead of a bare tool name. Best-effort:
|
|
59
|
+
// any field that can't be derived is simply omitted and the card degrades to
|
|
60
|
+
// the tool name. `input` is the raw tool arguments already carried on the
|
|
61
|
+
// tool_use/tool_result events emitted by claude-llm.
|
|
62
|
+
function buildToolLogEvent(name, input, status, agentRole) {
|
|
63
|
+
const ev = { type: 'tool_use', tool: name, status, agentRole };
|
|
64
|
+
const inp = input || {};
|
|
65
|
+
const basename = (p) => String(p).split('/').pop() || String(p);
|
|
66
|
+
const fp = inp.file_path || inp.path || inp.notebook_path;
|
|
67
|
+
if (fp) {
|
|
68
|
+
ev.filePath = fp;
|
|
69
|
+
ev.fileName = basename(fp);
|
|
70
|
+
}
|
|
71
|
+
if (typeof inp.command === 'string')
|
|
72
|
+
ev.command = inp.command;
|
|
73
|
+
if (typeof inp.pattern === 'string')
|
|
74
|
+
ev.pattern = inp.pattern;
|
|
75
|
+
if (typeof inp.url === 'string')
|
|
76
|
+
ev.url = inp.url;
|
|
77
|
+
if (typeof inp.description === 'string')
|
|
78
|
+
ev.description = inp.description;
|
|
79
|
+
try {
|
|
80
|
+
if (name === 'Edit' && typeof inp.old_string === 'string' && typeof inp.new_string === 'string') {
|
|
81
|
+
ev.linesRemoved = inp.old_string ? inp.old_string.split('\n').length : 0;
|
|
82
|
+
ev.linesAdded = inp.new_string ? inp.new_string.split('\n').length : 0;
|
|
83
|
+
ev.diff = createPatch(ev.fileName || 'edit', inp.old_string, inp.new_string, '', '', { context: 2 });
|
|
84
|
+
}
|
|
85
|
+
else if (name === 'MultiEdit' && Array.isArray(inp.edits)) {
|
|
86
|
+
let a = 0, r = 0;
|
|
87
|
+
const chunks = [];
|
|
88
|
+
for (const e of inp.edits) {
|
|
89
|
+
const o = String(e?.old_string ?? ''), n = String(e?.new_string ?? '');
|
|
90
|
+
r += o ? o.split('\n').length : 0;
|
|
91
|
+
a += n ? n.split('\n').length : 0;
|
|
92
|
+
chunks.push(createPatch(ev.fileName || 'edit', o, n, '', '', { context: 2 }));
|
|
93
|
+
}
|
|
94
|
+
ev.editCount = inp.edits.length;
|
|
95
|
+
ev.linesRemoved = r;
|
|
96
|
+
ev.linesAdded = a;
|
|
97
|
+
ev.diff = chunks.join('\n');
|
|
98
|
+
}
|
|
99
|
+
else if (name === 'Write' && typeof inp.content === 'string') {
|
|
100
|
+
ev.linesAdded = inp.content.split('\n').length;
|
|
101
|
+
ev.linesRemoved = 0;
|
|
102
|
+
ev.diff = inp.content.split('\n').slice(0, 60).map((l) => '+' + l).join('\n');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch { /* diff is best-effort — omit on any failure */ }
|
|
106
|
+
return ev;
|
|
107
|
+
}
|
|
56
108
|
// Load skills list with name + description for frontend display
|
|
57
109
|
function loadSkillsList(agentDir) {
|
|
58
110
|
const skillsDir = join(agentDir, '.claude', 'skills');
|
|
@@ -2542,7 +2594,7 @@ async function main() {
|
|
|
2542
2594
|
// Inline chat bubble — reuses the existing claude_output path that's already working.
|
|
2543
2595
|
if (event.type === 'compaction_started') {
|
|
2544
2596
|
const triggerLabel = event.trigger ? ` (${event.trigger})` : '';
|
|
2545
|
-
const text = `✨
|
|
2597
|
+
const text = `✨ _Teaching Osborn from this session — saving your preferences and decisions…_${triggerLabel}`;
|
|
2546
2598
|
sendToFrontend({
|
|
2547
2599
|
type: 'claude_output',
|
|
2548
2600
|
text,
|
|
@@ -2555,7 +2607,7 @@ async function main() {
|
|
|
2555
2607
|
const names = Array.isArray(event.skillNames) && event.skillNames.length > 0
|
|
2556
2608
|
? ` — ${event.skillNames.join(', ')}`
|
|
2557
2609
|
: '';
|
|
2558
|
-
const text = `✨
|
|
2610
|
+
const text = `✨ Osborn learned — ${n} skill${n === 1 ? '' : 's'} updated${names}. I'll carry these forward.`;
|
|
2559
2611
|
sendToFrontend({
|
|
2560
2612
|
type: 'claude_output',
|
|
2561
2613
|
text,
|
|
@@ -2630,11 +2682,11 @@ async function main() {
|
|
|
2630
2682
|
// Wire up events from the Claude SDK wrapper to frontend
|
|
2631
2683
|
directLLM.events.on('tool_use', (data) => {
|
|
2632
2684
|
console.log(`🔧 Claude: ${data.name}`);
|
|
2633
|
-
sendToFrontend(
|
|
2685
|
+
sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', 'direct'));
|
|
2634
2686
|
});
|
|
2635
2687
|
directLLM.events.on('tool_result', (data) => {
|
|
2636
2688
|
console.log(`✅ Done: ${data.name}`);
|
|
2637
|
-
sendToFrontend(
|
|
2689
|
+
sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', 'direct'));
|
|
2638
2690
|
// Detect research artifact writes (session workspace or legacy research dir)
|
|
2639
2691
|
if ((data.name === 'Write' || data.name === 'Edit') && data.input?.file_path) {
|
|
2640
2692
|
const fp = data.input.file_path;
|
|
@@ -3069,11 +3121,11 @@ async function main() {
|
|
|
3069
3121
|
// Wire up Claude events to frontend
|
|
3070
3122
|
realtimeClaudeHandler.events.on('tool_use', (data) => {
|
|
3071
3123
|
console.log(`🔧 Claude: ${data.name}`);
|
|
3072
|
-
sendToFrontend(
|
|
3124
|
+
sendToFrontend(buildToolLogEvent(data.name, data.input, 'running', 'realtime'));
|
|
3073
3125
|
});
|
|
3074
3126
|
realtimeClaudeHandler.events.on('tool_result', (data) => {
|
|
3075
3127
|
console.log(`✅ Done: ${data.name}`);
|
|
3076
|
-
sendToFrontend(
|
|
3128
|
+
sendToFrontend(buildToolLogEvent(data.name, data.input, 'completed', 'realtime'));
|
|
3077
3129
|
// Detect research artifact writes (session workspace or legacy research dir)
|
|
3078
3130
|
if ((data.name === 'Write' || data.name === 'Edit') && data.input?.file_path) {
|
|
3079
3131
|
const fp = data.input.file_path;
|