hebbian 0.8.0 → 0.8.2

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.
@@ -2169,6 +2169,7 @@ var init_hooks = __esm({
2169
2169
  var digest_exports = {};
2170
2170
  __export(digest_exports, {
2171
2171
  detectRetryPatterns: () => detectRetryPatterns,
2172
+ detectSoftFailure: () => detectSoftFailure,
2172
2173
  detectToolFailure: () => detectToolFailure,
2173
2174
  digestTranscript: () => digestTranscript,
2174
2175
  extractCorrections: () => extractCorrections,
@@ -2197,12 +2198,21 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2197
2198
  const resolvedSessionId = sessionId || basename(transcriptPath, ".jsonl");
2198
2199
  const logDir = join16(brainRoot, DIGEST_LOG_DIR);
2199
2200
  const logPath = join16(logDir, `${resolvedSessionId}.jsonl`);
2200
- if (existsSync15(logPath)) {
2201
+ const content = readFileSync8(transcriptPath, "utf8");
2202
+ const allLines = content.split("\n").filter(Boolean);
2203
+ const totalLines = allLines.length;
2204
+ const meta = readAuditMeta(logPath);
2205
+ if (existsSync15(logPath) && !meta) {
2201
2206
  console.log(`\u23ED already digested session ${resolvedSessionId}, skip`);
2202
2207
  return { corrections: 0, skipped: 0, toolFailures: 0, transcriptPath, sessionId: resolvedSessionId };
2203
2208
  }
2204
- const messages = parseTranscript(transcriptPath);
2205
- const toolFailures = parseToolResults(transcriptPath);
2209
+ const skipLines = meta ? meta.lineCount : 0;
2210
+ if (skipLines >= totalLines) {
2211
+ return { corrections: 0, skipped: 0, toolFailures: 0, transcriptPath, sessionId: resolvedSessionId };
2212
+ }
2213
+ const newLines = allLines.slice(skipLines);
2214
+ const messages = parseTranscriptFromLines(newLines);
2215
+ const toolFailures = parseToolResultsFromLines(newLines);
2206
2216
  for (const failure of toolFailures) {
2207
2217
  logEpisode(brainRoot, "tool-failure", failure.toolName, failure.errorText);
2208
2218
  }
@@ -2217,11 +2227,11 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2217
2227
  const corrections = extractCorrections(messages);
2218
2228
  if (corrections.length === 0 && toolFailures.length === 0) {
2219
2229
  console.log(`\u{1F4DD} digest: no corrections found in session ${resolvedSessionId}`);
2220
- writeAuditLog(brainRoot, resolvedSessionId, []);
2230
+ writeAuditLog(brainRoot, resolvedSessionId, [], totalLines);
2221
2231
  return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
2222
2232
  }
2223
2233
  if (corrections.length === 0) {
2224
- writeAuditLog(brainRoot, resolvedSessionId, []);
2234
+ writeAuditLog(brainRoot, resolvedSessionId, [], totalLines);
2225
2235
  return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
2226
2236
  }
2227
2237
  let applied = 0;
@@ -2237,7 +2247,7 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2237
2247
  auditEntries.push({ correction, applied: false });
2238
2248
  }
2239
2249
  }
2240
- writeAuditLog(brainRoot, resolvedSessionId, auditEntries);
2250
+ writeAuditLog(brainRoot, resolvedSessionId, auditEntries, totalLines);
2241
2251
  console.log(`\u{1F4DD} digest: ${applied} correction(s) from session ${resolvedSessionId}`);
2242
2252
  return {
2243
2253
  corrections: applied,
@@ -2247,9 +2257,7 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
2247
2257
  sessionId: resolvedSessionId
2248
2258
  };
2249
2259
  }
2250
- function parseTranscript(transcriptPath) {
2251
- const content = readFileSync8(transcriptPath, "utf8");
2252
- const lines = content.split("\n").filter(Boolean);
2260
+ function parseTranscriptFromLines(lines) {
2253
2261
  const messages = [];
2254
2262
  for (const line of lines) {
2255
2263
  let entry;
@@ -2276,7 +2284,9 @@ function extractText(content) {
2276
2284
  }
2277
2285
  function parseToolResults(transcriptPath) {
2278
2286
  const content = readFileSync8(transcriptPath, "utf8");
2279
- const lines = content.split("\n").filter(Boolean);
2287
+ return parseToolResultsFromLines(content.split("\n").filter(Boolean));
2288
+ }
2289
+ function parseToolResultsFromLines(lines) {
2280
2290
  const failures = [];
2281
2291
  for (const line of lines) {
2282
2292
  if (failures.length >= MAX_FAILURES_PER_SESSION) break;
@@ -2290,9 +2300,13 @@ function parseToolResults(transcriptPath) {
2290
2300
  if (!entry.message || !Array.isArray(entry.message.content)) continue;
2291
2301
  for (const block of entry.message.content) {
2292
2302
  if (block.type !== "tool_result") continue;
2293
- if (!block.is_error) continue;
2294
- const failure = detectToolFailure(block, entry.toolUseResult);
2295
- if (failure) failures.push(failure);
2303
+ if (block.is_error) {
2304
+ const failure = detectToolFailure(block, entry.toolUseResult);
2305
+ if (failure) failures.push(failure);
2306
+ } else {
2307
+ const failure = detectSoftFailure(block, entry.toolUseResult);
2308
+ if (failure) failures.push(failure);
2309
+ }
2296
2310
  }
2297
2311
  }
2298
2312
  return failures;
@@ -2331,6 +2345,30 @@ function detectToolFailure(block, toolUseResult) {
2331
2345
  const toolName = firstLine.trim().slice(0, 80);
2332
2346
  return { toolName, exitCode, errorText: errorText.slice(0, 500) };
2333
2347
  }
2348
+ function detectSoftFailure(block, toolUseResult) {
2349
+ let text = "";
2350
+ if (typeof block.content === "string") {
2351
+ text = block.content;
2352
+ } else if (Array.isArray(block.content)) {
2353
+ text = block.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("\n");
2354
+ }
2355
+ if (toolUseResult && typeof toolUseResult === "object") {
2356
+ if (toolUseResult.stderr) text += "\n" + toolUseResult.stderr;
2357
+ }
2358
+ if (!text) return null;
2359
+ for (const pattern of SOFT_ERROR_PATTERNS) {
2360
+ const match = text.match(pattern);
2361
+ if (match) {
2362
+ const matchedLine = text.split("\n").find((l) => pattern.test(l)) || "unknown";
2363
+ return {
2364
+ toolName: `[soft] ${matchedLine.trim().slice(0, 70)}`,
2365
+ exitCode: 0,
2366
+ errorText: text.slice(0, 500)
2367
+ };
2368
+ }
2369
+ }
2370
+ return null;
2371
+ }
2334
2372
  function extractCorrections(messages) {
2335
2373
  const corrections = [];
2336
2374
  for (const text of messages) {
@@ -2505,13 +2543,28 @@ function extractKeywords(text) {
2505
2543
  ]);
2506
2544
  return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9\u3000-\u9FFF\uAC00-\uD7AF]+/g, " ").toLowerCase().split(/\s+/).filter((t) => t.length > 2 && !STOP_WORDS.has(t));
2507
2545
  }
2508
- function writeAuditLog(brainRoot, sessionId, entries) {
2546
+ function readAuditMeta(logPath) {
2547
+ if (!existsSync15(logPath)) return null;
2548
+ try {
2549
+ const content = readFileSync8(logPath, "utf8");
2550
+ const firstLine = content.split("\n")[0];
2551
+ if (!firstLine) return null;
2552
+ const parsed = JSON.parse(firstLine);
2553
+ if (parsed._meta && typeof parsed.lineCount === "number") {
2554
+ return { lineCount: parsed.lineCount };
2555
+ }
2556
+ } catch {
2557
+ }
2558
+ return null;
2559
+ }
2560
+ function writeAuditLog(brainRoot, sessionId, entries, lineCount) {
2509
2561
  const logDir = join16(brainRoot, DIGEST_LOG_DIR);
2510
2562
  if (!existsSync15(logDir)) {
2511
2563
  mkdirSync10(logDir, { recursive: true });
2512
2564
  }
2513
2565
  const logPath = join16(logDir, `${sessionId}.jsonl`);
2514
- const lines = entries.map(
2566
+ const metaLine = JSON.stringify({ _meta: true, lineCount, ts: (/* @__PURE__ */ new Date()).toISOString() });
2567
+ const entryLines = entries.map(
2515
2568
  (e) => JSON.stringify({
2516
2569
  ts: (/* @__PURE__ */ new Date()).toISOString(),
2517
2570
  path: e.correction.path,
@@ -2521,9 +2574,9 @@ function writeAuditLog(brainRoot, sessionId, entries) {
2521
2574
  applied: e.applied
2522
2575
  })
2523
2576
  );
2524
- writeFileSync12(logPath, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf8");
2577
+ writeFileSync12(logPath, [metaLine, ...entryLines].join("\n") + "\n", "utf8");
2525
2578
  }
2526
- var NEGATION_PATTERNS, AFFIRMATION_PATTERNS, MUST_PATTERNS, WARN_PATTERNS, MAX_FAILURES_PER_SESSION;
2579
+ var NEGATION_PATTERNS, AFFIRMATION_PATTERNS, MUST_PATTERNS, WARN_PATTERNS, MAX_FAILURES_PER_SESSION, SOFT_ERROR_PATTERNS;
2527
2580
  var init_digest = __esm({
2528
2581
  "src/digest.ts"() {
2529
2582
  "use strict";
@@ -2570,6 +2623,14 @@ var init_digest = __esm({
2570
2623
  /주의/
2571
2624
  ];
2572
2625
  MAX_FAILURES_PER_SESSION = 20;
2626
+ SOFT_ERROR_PATTERNS = [
2627
+ /(?:^|\n)\S*(?:\(\w+\):\d+: )?command not found:/m,
2628
+ // shell: command not found
2629
+ /(?:^|\n)npm error\b/m,
2630
+ // npm error (not npm warn)
2631
+ /(?:^|\n)fatal: /m
2632
+ // git fatal
2633
+ ];
2573
2634
  }
2574
2635
  });
2575
2636
 
@@ -3534,7 +3595,17 @@ var init_feedback = __esm({
3534
3595
  init_constants();
3535
3596
  import { parseArgs } from "util";
3536
3597
  import { resolve as resolve3 } from "path";
3537
- var VERSION = "0.8.0";
3598
+ import { createRequire } from "module";
3599
+ var _require = createRequire(import.meta.url);
3600
+ var VERSION = (() => {
3601
+ for (const rel of ["../package.json", "../../package.json"]) {
3602
+ try {
3603
+ return _require(rel).version;
3604
+ } catch {
3605
+ }
3606
+ }
3607
+ return "0.0.0";
3608
+ })();
3538
3609
  var HELP = `
3539
3610
  hebbian v${VERSION} \u2014 Folder-as-neuron brain for any AI agent.
3540
3611