hebbian 0.8.0 → 0.8.1
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/bin/hebbian.js +78 -17
- package/dist/bin/hebbian.js.map +1 -1
- package/dist/digest.d.ts +13 -1
- package/dist/digest.d.ts.map +1 -1
- package/dist/index.js +76 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/hebbian.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
2205
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
2294
|
-
|
|
2295
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
|