@fusengine/harness 0.1.35 → 0.1.36
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/cli/bin.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { r as resolveTtlSec } from "../ttl-BG55s6HZ.mjs";
|
|
|
3
3
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
4
4
|
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-CUL70W0k.mjs";
|
|
5
5
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { Dt as claudeHome, Et as todayUtc, t as handleHook } from "../handle-DL2sZiWH.mjs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { homedir } from "node:os";
|
|
@@ -107,7 +107,8 @@ if (cmd === "hook") {
|
|
|
107
107
|
"changelog",
|
|
108
108
|
"aipilot",
|
|
109
109
|
"lessons",
|
|
110
|
-
"seo"
|
|
110
|
+
"seo",
|
|
111
|
+
"memory"
|
|
111
112
|
])).has(scopeArg) ? scopeArg : "core";
|
|
112
113
|
const outcome = await handleHook(id, await readStdin(), {
|
|
113
114
|
now: Date.now(),
|
|
@@ -1297,7 +1297,7 @@ function trackSessionChanges(sessionIdRaw, filePath, home = homedir(), now = Dat
|
|
|
1297
1297
|
//#endregion
|
|
1298
1298
|
//#region src/runtime/lifecycle/post-edit-ts.ts
|
|
1299
1299
|
const TS_EXT$1 = /\.(ts|tsx)$/;
|
|
1300
|
-
const TIMEOUT_MS$
|
|
1300
|
+
const TIMEOUT_MS$2 = 1e4;
|
|
1301
1301
|
/** True when `bin` is resolvable on PATH (mirrors shutil.which). */
|
|
1302
1302
|
function hasBin(bin) {
|
|
1303
1303
|
try {
|
|
@@ -1318,7 +1318,7 @@ function run(bin, args) {
|
|
|
1318
1318
|
out: execFileSync(bin, args, {
|
|
1319
1319
|
encoding: "utf-8",
|
|
1320
1320
|
stdio: "pipe",
|
|
1321
|
-
timeout: TIMEOUT_MS$
|
|
1321
|
+
timeout: TIMEOUT_MS$2
|
|
1322
1322
|
})
|
|
1323
1323
|
};
|
|
1324
1324
|
} catch (err) {
|
|
@@ -3204,6 +3204,260 @@ function securityAdvisory(tool, filePath, now = Date.now(), home = homedir()) {
|
|
|
3204
3204
|
} });
|
|
3205
3205
|
}
|
|
3206
3206
|
//#endregion
|
|
3207
|
+
//#region src/runtime/lifecycle/memory/client.ts
|
|
3208
|
+
/**
|
|
3209
|
+
* Graphiti neural-memory HTTP client (best-effort). Ports the urllib calls in
|
|
3210
|
+
* the memory-neural scripts: POST /episodes (store) + POST /search (recall).
|
|
3211
|
+
* Every call swallows network errors and honors a 5s timeout, so a hook never
|
|
3212
|
+
* fails or hangs when the Graphiti server is absent.
|
|
3213
|
+
*/
|
|
3214
|
+
const TIMEOUT_MS$1 = 5e3;
|
|
3215
|
+
/** Base URL `http://<NEURAL_MEMORY_HOST>:<GRAPHITI_PORT>` (env-overridable). */
|
|
3216
|
+
function neuralBase(env = process.env) {
|
|
3217
|
+
return `http://${env.NEURAL_MEMORY_HOST ?? "localhost"}:${env.GRAPHITI_PORT ?? "8000"}`;
|
|
3218
|
+
}
|
|
3219
|
+
/** POST an episode to Graphiti `/episodes`. Resolves silently on any failure. */
|
|
3220
|
+
async function postEpisode(ep, env = process.env) {
|
|
3221
|
+
try {
|
|
3222
|
+
await fetch(`${neuralBase(env)}/episodes`, {
|
|
3223
|
+
method: "POST",
|
|
3224
|
+
headers: { "Content-Type": "application/json" },
|
|
3225
|
+
body: JSON.stringify(ep),
|
|
3226
|
+
signal: AbortSignal.timeout(TIMEOUT_MS$1)
|
|
3227
|
+
});
|
|
3228
|
+
} catch {}
|
|
3229
|
+
}
|
|
3230
|
+
/**
|
|
3231
|
+
* POST a query to Graphiti `/search`; returns hits or `[]` on any failure
|
|
3232
|
+
* (network error, timeout, non-2xx, malformed JSON).
|
|
3233
|
+
* @param query - The search query.
|
|
3234
|
+
* @param numResults - Max results requested.
|
|
3235
|
+
* @param env - Env (for host/port overrides).
|
|
3236
|
+
* @returns The recall hits, possibly empty.
|
|
3237
|
+
*/
|
|
3238
|
+
async function searchMemory(query, numResults, env = process.env) {
|
|
3239
|
+
try {
|
|
3240
|
+
const resp = await fetch(`${neuralBase(env)}/search`, {
|
|
3241
|
+
method: "POST",
|
|
3242
|
+
headers: { "Content-Type": "application/json" },
|
|
3243
|
+
body: JSON.stringify({
|
|
3244
|
+
query,
|
|
3245
|
+
num_results: numResults
|
|
3246
|
+
}),
|
|
3247
|
+
signal: AbortSignal.timeout(TIMEOUT_MS$1)
|
|
3248
|
+
});
|
|
3249
|
+
if (!resp.ok) return [];
|
|
3250
|
+
const data = await resp.json();
|
|
3251
|
+
return Array.isArray(data.results) ? data.results : [];
|
|
3252
|
+
} catch {
|
|
3253
|
+
return [];
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
/** Severity (1-10) of a Bash stderr by keyword (mirrors auto-capture-error). */
|
|
3257
|
+
function bashSeverity(stderr) {
|
|
3258
|
+
const s = stderr.toLowerCase();
|
|
3259
|
+
if (s.includes("fatal") || s.includes("panic")) return 10;
|
|
3260
|
+
if (s.includes("error") || s.includes("failed")) return 8;
|
|
3261
|
+
if (s.includes("warning")) return 4;
|
|
3262
|
+
if (s.includes("deprecated")) return 2;
|
|
3263
|
+
return 5;
|
|
3264
|
+
}
|
|
3265
|
+
/** Severity (1-10) of a finished agent by name (mirrors capture-agent-lesson). */
|
|
3266
|
+
function agentSeverity(name) {
|
|
3267
|
+
if (name === "sniper" || name === "sniper-faster") return 8;
|
|
3268
|
+
if (name === "research-expert") return 6;
|
|
3269
|
+
if (name.endsWith("-expert")) return 7;
|
|
3270
|
+
return 5;
|
|
3271
|
+
}
|
|
3272
|
+
/** Salience from severity: 0.40·sev/10 + 0.30 + 0.20·0.5 + 0.10·0.5. */
|
|
3273
|
+
function salience(severity) {
|
|
3274
|
+
return .4 * severity / 10 + .3 + .2 * .5 + .1 * .5;
|
|
3275
|
+
}
|
|
3276
|
+
//#endregion
|
|
3277
|
+
//#region src/runtime/lifecycle/memory/state.ts
|
|
3278
|
+
/**
|
|
3279
|
+
* fuse-memory-neural scope state: per-line logs under
|
|
3280
|
+
* `~/.claude/logs/00-memory` + project-type detection. Ports the shared
|
|
3281
|
+
* filesystem helpers of the four memory-neural scripts.
|
|
3282
|
+
*/
|
|
3283
|
+
/** `~/.claude/logs/00-memory` log directory. */
|
|
3284
|
+
function memoryLogDir(home = homedir()) {
|
|
3285
|
+
return join(claudeHome(home), "logs", "00-memory");
|
|
3286
|
+
}
|
|
3287
|
+
/**
|
|
3288
|
+
* Append a line to a memory log file, creating the dir. When `rotateAt > 0` and
|
|
3289
|
+
* the file exceeds it, keep only the newest `keep` lines. Best-effort (errors
|
|
3290
|
+
* swallowed), so a hook never fails on a logging issue.
|
|
3291
|
+
* @param name - Log file name (e.g. `operations.log`).
|
|
3292
|
+
* @param line - Line to append (newline added).
|
|
3293
|
+
* @param rotateAt - Rotate when line count exceeds this (0 disables).
|
|
3294
|
+
* @param keep - Lines to keep on rotation.
|
|
3295
|
+
* @param home - Home dir.
|
|
3296
|
+
*/
|
|
3297
|
+
function appendMemoryLog(name, line, rotateAt = 0, keep = 0, home = homedir()) {
|
|
3298
|
+
const dir = memoryLogDir(home);
|
|
3299
|
+
try {
|
|
3300
|
+
mkdirSync(dir, { recursive: true });
|
|
3301
|
+
const file = join(dir, name);
|
|
3302
|
+
appendFileSync(file, `${line}\n`, "utf-8");
|
|
3303
|
+
if (rotateAt > 0) {
|
|
3304
|
+
const lines = readFileSync(file, "utf-8").split("\n").filter((l) => l.length > 0);
|
|
3305
|
+
if (lines.length > rotateAt) writeFileSync(file, `${lines.slice(-keep).join("\n")}\n`, "utf-8");
|
|
3306
|
+
}
|
|
3307
|
+
} catch {}
|
|
3308
|
+
}
|
|
3309
|
+
/** Detect the project type from cwd markers (mirrors recall-on-session.py). */
|
|
3310
|
+
function detectProjectType(cwd) {
|
|
3311
|
+
for (const [f, t] of [
|
|
3312
|
+
["package.json", "node"],
|
|
3313
|
+
["composer.json", "php"],
|
|
3314
|
+
["Package.swift", "swift"],
|
|
3315
|
+
["Cargo.toml", "rust"],
|
|
3316
|
+
["go.mod", "go"]
|
|
3317
|
+
]) if (existsSync(join(cwd, f))) return t;
|
|
3318
|
+
if (existsSync(join(cwd, "requirements.txt")) || existsSync(join(cwd, "pyproject.toml"))) return "python";
|
|
3319
|
+
return "unknown";
|
|
3320
|
+
}
|
|
3321
|
+
//#endregion
|
|
3322
|
+
//#region src/runtime/lifecycle/memory/agent-lesson.ts
|
|
3323
|
+
/**
|
|
3324
|
+
* SubagentStop memory handler. Ports `capture-agent-lesson.py`: log a finished
|
|
3325
|
+
* agent's conclusion and, when salient enough, store it as a Graphiti episode.
|
|
3326
|
+
* Skips explore-codebase/websearch agents and errored exits.
|
|
3327
|
+
*/
|
|
3328
|
+
/** Agents whose conclusions are never captured. */
|
|
3329
|
+
const SKIP = /* @__PURE__ */ new Set(["explore-codebase", "websearch"]);
|
|
3330
|
+
/**
|
|
3331
|
+
* Handle SubagentStop: log + maybe store the agent's conclusion. Side-effect
|
|
3332
|
+
* only (no stdout).
|
|
3333
|
+
* @param payload - The raw hook payload.
|
|
3334
|
+
* @param now - Clock.
|
|
3335
|
+
*/
|
|
3336
|
+
async function captureAgentLesson(payload, now) {
|
|
3337
|
+
const name = typeof payload.agent_name === "string" ? payload.agent_name : "unknown";
|
|
3338
|
+
const lastMsg = typeof payload.last_assistant_message === "string" ? payload.last_assistant_message : "";
|
|
3339
|
+
const exitReason = typeof payload.exit_reason === "string" ? payload.exit_reason : "unknown";
|
|
3340
|
+
if (!lastMsg || exitReason === "error" || SKIP.has(name)) return;
|
|
3341
|
+
const lesson = lastMsg.slice(0, 1e3);
|
|
3342
|
+
const ts = isoUtc(now);
|
|
3343
|
+
appendMemoryLog("agent-lessons.log", `[${ts}] ${name} | ${exitReason} | ${lesson.slice(0, 80)}...`);
|
|
3344
|
+
if (salience(agentSeverity(name)) <= .3) return;
|
|
3345
|
+
await postEpisode({
|
|
3346
|
+
name: "agent_lesson",
|
|
3347
|
+
episode_body: `Agent ${name} conclusion: ${lesson}`,
|
|
3348
|
+
source_description: `agent-stop-${name}`,
|
|
3349
|
+
reference_time: ts
|
|
3350
|
+
});
|
|
3351
|
+
}
|
|
3352
|
+
//#endregion
|
|
3353
|
+
//#region src/runtime/lifecycle/memory/capture-error.ts
|
|
3354
|
+
/**
|
|
3355
|
+
* PostToolUse (Bash) memory handler. Ports `auto-capture-error.py`: on a
|
|
3356
|
+
* non-zero Bash exit with stderr, store an episode in Graphiti and surface a
|
|
3357
|
+
* `<memory-capture>` hint to search past errors / store the eventual solution.
|
|
3358
|
+
*/
|
|
3359
|
+
/** Extract exit code + stderr from a PostToolUse Bash payload (either field). */
|
|
3360
|
+
function bashResult(payload) {
|
|
3361
|
+
const r = payload.tool_result ?? payload.tool_response;
|
|
3362
|
+
return {
|
|
3363
|
+
exit: String(r?.exit_code ?? "0"),
|
|
3364
|
+
stderr: typeof r?.stderr === "string" ? r.stderr : ""
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* Handle a Bash PostToolUse: capture a failed command's error in neural memory
|
|
3369
|
+
* and return the native additionalContext stdout (or "" when nothing to emit).
|
|
3370
|
+
* @param payload - The raw hook payload.
|
|
3371
|
+
* @param now - Clock.
|
|
3372
|
+
* @returns The native stdout (possibly empty).
|
|
3373
|
+
*/
|
|
3374
|
+
async function captureBashError(payload, now) {
|
|
3375
|
+
const { exit, stderr } = bashResult(payload);
|
|
3376
|
+
if (exit === "0" || !stderr) return "";
|
|
3377
|
+
if (salience(bashSeverity(stderr)) <= .3) return "";
|
|
3378
|
+
const errorMsg = stderr.slice(0, 500);
|
|
3379
|
+
await postEpisode({
|
|
3380
|
+
name: "bash_error",
|
|
3381
|
+
episode_body: `Bash error (exit ${exit}): ${errorMsg}`,
|
|
3382
|
+
source_description: "auto-capture",
|
|
3383
|
+
reference_time: isoUtc(now)
|
|
3384
|
+
});
|
|
3385
|
+
return contextResponse("PostToolUse", `Error captured in neural memory (Graphiti).\nSearch for similar past errors: use mcp__qdrant__qdrant-find with query "${errorMsg}"\nIf you solve this, store the solution: use mcp__qdrant__qdrant-store`);
|
|
3386
|
+
}
|
|
3387
|
+
//#endregion
|
|
3388
|
+
//#region src/runtime/lifecycle/memory/track-ops.ts
|
|
3389
|
+
/**
|
|
3390
|
+
* PostToolUse (mcp__graphiti|mcp__qdrant) memory handler. Ports
|
|
3391
|
+
* `track-memory-ops.py`: append `[ts] <tool> | ok|error` to
|
|
3392
|
+
* `operations.log`, rotating at 1000 lines (keeping the newest 500).
|
|
3393
|
+
*/
|
|
3394
|
+
/** Append a memory-operation log line for a graphiti/qdrant tool call. */
|
|
3395
|
+
function trackMemoryOp(payload, now) {
|
|
3396
|
+
const tool = typeof payload.tool_name === "string" ? payload.tool_name : "unknown";
|
|
3397
|
+
const status = (payload.tool_result ?? payload.tool_response)?.error ? "error" : "ok";
|
|
3398
|
+
appendMemoryLog("operations.log", `[${isoUtc(now)}] ${tool} | ${status}`, 1e3, 500);
|
|
3399
|
+
}
|
|
3400
|
+
//#endregion
|
|
3401
|
+
//#region src/runtime/lifecycle/memory/recall.ts
|
|
3402
|
+
/**
|
|
3403
|
+
* SessionStart memory handler. Ports `recall-on-session.py`: detect the project
|
|
3404
|
+
* type, recall relevant lessons from Graphiti, log the recall, and inject a
|
|
3405
|
+
* neural-memory-recall additionalContext block.
|
|
3406
|
+
*/
|
|
3407
|
+
/**
|
|
3408
|
+
* Handle SessionStart: recall past lessons for this project and return the
|
|
3409
|
+
* native additionalContext stdout (or "" when there is nothing to recall).
|
|
3410
|
+
* @param cwd - Project root.
|
|
3411
|
+
* @param now - Clock.
|
|
3412
|
+
* @returns The native stdout (possibly empty).
|
|
3413
|
+
*/
|
|
3414
|
+
async function recallOnSession(cwd, now) {
|
|
3415
|
+
const projectType = detectProjectType(cwd);
|
|
3416
|
+
const projectName = basename(cwd);
|
|
3417
|
+
const hits = await searchMemory(`${projectType} ${projectName} common errors`, 5);
|
|
3418
|
+
appendMemoryLog("recalls.log", `[${isoUtc(now)}] session_recall | ${projectType} | ${projectName}`);
|
|
3419
|
+
if (hits.length === 0) return "";
|
|
3420
|
+
return contextResponse("SessionStart", `Relevant lessons from past sessions:\n${hits.slice(0, 5).map((r) => `- ${r.content || r.name || "unknown"}`).join("\n")}\nFor deeper search: use mcp__qdrant__qdrant-find with project-specific queries.`);
|
|
3421
|
+
}
|
|
3422
|
+
//#endregion
|
|
3423
|
+
//#region src/runtime/lifecycle/memory/dispatch.ts
|
|
3424
|
+
/**
|
|
3425
|
+
* fuse-memory-neural scope dispatcher (async; the handlers hit Graphiti over
|
|
3426
|
+
* HTTP, best-effort). Routes by event: SessionStart recalls past lessons,
|
|
3427
|
+
* PostToolUse captures Bash errors / tracks graphiti+qdrant ops, SubagentStop
|
|
3428
|
+
* captures agent conclusions. Returns the native stdout when handled, or `null`
|
|
3429
|
+
* to fall through to the generic pipeline.
|
|
3430
|
+
*/
|
|
3431
|
+
/** Is this a graphiti/qdrant MCP tool call? */
|
|
3432
|
+
function isMemoryTool(tool) {
|
|
3433
|
+
return tool.startsWith("mcp__graphiti") || tool.startsWith("mcp__qdrant");
|
|
3434
|
+
}
|
|
3435
|
+
/**
|
|
3436
|
+
* Dispatch a memory-scope lifecycle event to its ported handler.
|
|
3437
|
+
* @param event - Raw hook event name.
|
|
3438
|
+
* @param payload - Raw hook payload.
|
|
3439
|
+
* @param cwd - Project root.
|
|
3440
|
+
* @param now - Clock.
|
|
3441
|
+
* @returns The native stdout, or `null` when unhandled.
|
|
3442
|
+
*/
|
|
3443
|
+
async function dispatchMemory(event, payload, cwd, now) {
|
|
3444
|
+
if (event === "SessionStart") return recallOnSession(cwd, now);
|
|
3445
|
+
if (event === "SubagentStop") {
|
|
3446
|
+
await captureAgentLesson(payload, now);
|
|
3447
|
+
return "";
|
|
3448
|
+
}
|
|
3449
|
+
if (event === "PostToolUse") {
|
|
3450
|
+
const tool = typeof payload.tool_name === "string" ? payload.tool_name : "";
|
|
3451
|
+
if (tool === "Bash") return captureBashError(payload, now);
|
|
3452
|
+
if (isMemoryTool(tool)) {
|
|
3453
|
+
trackMemoryOp(payload, now);
|
|
3454
|
+
return "";
|
|
3455
|
+
}
|
|
3456
|
+
return null;
|
|
3457
|
+
}
|
|
3458
|
+
return null;
|
|
3459
|
+
}
|
|
3460
|
+
//#endregion
|
|
3207
3461
|
//#region src/runtime/lifecycle/seo/post-tool-use.ts
|
|
3208
3462
|
/**
|
|
3209
3463
|
* SEO PostToolUse handler (fs effects). Ports `seo/hooks/validate-seo.ts`: on an
|
|
@@ -3819,6 +4073,27 @@ async function handlePre(ctx) {
|
|
|
3819
4073
|
};
|
|
3820
4074
|
}
|
|
3821
4075
|
//#endregion
|
|
4076
|
+
//#region src/runtime/handle-scope-async.ts
|
|
4077
|
+
/**
|
|
4078
|
+
* Pre-pipeline async scope interception for {@link handleHook}. The aipilot and
|
|
4079
|
+
* memory scopes reach external resources (cache files / Graphiti HTTP) and may
|
|
4080
|
+
* emit stdout for lifecycle events, so they run before the sync gate pipeline.
|
|
4081
|
+
*/
|
|
4082
|
+
/**
|
|
4083
|
+
* Run the async per-scope dispatcher for the invoking scope, if any.
|
|
4084
|
+
* @param scope - The invoking plugin scope.
|
|
4085
|
+
* @param event - The raw hook event name.
|
|
4086
|
+
* @param payload - The raw hook payload.
|
|
4087
|
+
* @param cwd - Project root.
|
|
4088
|
+
* @param now - Clock.
|
|
4089
|
+
* @returns The native stdout when intercepted, or `null` to fall through.
|
|
4090
|
+
*/
|
|
4091
|
+
async function asyncScopeStdout(scope, event, payload, cwd, now) {
|
|
4092
|
+
if (scope === "aipilot") return dispatchAipilot(event, payload, cwd, now);
|
|
4093
|
+
if (scope === "memory") return dispatchMemory(event, payload, cwd, now);
|
|
4094
|
+
return null;
|
|
4095
|
+
}
|
|
4096
|
+
//#endregion
|
|
3822
4097
|
//#region src/runtime/handle.ts
|
|
3823
4098
|
/** Raw Claude hook event name from a payload (empty when absent). */
|
|
3824
4099
|
function rawEventName(payload) {
|
|
@@ -3840,13 +4115,11 @@ async function handleHook(id, payload, opts) {
|
|
|
3840
4115
|
stdout: "",
|
|
3841
4116
|
exit: 0
|
|
3842
4117
|
};
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
};
|
|
3849
|
-
}
|
|
4118
|
+
const asyncOut = await asyncScopeStdout(opts.scope, rawEventName(payload), payload, opts.cwd, opts.now);
|
|
4119
|
+
if (asyncOut !== null) return {
|
|
4120
|
+
stdout: asyncOut,
|
|
4121
|
+
exit: 0
|
|
4122
|
+
};
|
|
3850
4123
|
const life = lifecycleStdout(payload, opts.cwd, opts.scope ?? "core", opts.now);
|
|
3851
4124
|
if (life !== null) return {
|
|
3852
4125
|
stdout: life,
|
|
@@ -3903,4 +4176,4 @@ async function handleHook(id, payload, opts) {
|
|
|
3903
4176
|
});
|
|
3904
4177
|
}
|
|
3905
4178
|
//#endregion
|
|
3906
|
-
export {
|
|
4179
|
+
export { sessionStartCore as $, writePluginMap as A, sanitizeSessionId as At, trackSessionChanges as B, aipilotPostToolUse as C, saveSecurityState as Ct, lessonsStateFileFor as D, claudeHome as Dt, lessonsFileFor as E, todayUtc as Et, mergeLines as F, validateTeammateOutput as G, cleanupSession as H, countFiles as I, detectSolidProfile as J, trackAgentMemory as K, getFileDesc as L, isProject as M, sessionStatePath as Mt, writeTree as N, sessionsDir as Nt, cartoSessionStart as O, fusengineCache as Ot, loadEnriched as P, runSessionStartCleanups as Q, listChildren as R, dispatchLifecycle as S, loadSecurityState as St, dispatchLessons as T, securityStatePath as Tt, saveApexState as U, validateRulesLoaded as V, logToolFailure as W, injectRules as X, solidDetectStart as Y, readRules as Z, postTrackingSideEffects as _, mcpPostStore as _t, TRIVIAL_BUDGET as a, gitContext as at, trackSkillRead as b, activityFor as bt, detectDuplication as c, taskContext as ct, lifecycleStdout as d, defaultStateDir as dt, pruneEmptyDirs as et, postEditContext as f, projectHash$1 as ft, securityAdvisory as g, isMcpTool as gt, dispatchMemory as h, MCP_TTL_MS as ht, REQUIRED_AGENTS as i, devContext as it, generateProjectMap as j, saveSessionState as jt, generateEcosystemMap as k, loadSessionState as kt, dryGate as l, respond as lt, seoPostToolUseResponse as m, normalizeEvent as mt, handlePre as n, removeOldFiles as nt, gate as o, projectContext as ot, seoPostToolUse as p, trackFile as pt, subagentCacheContext as q, DEFAULT_WINDOW_MS as r, trimLogFile as rt, preCommitGate as s, promptSubmitContext as st, handleHook as t, purgeTtlTree as tt, extractSymbols as u, recordActivity as ut, trackWatchResearch as v, mcpPreIntercept as vt, dispatchAipilot as w, securityStateDir as wt, trackEnrichment as x, isoUtc as xt, trackMcpResearch as y, queryOf as yt, postEditTypescript as z };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -393,7 +393,7 @@ declare function aipilotPostToolUse(payload: Record<string, unknown>, cwd: strin
|
|
|
393
393
|
//#endregion
|
|
394
394
|
//#region src/runtime/lifecycle/dispatch.d.ts
|
|
395
395
|
/** Which plugin's hooks.json invoked the harness (selects SessionStart behavior). */
|
|
396
|
-
type PluginScope = "core" | "solid" | "rules" | "carto" | "security" | "changelog" | "aipilot" | "lessons" | "seo";
|
|
396
|
+
type PluginScope = "core" | "solid" | "rules" | "carto" | "security" | "changelog" | "aipilot" | "lessons" | "seo" | "memory";
|
|
397
397
|
/** Inputs the lifecycle dispatcher needs (clock + roots injected). */
|
|
398
398
|
interface LifecycleInput {
|
|
399
399
|
event: string;
|
|
@@ -589,6 +589,17 @@ declare function lessonsFileFor(root: string): string;
|
|
|
589
589
|
/** Absolute `<root>/MEMORY/state.json` — machine-local throttle counter. */
|
|
590
590
|
declare function lessonsStateFileFor(root: string): string;
|
|
591
591
|
//#endregion
|
|
592
|
+
//#region src/runtime/lifecycle/memory/dispatch.d.ts
|
|
593
|
+
/**
|
|
594
|
+
* Dispatch a memory-scope lifecycle event to its ported handler.
|
|
595
|
+
* @param event - Raw hook event name.
|
|
596
|
+
* @param payload - Raw hook payload.
|
|
597
|
+
* @param cwd - Project root.
|
|
598
|
+
* @param now - Clock.
|
|
599
|
+
* @returns The native stdout, or `null` when unhandled.
|
|
600
|
+
*/
|
|
601
|
+
declare function dispatchMemory(event: string, payload: Record<string, unknown>, cwd: string, now: number): Promise<string | null>;
|
|
602
|
+
//#endregion
|
|
592
603
|
//#region src/runtime/lifecycle/seo/post-tool-use.d.ts
|
|
593
604
|
/**
|
|
594
605
|
* Validate the edited file's SEO completeness. Returns a deny message (for a
|
|
@@ -705,4 +716,4 @@ interface PreContext {
|
|
|
705
716
|
*/
|
|
706
717
|
declare function handlePre(ctx: PreContext): Promise<HandleOutcome>;
|
|
707
718
|
//#endregion
|
|
708
|
-
export { Activity, DEFAULT_WINDOW_MS, DuplicationVerdict, type GateInput, HandleOptions, HandleOutcome, LifecycleInput, MCP_TTL_MS, McpIntercept, NormalizedEvent, PluginScope, PreContext, REQUIRED_AGENTS, SolidProfile, TRIVIAL_BUDGET, ToolEvent, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, defaultStateDir, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLessons, dispatchLifecycle, dryGate, extractSymbols, fusengineCache, gate, generateEcosystemMap, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lessonsFileFor, lessonsStateFileFor, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, projectHash, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, seoPostToolUse, seoPostToolUseResponse, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writePluginMap, writeTree };
|
|
719
|
+
export { Activity, DEFAULT_WINDOW_MS, DuplicationVerdict, type GateInput, HandleOptions, HandleOutcome, LifecycleInput, MCP_TTL_MS, McpIntercept, NormalizedEvent, PluginScope, PreContext, REQUIRED_AGENTS, SolidProfile, TRIVIAL_BUDGET, ToolEvent, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, defaultStateDir, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLessons, dispatchLifecycle, dispatchMemory, dryGate, extractSymbols, fusengineCache, gate, generateEcosystemMap, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lessonsFileFor, lessonsStateFileFor, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, projectHash, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, seoPostToolUse, seoPostToolUseResponse, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writePluginMap, writeTree };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as sessionStartCore, A as writePluginMap, At as sanitizeSessionId, B as trackSessionChanges, C as aipilotPostToolUse, Ct as saveSecurityState, D as lessonsStateFileFor, Dt as claudeHome, E as lessonsFileFor, Et as todayUtc, F as mergeLines, G as validateTeammateOutput, H as cleanupSession, I as countFiles, J as detectSolidProfile, K as trackAgentMemory, L as getFileDesc, M as isProject, Mt as sessionStatePath, N as writeTree, Nt as sessionsDir, O as cartoSessionStart, Ot as fusengineCache, P as loadEnriched, Q as runSessionStartCleanups, R as listChildren, S as dispatchLifecycle, St as loadSecurityState, T as dispatchLessons, Tt as securityStatePath, U as saveApexState, V as validateRulesLoaded, W as logToolFailure, X as injectRules, Y as solidDetectStart, Z as readRules, _ as postTrackingSideEffects, _t as mcpPostStore, a as TRIVIAL_BUDGET, at as gitContext, b as trackSkillRead, bt as activityFor, c as detectDuplication, ct as taskContext, d as lifecycleStdout, dt as defaultStateDir, et as pruneEmptyDirs, f as postEditContext, ft as projectHash, g as securityAdvisory, gt as isMcpTool, h as dispatchMemory, ht as MCP_TTL_MS, i as REQUIRED_AGENTS, it as devContext, j as generateProjectMap, jt as saveSessionState, k as generateEcosystemMap, kt as loadSessionState, l as dryGate, lt as respond, m as seoPostToolUseResponse, mt as normalizeEvent, n as handlePre, nt as removeOldFiles, o as gate, ot as projectContext, p as seoPostToolUse, pt as trackFile, q as subagentCacheContext, r as DEFAULT_WINDOW_MS, rt as trimLogFile, s as preCommitGate, st as promptSubmitContext, t as handleHook, tt as purgeTtlTree, u as extractSymbols, ut as recordActivity, v as trackWatchResearch, vt as mcpPreIntercept, w as dispatchAipilot, wt as securityStateDir, x as trackEnrichment, xt as isoUtc, y as trackMcpResearch, yt as queryOf, z as postEditTypescript } from "../handle-DL2sZiWH.mjs";
|
|
3
3
|
//#region src/runtime/storage.ts
|
|
4
4
|
/**
|
|
5
5
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
|
@@ -9,4 +9,4 @@ function harnessStateDir(root) {
|
|
|
9
9
|
return projectLayout(root).stateDir;
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
-
export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, defaultStateDir, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLessons, dispatchLifecycle, dryGate, extractSymbols, fusengineCache, gate, generateEcosystemMap, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lessonsFileFor, lessonsStateFileFor, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, projectHash, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, seoPostToolUse, seoPostToolUseResponse, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writePluginMap, writeTree };
|
|
12
|
+
export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, aipilotPostToolUse, cartoSessionStart, claudeHome, cleanupSession, countFiles, defaultStateDir, detectDuplication, detectSolidProfile, devContext, dispatchAipilot, dispatchLessons, dispatchLifecycle, dispatchMemory, dryGate, extractSymbols, fusengineCache, gate, generateEcosystemMap, generateProjectMap, getFileDesc, gitContext, handleHook, handlePre, harnessStateDir, injectRules, isMcpTool, isProject, isoUtc, lessonsFileFor, lessonsStateFileFor, lifecycleStdout, listChildren, loadEnriched, loadSecurityState, loadSessionState, logToolFailure, mcpPostStore, mcpPreIntercept, mergeLines, normalizeEvent, postEditContext, postEditTypescript, postTrackingSideEffects, preCommitGate, projectContext, projectHash, promptSubmitContext, pruneEmptyDirs, purgeTtlTree, queryOf, readRules, recordActivity, removeOldFiles, respond, runSessionStartCleanups, sanitizeSessionId, saveApexState, saveSecurityState, saveSessionState, securityAdvisory, securityStateDir, securityStatePath, seoPostToolUse, seoPostToolUseResponse, sessionStartCore, sessionStatePath, sessionsDir, solidDetectStart, subagentCacheContext, taskContext, todayUtc, trackAgentMemory, trackEnrichment, trackFile, trackMcpResearch, trackSessionChanges, trackSkillRead, trackWatchResearch, trimLogFile, validateRulesLoaded, validateTeammateOutput, writePluginMap, writeTree };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Harness-agnostic toolkit for AI coding agents: runtime harness detection (Claude Code, Codex, Cursor, Cline, Gemini, Aider...), pure policy core (env config, project/framework detection, SOLID/file-size limits, APEX freshness, guard patterns, portable prompts), cache, project memory, ref routing, state/locks, statusline, per-harness adapters (Claude/Cursor/Cline/Gemini) and a cli-mode harness-check binary. Bun-native, with a built dist for Node + bundlers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.ts",
|