osborn 0.9.233 → 0.9.235
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 +49 -3
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -95,6 +95,15 @@ async function buildRecallInjection(sessionId, workingDir, prompt) {
|
|
|
95
95
|
// agent's direct tool calls count against the budget.
|
|
96
96
|
let turnToolCallCount = 0;
|
|
97
97
|
const TOOL_CALL_BUDGET = 5;
|
|
98
|
+
// Writer PROVENANCE REMINDER (Chesterton's Fence): before the writer mutates a
|
|
99
|
+
// version-controlled file it should review that file's git history so the edit is
|
|
100
|
+
// backward-informed (root cause / prior + reverted attempts / cross-component coupling),
|
|
101
|
+
// not forward-only. Delivered as a ONE-SHOT PreToolUse deny-with-reminder on the writer's
|
|
102
|
+
// first edit of an existing file each turn. This single flag is the irreducible minimum:
|
|
103
|
+
// a PreToolUse hook can only allow/deny/ask (no "inject note + proceed"), so the reminder
|
|
104
|
+
// must be a deny — and without a "already nudged this turn" flag that deny would loop
|
|
105
|
+
// forever. Reset on every UserPromptSubmit (new turn).
|
|
106
|
+
let writerHistoryReminded = false;
|
|
98
107
|
/**
|
|
99
108
|
* Strip markdown formatting for TTS (text-to-speech)
|
|
100
109
|
* Removes **bold**, ##headers, ```code```, etc. so TTS doesn't read them literally
|
|
@@ -493,6 +502,13 @@ export const NAMED_AGENTS = {
|
|
|
493
502
|
'4. If ANYTHING has drifted from the plan (file moved, code refactored, dependency changed):',
|
|
494
503
|
' STOP and report back to the main agent. Do NOT improvise.',
|
|
495
504
|
'',
|
|
505
|
+
'### Step 1b: Review history before touching a versioned file',
|
|
506
|
+
'For any file that ALREADY EXISTS (i.e. has git history), review its lineage BEFORE you edit it (on your first edit of an existing file each turn you\'ll get a one-time reminder to). This is Chesterton\'s Fence — understand WHY the code is there before you change it. Do:',
|
|
507
|
+
' 1. `git log --follow -p -- "<file>"` — recent commits and the rationale behind them.',
|
|
508
|
+
' 2. `git blame` on the exact lines you will change — who/when/which commit introduced them.',
|
|
509
|
+
' 3. `git log --oneline --grep=<feature>` and `git log --oneline -S"<symbol>"` — look for related OR REVERTED work so you do not redo something already tried, and note which components were co-changed (coupling).',
|
|
510
|
+
'Fix problems backward-informed — root cause, systemic implications, prior attempts — not forward-only. New files are exempt (no history to review).',
|
|
511
|
+
'',
|
|
496
512
|
'### Step 2: Clarify unknowns',
|
|
497
513
|
'1. If the plan is vague or ambiguous — ask the main agent a specific clarifying question.',
|
|
498
514
|
' Examples: "Which config format — YAML or JSON?", "New file or extend existing auth.ts?"',
|
|
@@ -1583,8 +1599,15 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
1583
1599
|
if (msg.requestId) {
|
|
1584
1600
|
callbacks.eventEmitter.emit('query_request_id', { requestId: msg.requestId });
|
|
1585
1601
|
}
|
|
1586
|
-
// Stream assistant text → tts_say events
|
|
1587
|
-
|
|
1602
|
+
// Stream assistant text → tts_say events.
|
|
1603
|
+
// SUBAGENT GUARD (regression fix): the SDK surfaces a Task sub-agent's assistant
|
|
1604
|
+
// messages inline in THIS same stream, tagged with a non-null parent_tool_use_id
|
|
1605
|
+
// ("non-null when the message was produced inside a subagent" — SDK docs). Only the
|
|
1606
|
+
// MAIN orchestrator (parent_tool_use_id === null) may speak or post a chat bubble;
|
|
1607
|
+
// a sub-agent's raw narration must NOT reach TTS/UI (its work already shows via the
|
|
1608
|
+
// tool_use activity events). Without this, once verifiers/researcher became real Task
|
|
1609
|
+
// sub-agents their text "took over the mic" and leaked raw code/markdown into audio.
|
|
1610
|
+
if (msg.type === 'assistant' && msg.message?.content && !msg.parent_tool_use_id) {
|
|
1588
1611
|
// Assign a stable messageId for this turn (first block sets it, rest reuse)
|
|
1589
1612
|
if (!this.#currentTurnMessageId) {
|
|
1590
1613
|
this.#currentTurnMessageId = crypto.randomUUID();
|
|
@@ -1866,6 +1889,25 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1866
1889
|
// hardcoded per-role branches had, now in one visible place.
|
|
1867
1890
|
if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
|
|
1868
1891
|
const filePath = String(toolInput.file_path || '');
|
|
1892
|
+
// Writer PROVENANCE REMINDER (Chesterton's Fence): on the writer's FIRST edit of
|
|
1893
|
+
// an EXISTING (version-controlled) file this turn, deny ONCE with a reminder to
|
|
1894
|
+
// review the file's git history so the change is backward-informed (root cause,
|
|
1895
|
+
// prior/reverted attempts, cross-component coupling), not forward-only — then it
|
|
1896
|
+
// proceeds. New files are exempt (no history). One-shot per turn so it can't loop.
|
|
1897
|
+
if (agentType === 'writer' && filePath && existsSync(filePath) && !writerHistoryReminded) {
|
|
1898
|
+
writerHistoryReminded = true;
|
|
1899
|
+
const base = filePath.split('/').pop() || filePath;
|
|
1900
|
+
const gateReason = [
|
|
1901
|
+
`Before editing ${base}, first review its history so your change is backward-informed, not forward-only (Chesterton's Fence — understand WHY this code exists before changing it):`,
|
|
1902
|
+
` 1. git log --follow -p -- "${filePath}" (recent commits + rationale)`,
|
|
1903
|
+
` 2. git blame the specific lines you're about to change (who/when/which commit)`,
|
|
1904
|
+
` 3. git log --oneline --grep=<feature> and git log --oneline -S"<symbol>" (related OR reverted work so you don't redo something already tried; note components co-changed in those commits)`,
|
|
1905
|
+
`Then re-attempt this edit. (One-time reminder this turn; new files are exempt.)`,
|
|
1906
|
+
].join('\n');
|
|
1907
|
+
console.log(`🏛️ Writer provenance reminder: nudging before ${base} (first edit of an existing file this turn)`);
|
|
1908
|
+
this.#eventEmitter.emit('tool_blocked', { name: toolName, reason: `Review ${base}'s git history first (one-time provenance reminder).` });
|
|
1909
|
+
return { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny' }, reason: gateReason };
|
|
1910
|
+
}
|
|
1869
1911
|
const { decision, reason } = decideWrite(agentType, filePath);
|
|
1870
1912
|
console.log(`🔎 Write gate: agent=${agentType ?? 'main'} path="${filePath || '(none)'}" → ${decision}`);
|
|
1871
1913
|
if (decision === 'deny') {
|
|
@@ -1937,6 +1979,8 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
1937
1979
|
try {
|
|
1938
1980
|
// Reset the per-turn tool call counter so the new turn starts fresh.
|
|
1939
1981
|
turnToolCallCount = 0;
|
|
1982
|
+
// Reset the writer provenance-reminder flag for the new turn.
|
|
1983
|
+
writerHistoryReminded = false;
|
|
1940
1984
|
const reminder = readFileSync(TURN_SHAPE_REMINDER_PATH, 'utf-8');
|
|
1941
1985
|
const promptPreview = String(input?.prompt || '').substring(0, 60).replace(/\n/g, ' ');
|
|
1942
1986
|
// Reliable recall: retrieve prior messages relevant to THIS prompt and inject
|
|
@@ -2391,7 +2435,9 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
2391
2435
|
this.#onCheckpoint(checkpointId);
|
|
2392
2436
|
}
|
|
2393
2437
|
// Stream text chunks — send each assistant text block to TTS
|
|
2394
|
-
|
|
2438
|
+
// SUBAGENT GUARD (see the persistent-consumer loop above): skip sub-agent messages
|
|
2439
|
+
// (non-null parent_tool_use_id) so only the main orchestrator's text speaks/streams.
|
|
2440
|
+
if (message.type === 'assistant' && message.message?.content && !message.parent_tool_use_id) {
|
|
2395
2441
|
// Emit SDK requestId on first assistant message — identifies this query()
|
|
2396
2442
|
// in the JSONL for tracking which research task produced which output
|
|
2397
2443
|
const sdkRequestId = message.requestId;
|