osborn 0.9.234 → 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.
Files changed (2) hide show
  1. package/dist/claude-llm.js +37 -0
  2. package/package.json +1 -1
@@ -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?"',
@@ -1873,6 +1889,25 @@ class ClaudeLLMStream extends llm.LLMStream {
1873
1889
  // hardcoded per-role branches had, now in one visible place.
1874
1890
  if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
1875
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
+ }
1876
1911
  const { decision, reason } = decideWrite(agentType, filePath);
1877
1912
  console.log(`🔎 Write gate: agent=${agentType ?? 'main'} path="${filePath || '(none)'}" → ${decision}`);
1878
1913
  if (decision === 'deny') {
@@ -1944,6 +1979,8 @@ class ClaudeLLMStream extends llm.LLMStream {
1944
1979
  try {
1945
1980
  // Reset the per-turn tool call counter so the new turn starts fresh.
1946
1981
  turnToolCallCount = 0;
1982
+ // Reset the writer provenance-reminder flag for the new turn.
1983
+ writerHistoryReminded = false;
1947
1984
  const reminder = readFileSync(TURN_SHAPE_REMINDER_PATH, 'utf-8');
1948
1985
  const promptPreview = String(input?.prompt || '').substring(0, 60).replace(/\n/g, ' ');
1949
1986
  // Reliable recall: retrieve prior messages relevant to THIS prompt and inject
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.234",
3
+ "version": "0.9.235",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {