@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 { Et as claudeHome, Tt as todayUtc, t as handleHook } from "../handle-CYlA6TeF.mjs";
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$1 = 1e4;
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$1
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
- if (opts.scope === "aipilot") {
3844
- const ai = await dispatchAipilot(rawEventName(payload), payload, opts.cwd, opts.now);
3845
- if (ai !== null) return {
3846
- stdout: ai,
3847
- exit: 0
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 { pruneEmptyDirs as $, generateProjectMap as A, saveSessionState as At, validateRulesLoaded as B, dispatchAipilot as C, securityStateDir as Ct, cartoSessionStart as D, fusengineCache as Dt, lessonsStateFileFor as E, claudeHome as Et, countFiles as F, trackAgentMemory as G, saveApexState as H, getFileDesc as I, solidDetectStart as J, subagentCacheContext as K, listChildren as L, writeTree as M, sessionsDir as Mt, loadEnriched as N, generateEcosystemMap as O, loadSessionState as Ot, mergeLines as P, sessionStartCore as Q, postEditTypescript as R, aipilotPostToolUse as S, saveSecurityState as St, lessonsFileFor as T, todayUtc as Tt, logToolFailure as U, cleanupSession as V, validateTeammateOutput as W, readRules as X, injectRules as Y, runSessionStartCleanups as Z, trackWatchResearch as _, mcpPreIntercept as _t, TRIVIAL_BUDGET as a, projectContext as at, trackEnrichment as b, isoUtc as bt, detectDuplication as c, respond as ct, lifecycleStdout as d, projectHash$1 as dt, purgeTtlTree as et, postEditContext as f, trackFile as ft, postTrackingSideEffects as g, mcpPostStore as gt, securityAdvisory as h, isMcpTool as ht, REQUIRED_AGENTS as i, gitContext as it, isProject as j, sessionStatePath as jt, writePluginMap as k, sanitizeSessionId as kt, dryGate as l, recordActivity as lt, seoPostToolUseResponse as m, MCP_TTL_MS as mt, handlePre as n, trimLogFile as nt, gate as o, promptSubmitContext as ot, seoPostToolUse as p, normalizeEvent as pt, detectSolidProfile as q, DEFAULT_WINDOW_MS as r, devContext as rt, preCommitGate as s, taskContext as st, handleHook as t, removeOldFiles as tt, extractSymbols as u, defaultStateDir as ut, trackMcpResearch as v, queryOf as vt, dispatchLessons as w, securityStatePath as wt, dispatchLifecycle as x, loadSecurityState as xt, trackSkillRead as y, activityFor as yt, trackSessionChanges as z };
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 };
@@ -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 };
@@ -1,5 +1,5 @@
1
1
  import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
2
- import { $ as pruneEmptyDirs, A as generateProjectMap, At as saveSessionState, B as validateRulesLoaded, C as dispatchAipilot, Ct as securityStateDir, D as cartoSessionStart, Dt as fusengineCache, E as lessonsStateFileFor, Et as claudeHome, F as countFiles, G as trackAgentMemory, H as saveApexState, I as getFileDesc, J as solidDetectStart, K as subagentCacheContext, L as listChildren, M as writeTree, Mt as sessionsDir, N as loadEnriched, O as generateEcosystemMap, Ot as loadSessionState, P as mergeLines, Q as sessionStartCore, R as postEditTypescript, S as aipilotPostToolUse, St as saveSecurityState, T as lessonsFileFor, Tt as todayUtc, U as logToolFailure, V as cleanupSession, W as validateTeammateOutput, X as readRules, Y as injectRules, Z as runSessionStartCleanups, _ as trackWatchResearch, _t as mcpPreIntercept, a as TRIVIAL_BUDGET, at as projectContext, b as trackEnrichment, bt as isoUtc, c as detectDuplication, ct as respond, d as lifecycleStdout, dt as projectHash, et as purgeTtlTree, f as postEditContext, ft as trackFile, g as postTrackingSideEffects, gt as mcpPostStore, h as securityAdvisory, ht as isMcpTool, i as REQUIRED_AGENTS, it as gitContext, j as isProject, jt as sessionStatePath, k as writePluginMap, kt as sanitizeSessionId, l as dryGate, lt as recordActivity, m as seoPostToolUseResponse, mt as MCP_TTL_MS, n as handlePre, nt as trimLogFile, o as gate, ot as promptSubmitContext, p as seoPostToolUse, pt as normalizeEvent, q as detectSolidProfile, r as DEFAULT_WINDOW_MS, rt as devContext, s as preCommitGate, st as taskContext, t as handleHook, tt as removeOldFiles, u as extractSymbols, ut as defaultStateDir, v as trackMcpResearch, vt as queryOf, w as dispatchLessons, wt as securityStatePath, x as dispatchLifecycle, xt as loadSecurityState, y as trackSkillRead, yt as activityFor, z as trackSessionChanges } from "../handle-CYlA6TeF.mjs";
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.35",
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",