osborn 0.9.167 → 0.9.169
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.d.ts +15 -0
- package/dist/claude-llm.js +71 -16
- package/dist/index.js +19 -5
- package/package.json +1 -1
package/dist/claude-llm.d.ts
CHANGED
|
@@ -79,6 +79,14 @@ export declare const NAMED_AGENTS: {
|
|
|
79
79
|
prompt: string;
|
|
80
80
|
};
|
|
81
81
|
};
|
|
82
|
+
export declare const FAST_MODEL = "haiku";
|
|
83
|
+
/**
|
|
84
|
+
* Apply turbo override to an agents roster.
|
|
85
|
+
* When turbo is true returns a DEEP COPY with every agent's model set to
|
|
86
|
+
* FAST_MODEL. When false returns the input object unchanged (zero allocation).
|
|
87
|
+
* NEVER mutates the input — NAMED_AGENTS and DB-sourced rows are untouched.
|
|
88
|
+
*/
|
|
89
|
+
export declare function applyTurbo(agents: Record<string, any>, turbo: boolean): Record<string, any>;
|
|
82
90
|
/**
|
|
83
91
|
* Claude LLM - Wraps Claude Agent SDK for LiveKit
|
|
84
92
|
* Research mode: reads anything, writes only to session workspace
|
|
@@ -133,6 +141,13 @@ export declare class ClaudeLLM extends llm.LLM {
|
|
|
133
141
|
* it was created with.
|
|
134
142
|
*/
|
|
135
143
|
setAgents(agents: ClaudeLLMOptions['agents']): void;
|
|
144
|
+
/**
|
|
145
|
+
* Enable/disable Turbo mode. When on, the main model and every sub-agent
|
|
146
|
+
* spawn on FAST_MODEL. Takes effect at the next query cold start.
|
|
147
|
+
*/
|
|
148
|
+
setTurbo(on: boolean): void;
|
|
149
|
+
/** Read-only accessor used by ClaudeLLMStream (private fields are class-scoped). */
|
|
150
|
+
get turbo(): boolean;
|
|
136
151
|
/**
|
|
137
152
|
* Set session ID to resume a specific conversation
|
|
138
153
|
* Call this before sending the first message to resume from a previous session
|
package/dist/claude-llm.js
CHANGED
|
@@ -341,16 +341,32 @@ export const NAMED_AGENTS = {
|
|
|
341
341
|
'## Your role',
|
|
342
342
|
'Execute test suites, build commands, and linters. Interpret failures clearly.',
|
|
343
343
|
'You are a quality gate — find out whether the code works, and say exactly what broke.',
|
|
344
|
+
'You protect the USER: the product must stay predictable across releases. A change that alters',
|
|
345
|
+
'observed behavior without a matching requirement is a regression — treat behavioral surprise as a defect.',
|
|
344
346
|
'',
|
|
345
347
|
'## Grounding — consult shared context before writing or running tests',
|
|
346
|
-
'Before deciding what to test, locate the session index (search-index.txt under .claude/projects/<slug>/osb/<session>/; newest if several) and Grep it for the changes/work under test. Also check
|
|
348
|
+
'Before deciding what to test, locate the session index (search-index.txt under .claude/projects/<slug>/osb/<session>/; newest if several) and Grep it for the changes/work under test. Also check project docs and known-issues files. Key doc locations to consult: `/workspace/osborn/CLAUDE.md`, `/workspace/osborn/docs/critical-patterns.md`, the `docs/` directory, `README.md`, and `CHANGELOG.md`. Check these for: (a) KNOWN ISSUES and gotchas already recorded, and (b) what behavior is ALREADY covered by existing tests.',
|
|
347
349
|
'Purpose: target regression coverage at real GAPS and known-risk areas rather than testing blind or duplicating coverage — and stay IN SYNC with the reviewer, which reads the same sources.',
|
|
348
350
|
'Read only the relevant slice of the index, never the whole file. If you find nothing or no index/docs exist, proceed normally — this is an optimization, not a hard dependency.',
|
|
351
|
+
'If the change adds or renames a feature, flag any doc now out of date (see DOC STALENESS in "What to return").',
|
|
349
352
|
'',
|
|
350
353
|
'## Backward-compatibility / regression mandate (CRITICAL)',
|
|
351
354
|
'Existing test suites MUST still pass — any pre-existing test that breaks is a BLOCKER; report it as such.',
|
|
352
355
|
'The public API surface (function signatures, exported types, return shapes, behavior) must NOT silently change.',
|
|
353
356
|
'Flag any change that could break existing callers, even if no test currently covers it.',
|
|
357
|
+
'Coverage targets — where applicable, explicitly include:',
|
|
358
|
+
' - Backward-compatibility tests: verify the old calling contract still holds for any modified function or export.',
|
|
359
|
+
' - Regression tests for existing behavior: confirm behaviors that existed before the change still work after.',
|
|
360
|
+
' - End-to-end checks: verify that an existing route, HTTP endpoint, or exported function still works as documented.',
|
|
361
|
+
'Find existing tests first. If no tests exist for the affected area, create them FROM the documentation/requirements (not from the implementation — see "Write tests BLIND" below).',
|
|
362
|
+
'',
|
|
363
|
+
'## Write tests BLIND to the solution',
|
|
364
|
+
'When you WRITE or CREATE tests, derive them ONLY from the requirements/task, the documentation, and the',
|
|
365
|
+
'EXISTING code (pre-change behavior). Do NOT read, and do NOT shape your assertions around, the writer\'s',
|
|
366
|
+
'new diff, new code, or rationale. Tests must encode the behavior that is REQUIRED and DOCUMENTED, never',
|
|
367
|
+
'the behavior that happens to have been implemented — otherwise they are rigged to pass.',
|
|
368
|
+
'(Running/executing the tests against the code afterward is expected; this blindness applies to the',
|
|
369
|
+
'test-WRITING phase only.)',
|
|
354
370
|
'',
|
|
355
371
|
'## How to work',
|
|
356
372
|
'0. **Get the diff first (MANDATORY):** Run `git diff HEAD~1 HEAD --name-only` to get the list of',
|
|
@@ -373,13 +389,19 @@ export const NAMED_AGENTS = {
|
|
|
373
389
|
'- TEST FILES: path(s) to any test files written or modified',
|
|
374
390
|
'- COVERAGE DELTA: what the change adds or leaves uncovered (before vs after where determinable); list notable uncovered lines/paths',
|
|
375
391
|
'- REGRESSIONS / COMPAT BREAKS: explicit list of any pre-existing tests that now fail or API changes that could break existing callers — tag each as BLOCKER',
|
|
392
|
+
'- DOC STALENESS: list any doc file+section that no longer matches the change (path + what drifted), or "none". Tag each DOC-STALE.',
|
|
376
393
|
'- What you checked but found to be unrelated',
|
|
377
394
|
'',
|
|
378
395
|
'## Backward-compatibility testing & building the test library',
|
|
379
396
|
'GROW THE LIBRARY OVER TIME: where coverage is missing for the behavior being verified,',
|
|
380
|
-
'CREATE a targeted regression test so the suite accumulates over time.
|
|
381
|
-
'
|
|
382
|
-
'
|
|
397
|
+
'CREATE a targeted regression test so the suite accumulates over time.',
|
|
398
|
+
'',
|
|
399
|
+
'AGENT TEST SUITE REALITY: as of now there is NO test suite for the agent (CLAUDE.md: "There is no test',
|
|
400
|
+
'suite"). You MAY write minimal `agent/tests/*.test.ts` files runnable via `npx tsx` (e.g.',
|
|
401
|
+
'`npx tsx agent/tests/my-feature.test.ts`). Do NOT attempt to wire a `package.json` "test" script —',
|
|
402
|
+
'that is a config write, is gate-denied, and is a WRITER task. Instead, FLAG that need as a follow-up.',
|
|
403
|
+
'If standing up even a minimal runner is out of scope for the change under test, report it as a',
|
|
404
|
+
'COVERAGE GAP rather than half-installing infra.',
|
|
383
405
|
'',
|
|
384
406
|
'HARD RESTRICTION: you may ONLY write TEST files — files whose names contain `.test.` or `.spec.`,',
|
|
385
407
|
'or files located under a `__tests__/` or `tests/` directory.',
|
|
@@ -559,6 +581,24 @@ export const NAMED_AGENTS = {
|
|
|
559
581
|
].join('\n'),
|
|
560
582
|
},
|
|
561
583
|
};
|
|
584
|
+
// Turbo mode — single fast model override. ONE place to update for future
|
|
585
|
+
// Gemini/OpenAI fast model swap. Keep this immediately after NAMED_AGENTS.
|
|
586
|
+
export const FAST_MODEL = 'haiku';
|
|
587
|
+
/**
|
|
588
|
+
* Apply turbo override to an agents roster.
|
|
589
|
+
* When turbo is true returns a DEEP COPY with every agent's model set to
|
|
590
|
+
* FAST_MODEL. When false returns the input object unchanged (zero allocation).
|
|
591
|
+
* NEVER mutates the input — NAMED_AGENTS and DB-sourced rows are untouched.
|
|
592
|
+
*/
|
|
593
|
+
export function applyTurbo(agents, turbo) {
|
|
594
|
+
if (!turbo)
|
|
595
|
+
return agents;
|
|
596
|
+
const out = {};
|
|
597
|
+
for (const [name, agent] of Object.entries(agents)) {
|
|
598
|
+
out[name] = { ...agent, model: FAST_MODEL };
|
|
599
|
+
}
|
|
600
|
+
return out;
|
|
601
|
+
}
|
|
562
602
|
const RESEARCH_TOOLS = [
|
|
563
603
|
'Read', 'Write', 'Edit', 'Glob', 'Grep',
|
|
564
604
|
'Bash', 'WebSearch', 'WebFetch',
|
|
@@ -642,6 +682,9 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
642
682
|
// Dedup guard — prevents double-firing reviewer/gate if SubagentStop fires
|
|
643
683
|
// more than once for the same agent_id (e.g. retry edge cases).
|
|
644
684
|
#dispatchedFor = new Set();
|
|
685
|
+
// Turbo mode — when true, every spawned agent (main + sub-agents) runs on
|
|
686
|
+
// FAST_MODEL regardless of individual model config. Default off = no-op.
|
|
687
|
+
#turbo = false;
|
|
645
688
|
constructor(opts = {}) {
|
|
646
689
|
super();
|
|
647
690
|
// Session resume/continue options
|
|
@@ -801,6 +844,16 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
801
844
|
this.#opts.agents = agents;
|
|
802
845
|
console.log(`🤖 Named agents ${agents ? `set (${Object.keys(agents).join(', ')})` : 'reset to built-ins'} — applies at next query cold start`);
|
|
803
846
|
}
|
|
847
|
+
/**
|
|
848
|
+
* Enable/disable Turbo mode. When on, the main model and every sub-agent
|
|
849
|
+
* spawn on FAST_MODEL. Takes effect at the next query cold start.
|
|
850
|
+
*/
|
|
851
|
+
setTurbo(on) {
|
|
852
|
+
this.#turbo = on;
|
|
853
|
+
console.log(`⚡ Turbo mode ${on ? 'ON' : 'OFF'} — main model → ${on ? FAST_MODEL : (this.#opts.model || 'claude-opus-4-8[1m]')}; applies at next query cold start`);
|
|
854
|
+
}
|
|
855
|
+
/** Read-only accessor used by ClaudeLLMStream (private fields are class-scoped). */
|
|
856
|
+
get turbo() { return this.#turbo; }
|
|
804
857
|
/**
|
|
805
858
|
* Set session ID to resume a specific conversation
|
|
806
859
|
* Call this before sending the first message to resume from a previous session
|
|
@@ -1248,15 +1301,13 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1248
1301
|
* if the verdict is REJECT. A reviewer failure must never crash the consumer.
|
|
1249
1302
|
* Public so ClaudeLLMStream can call it via this.#llmRef.spawnReviewer().
|
|
1250
1303
|
*/
|
|
1304
|
+
// writerOutput intentionally NOT fed to the reviewer (neutrality); kept for signature stability
|
|
1251
1305
|
async spawnReviewer(agentId, writerOutput, emitter) {
|
|
1252
1306
|
// Dedup guard — SubagentStop may fire more than once for the same agent_id.
|
|
1253
1307
|
if (this.#dispatchedFor.has(agentId))
|
|
1254
1308
|
return;
|
|
1255
1309
|
this.#dispatchedFor.add(agentId);
|
|
1256
1310
|
try {
|
|
1257
|
-
const idxPathReviewer = (this.#sessionId && this.#opts.workingDirectory)
|
|
1258
|
-
? getIndexPath(this.#sessionId, this.#opts.workingDirectory)
|
|
1259
|
-
: null;
|
|
1260
1311
|
// Get the actual diff to give reviewer concrete evidence instead of just the narrative
|
|
1261
1312
|
let gitDiff = '';
|
|
1262
1313
|
try {
|
|
@@ -1269,15 +1320,12 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1269
1320
|
// non-fatal: if git fails, proceed without diff
|
|
1270
1321
|
}
|
|
1271
1322
|
const prompt = [
|
|
1272
|
-
'
|
|
1273
|
-
'The git diff is
|
|
1323
|
+
'Review the change below for correctness, spec/requirement adherence, and obvious bugs.',
|
|
1324
|
+
'The git diff is the AUTHORITATIVE source of what changed. Judge it on its own merits —',
|
|
1325
|
+
'you are deliberately NOT given the writer\'s rationale or the session index; form an independent verdict from the code and the project\'s own documented standards.',
|
|
1326
|
+
'Read the actual modified files and their callers as needed (you are not limited to the diff).',
|
|
1274
1327
|
'End your reply with exactly `VERDICT: ACCEPT` or `VERDICT: REJECT`.',
|
|
1275
|
-
...(idxPathReviewer ? [`The session index is at ${idxPathReviewer} — you MUST read it before reviewing.`] : []),
|
|
1276
1328
|
gitDiff,
|
|
1277
|
-
'',
|
|
1278
|
-
'<writer_output>',
|
|
1279
|
-
writerOutput.slice(0, 6000),
|
|
1280
|
-
'</writer_output>',
|
|
1281
1329
|
].join('\n');
|
|
1282
1330
|
// Do NOT pass agents here — the reviewer must be review-only and must not
|
|
1283
1331
|
// be able to spawn writer/researcher/reasoner sub-agents. Passing an empty
|
|
@@ -1546,7 +1594,8 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1546
1594
|
permissionMode: this.#opts.permissionMode,
|
|
1547
1595
|
allowedTools,
|
|
1548
1596
|
// model: this.#opts.model || 'haiku', // haiku for speed with limited tools, sonnet for full research capabilities (including tool use trace in response)
|
|
1549
|
-
|
|
1597
|
+
// Turbo: when on, override main model to FAST_MODEL regardless of config.
|
|
1598
|
+
model: this.#llmRef.turbo ? FAST_MODEL : (this.#opts.model || 'claude-opus-4-8[1m]'), // Opus 4.8 + [1m] → 1M context (see get model() note); prevents ~153k early compaction
|
|
1550
1599
|
enableFileCheckpointing: true,
|
|
1551
1600
|
settingSources: ['project', 'user'],
|
|
1552
1601
|
extraArgs: { 'replay-user-messages': null },
|
|
@@ -1977,7 +2026,13 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1977
2026
|
// Named sub-agents — the orchestrator delegates to these specialists.
|
|
1978
2027
|
// Built-in definitions live at module level (NAMED_AGENTS); per-user
|
|
1979
2028
|
// DB-backed definitions (opts.agents, via set_agents) take precedence.
|
|
1980
|
-
|
|
2029
|
+
// TURBO BYPASS: when turbo is on and no custom agents were provided,
|
|
2030
|
+
// opts.agents is undefined — the ?? NAMED_AGENTS fallback would skip
|
|
2031
|
+
// the override entirely. Explicitly apply applyTurbo(NAMED_AGENTS)
|
|
2032
|
+
// so built-in agents always get FAST_MODEL when turbo is on.
|
|
2033
|
+
agents: this.#llmRef.turbo
|
|
2034
|
+
? applyTurbo(this.#opts.agents ?? NAMED_AGENTS, true)
|
|
2035
|
+
: (this.#opts.agents ?? NAMED_AGENTS),
|
|
1981
2036
|
};
|
|
1982
2037
|
// Run Claude Agent SDK query() and stream results
|
|
1983
2038
|
let hasOutput = false;
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ const __dirname = dirname(__filename);
|
|
|
30
30
|
import { createPatch } from 'diff';
|
|
31
31
|
import { loadConfig, getMcpServers, getEnabledMcpServerNames, getVoiceMode, getRealtimeConfig, getDirectConfig, listAllClaudeSessions, getMostRecentSessionId, sessionExists, getSessionSummary, getConversationHistory, ensureSessionWorkspace, getSessionWorkspace, getMcpServerStatusList, buildMcpServersForKeys, listWorkspaceArtifacts } from './config.js';
|
|
32
32
|
import { createSTT, createTTS, createRealtimeModelFromConfig, DIRECT_MODE_STT, DIRECT_MODE_TTS } from './voice-io.js';
|
|
33
|
-
import { createClaudeLLM, NAMED_AGENTS } from './claude-llm.js';
|
|
33
|
+
import { createClaudeLLM, NAMED_AGENTS, applyTurbo } from './claude-llm.js';
|
|
34
34
|
import { clearPipelineFastBrainSession, prewarmBM25Index } from './pipeline-fastbrain.js';
|
|
35
35
|
import { ensureClaudeAuth } from './claude-auth.js';
|
|
36
36
|
import { createSmitheryProxy, destroySmitheryProxy, parseSmitheryUrl, isSmitheryUrl, SmitheryAuthorizationError } from './smithery-proxy.js';
|
|
@@ -2239,6 +2239,9 @@ async function main() {
|
|
|
2239
2239
|
// switch/resume) reuse them without waiting for a frontend resend. Merged
|
|
2240
2240
|
// OVER the built-in NAMED_AGENTS (same-name user rows shadow built-ins).
|
|
2241
2241
|
let userNamedAgents = null;
|
|
2242
|
+
// Turbo mode — read from set_agents handshake (data.turbo). When true every
|
|
2243
|
+
// agent (main + sub-agents) runs on FAST_MODEL. Default false = perfect no-op.
|
|
2244
|
+
let turboMode = false;
|
|
2242
2245
|
// Built-ins the user has removed (tombstoned in user_agents with enabled=false).
|
|
2243
2246
|
// A removed built-in disappears from the effective set but stays re-addable
|
|
2244
2247
|
// from the frontend's "Available to add" list — default agents are never
|
|
@@ -3266,14 +3269,21 @@ async function main() {
|
|
|
3266
3269
|
voiceMode: 'direct',
|
|
3267
3270
|
skipTTSQueue: true,
|
|
3268
3271
|
onCompactionEvent: buildOnCompactionEvent(),
|
|
3269
|
-
// Per-user named agents survive LLM recreations (session switch/resume)
|
|
3272
|
+
// Per-user named agents survive LLM recreations (session switch/resume).
|
|
3273
|
+
// applyTurbo runs post-merge so custom models are also overridden when turbo is on.
|
|
3274
|
+
// When neither custom agents nor turbo are active, pass undefined so the
|
|
3275
|
+
// ClaudeLLM cold-start uses NAMED_AGENTS directly (the ?? NAMED_AGENTS path).
|
|
3276
|
+
// When turbo IS on with no custom agents, undefined would skip the override —
|
|
3277
|
+
// ClaudeLLM handles that internally via the turbo bypass in ClaudeLLMStream.
|
|
3270
3278
|
agents: (userNamedAgents || userRemovedAgents.length) ? (() => {
|
|
3271
3279
|
const base = { ...NAMED_AGENTS };
|
|
3272
3280
|
for (const r of userRemovedAgents)
|
|
3273
3281
|
delete base[r];
|
|
3274
|
-
return { ...base, ...(userNamedAgents || {}) };
|
|
3282
|
+
return applyTurbo({ ...base, ...(userNamedAgents || {}) }, turboMode);
|
|
3275
3283
|
})() : undefined,
|
|
3276
3284
|
});
|
|
3285
|
+
// Apply turbo to the freshly created LLM so the main model also picks it up.
|
|
3286
|
+
directLLM.setTurbo(turboMode);
|
|
3277
3287
|
currentLLM = directLLM;
|
|
3278
3288
|
// Reset the session always-allow list for each new direct session
|
|
3279
3289
|
sessionAlwaysAllowPaths = new Set();
|
|
@@ -6008,11 +6018,15 @@ async function main() {
|
|
|
6008
6018
|
userNamedAgents = Object.keys(validated).length ? validated : null;
|
|
6009
6019
|
// Removed built-ins (tombstones) — excluded from the effective set.
|
|
6010
6020
|
userRemovedAgents = Array.isArray(data.removed) ? data.removed.map(String) : [];
|
|
6021
|
+
// Turbo preference from the frontend (sourced from user_settings DB row).
|
|
6022
|
+
turboMode = !!data.turbo;
|
|
6011
6023
|
const base = { ...NAMED_AGENTS };
|
|
6012
6024
|
for (const r of userRemovedAgents)
|
|
6013
6025
|
delete base[r];
|
|
6014
|
-
|
|
6015
|
-
const
|
|
6026
|
+
// applyTurbo wraps AFTER the base+custom merge so custom models also get overridden.
|
|
6027
|
+
const merged = applyTurbo({ ...base, ...(userNamedAgents || {}) }, turboMode);
|
|
6028
|
+
const customized = !!userNamedAgents || userRemovedAgents.length > 0 || turboMode;
|
|
6029
|
+
currentLLM?.setTurbo?.(turboMode);
|
|
6016
6030
|
currentLLM?.setAgents?.(customized ? merged : undefined);
|
|
6017
6031
|
console.log(`🤖 set_agents: ${Object.keys(validated).length} user agent(s), ${userRemovedAgents.length} removed → effective [${Object.keys(merged).join(', ')}]`);
|
|
6018
6032
|
await sendToFrontend({
|