hebbian 0.5.3 → 0.6.0

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.
@@ -2065,8 +2065,10 @@ var init_hooks = __esm({
2065
2065
  // src/digest.ts
2066
2066
  var digest_exports = {};
2067
2067
  __export(digest_exports, {
2068
+ detectToolFailure: () => detectToolFailure,
2068
2069
  digestTranscript: () => digestTranscript,
2069
2070
  extractCorrections: () => extractCorrections,
2071
+ parseToolResults: () => parseToolResults,
2070
2072
  readHookInput: () => readHookInput
2071
2073
  });
2072
2074
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync10 } from "fs";
@@ -2093,14 +2095,25 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2093
2095
  const logPath = join16(logDir, `${resolvedSessionId}.jsonl`);
2094
2096
  if (existsSync15(logPath)) {
2095
2097
  console.log(`\u23ED already digested session ${resolvedSessionId}, skip`);
2096
- return { corrections: 0, skipped: 0, transcriptPath, sessionId: resolvedSessionId };
2098
+ return { corrections: 0, skipped: 0, toolFailures: 0, transcriptPath, sessionId: resolvedSessionId };
2097
2099
  }
2098
2100
  const messages = parseTranscript(transcriptPath);
2101
+ const toolFailures = parseToolResults(transcriptPath);
2102
+ for (const failure of toolFailures) {
2103
+ logEpisode(brainRoot, "tool-failure", failure.toolName, failure.errorText);
2104
+ }
2105
+ if (toolFailures.length > 0) {
2106
+ console.log(`\u{1F527} digest: ${toolFailures.length} tool failure(s) logged as episodes`);
2107
+ }
2099
2108
  const corrections = extractCorrections(messages);
2100
- if (corrections.length === 0) {
2109
+ if (corrections.length === 0 && toolFailures.length === 0) {
2101
2110
  console.log(`\u{1F4DD} digest: no corrections found in session ${resolvedSessionId}`);
2102
2111
  writeAuditLog(brainRoot, resolvedSessionId, []);
2103
- return { corrections: 0, skipped: messages.length, transcriptPath, sessionId: resolvedSessionId };
2112
+ return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
2113
+ }
2114
+ if (corrections.length === 0) {
2115
+ writeAuditLog(brainRoot, resolvedSessionId, []);
2116
+ return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
2104
2117
  }
2105
2118
  let applied = 0;
2106
2119
  const auditEntries = [];
@@ -2120,6 +2133,7 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2120
2133
  return {
2121
2134
  corrections: applied,
2122
2135
  skipped: messages.length - corrections.length,
2136
+ toolFailures: toolFailures.length,
2123
2137
  transcriptPath,
2124
2138
  sessionId: resolvedSessionId
2125
2139
  };
@@ -2151,6 +2165,47 @@ function extractText(content) {
2151
2165
  }
2152
2166
  return null;
2153
2167
  }
2168
+ function parseToolResults(transcriptPath) {
2169
+ const content = readFileSync7(transcriptPath, "utf8");
2170
+ const lines = content.split("\n").filter(Boolean);
2171
+ const failures = [];
2172
+ for (const line of lines) {
2173
+ if (failures.length >= MAX_FAILURES_PER_SESSION) break;
2174
+ let entry;
2175
+ try {
2176
+ entry = JSON.parse(line);
2177
+ } catch {
2178
+ continue;
2179
+ }
2180
+ if (entry.type !== "user") continue;
2181
+ if (!entry.message || !Array.isArray(entry.message.content)) continue;
2182
+ for (const block of entry.message.content) {
2183
+ if (block.type !== "tool_result") continue;
2184
+ if (!block.is_error) continue;
2185
+ const failure = detectToolFailure(block, entry.toolUseResult);
2186
+ if (failure) failures.push(failure);
2187
+ }
2188
+ }
2189
+ return failures;
2190
+ }
2191
+ function detectToolFailure(block, toolUseResult) {
2192
+ if (!block.is_error) return null;
2193
+ let errorText = "";
2194
+ if (typeof block.content === "string") {
2195
+ errorText = block.content;
2196
+ } else if (Array.isArray(block.content)) {
2197
+ errorText = block.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
2198
+ }
2199
+ if (!errorText && typeof toolUseResult === "string") {
2200
+ errorText = toolUseResult;
2201
+ }
2202
+ if (!errorText) return null;
2203
+ const exitMatch = errorText.match(/^Exit code (\d+)/);
2204
+ const exitCode = exitMatch ? parseInt(exitMatch[1], 10) : 1;
2205
+ const firstLine = errorText.split("\n").find((l) => l.trim() && !l.startsWith("Exit code")) || "unknown";
2206
+ const toolName = firstLine.trim().slice(0, 80);
2207
+ return { toolName, exitCode, errorText: errorText.slice(0, 500) };
2208
+ }
2154
2209
  function extractCorrections(messages) {
2155
2210
  const corrections = [];
2156
2211
  for (const text of messages) {
@@ -2343,7 +2398,7 @@ function writeAuditLog(brainRoot, sessionId, entries) {
2343
2398
  );
2344
2399
  writeFileSync12(logPath, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf8");
2345
2400
  }
2346
- var NEGATION_PATTERNS, AFFIRMATION_PATTERNS, MUST_PATTERNS, WARN_PATTERNS;
2401
+ var NEGATION_PATTERNS, AFFIRMATION_PATTERNS, MUST_PATTERNS, WARN_PATTERNS, MAX_FAILURES_PER_SESSION;
2347
2402
  var init_digest = __esm({
2348
2403
  "src/digest.ts"() {
2349
2404
  "use strict";
@@ -2389,6 +2444,7 @@ var init_digest = __esm({
2389
2444
  // Korean
2390
2445
  /주의/
2391
2446
  ];
2447
+ MAX_FAILURES_PER_SESSION = 20;
2392
2448
  }
2393
2449
  });
2394
2450
 
@@ -3060,7 +3116,7 @@ var init_doctor = __esm({
3060
3116
  init_constants();
3061
3117
  import { parseArgs } from "util";
3062
3118
  import { resolve as resolve3 } from "path";
3063
- var VERSION = "0.5.3";
3119
+ var VERSION = "0.6.0";
3064
3120
  var HELP = `
3065
3121
  hebbian v${VERSION} \u2014 Folder-as-neuron brain for any AI agent.
3066
3122