@fusengine/harness 0.1.81 → 0.1.83

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
@@ -5,7 +5,7 @@ import { t as detectHarness } from "../harness-BMuLJ9lm.mjs";
5
5
  import { t as claudeHome } from "../home-state-oUGFB4ds.mjs";
6
6
  import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D_WchZ3R.mjs";
7
7
  import { n as writeInitFile, t as initFor } from "../run-TAXNRSpD.mjs";
8
- import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, i as respond, t as handleHook, zt as todayUtc } from "../handle-J5r2f_O_.mjs";
8
+ import { $ as runningVersion, Q as runDoctor, Z as notify, et as versionBanner, i as respond, t as handleHook, zt as todayUtc } from "../handle-F35Gx-QS.mjs";
9
9
  import { delimiter, join } from "node:path";
10
10
  import { existsSync, mkdirSync, readFileSync, readSync, readdirSync, writeFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
@@ -12,13 +12,13 @@ import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
12
12
  import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as setStateField, t as addRoot } from "./registry-IhHk2KlT.mjs";
13
13
  import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
14
14
  import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
15
- import { _ as trivialCount, a as LOCK_TTL_MS, c as writeLastNonce, d as recordAgent, f as recordBrainstormRequired, g as recordTrivialEdit, h as recordTarget, i as LOCK_FAILED, l as agentsFresh, m as recordRefRead, o as signTrack, p as recordDoc, r as withTrack, s as verifyTrack, t as loadTrack, u as emptyTrack, v as apexAuthorizationGate } from "./store-yr_QFsZY.mjs";
15
+ import { C as apexAuthorizationGate, S as trivialCount, _ as recordBrainstormRequired, a as journalLogPath, b as recordTarget, c as appendEvent, d as signTrack, f as verifyTrack, g as recordAgent, h as emptyTrack, i as withTrack, l as withTrackLockSync, m as agentsFresh, o as readTrackSync, p as writeLastNonce, r as trackJournalEnabled, s as diffTrackEvents, t as loadTrack, u as LOCK_FAILED, v as recordDoc, x as recordTrivialEdit, y as recordRefRead } from "./store-Dw08Amqh.mjs";
16
16
  import { n as parseApplyPatch, t as isBypassPermissions } from "./permission-mode-BN3MNgbm.mjs";
17
17
  import { _ as commandToString, d as collectFiles, f as pathExists, g as writeText, h as spawnCapture, i as denyResponse, l as systemMessage, m as sleep, n as blockResponse, p as readText$1, r as contextResponse, s as informResponse, t as attachSystemMessage } from "./claude-D93HY8dq.mjs";
18
18
  import { i as toKimiResponse, n as kimiDenyResponse } from "./kimi-ChbcU2uI.mjs";
19
19
  import { r as toHermesResponse } from "./hermes-1PUEoY8q.mjs";
20
20
  import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
21
- import { appendFileSync, closeSync, copyFileSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
21
+ import { appendFileSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
22
22
  import { homedir } from "node:os";
23
23
  import { mkdir, rmdir } from "node:fs/promises";
24
24
  import { createHash } from "node:crypto";
@@ -1248,15 +1248,13 @@ function freshPassingReceipt(track, windowMs, now) {
1248
1248
  return hits.length ? hits.reduce((a, b) => b.ts > a.ts ? b : a) : null;
1249
1249
  }
1250
1250
  /**
1251
- * Sync variant reading the signed track file directly — for the sync gates
1252
- * (TaskCompleted / SubagentStop). Returns the newest passing receipt, or `null`
1253
- * on any read/verify failure (fail-closed: no proof ⇒ treated as unverified).
1251
+ * Sync variant reading the track directly (snapshot ⊕ journal) — for the sync
1252
+ * gates (TaskCompleted / SubagentStop). Returns the newest passing receipt, or
1253
+ * `null` on any read/verify failure (fail-closed: no proof ⇒ unverified).
1254
1254
  */
1255
1255
  function freshReceiptFromFile(file, windowMs, now) {
1256
1256
  try {
1257
- if (!existsSync(file)) return null;
1258
- const track = verifyTrack(JSON.parse(readFileSync(file, "utf-8")));
1259
- return track ? freshPassingReceipt(track, windowMs, now) : null;
1257
+ return freshPassingReceipt(readTrackSync(file, trackJournalEnabled()), windowMs, now);
1260
1258
  } catch {
1261
1259
  return null;
1262
1260
  }
@@ -2115,62 +2113,6 @@ function harvestAgentEvidence(transcriptPath, track, now) {
2115
2113
  return uses.reduce((acc, u) => applyToolUse(acc, u, now), track);
2116
2114
  }
2117
2115
  //#endregion
2118
- //#region src/tracking/track-lock-sync.ts
2119
- /**
2120
- * Synchronous twin of {@link withTrackLock} for lifecycle paths that CANNOT
2121
- * float async work (the SubagentStop dispatch runs synchronously in a
2122
- * short-lived hook process — an async write could be dropped before exit,
2123
- * see `evidence-harvest-io.ts`). Same semantics: `wx` lockfile, bounded total
2124
- * retry, named skipped write on contention, stale reclamation. The wait uses
2125
- * `Atomics.wait` (allowed on the main thread in both Node and Bun).
2126
- */
2127
- /** Block the thread for `ms` (bounded by the caller's total budget). */
2128
- function sleepSync(ms) {
2129
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
2130
- }
2131
- /** True when the lockfile is older than the TTL (orphaned by a dead process). */
2132
- function isStale(lock) {
2133
- try {
2134
- return Date.now() - statSync(lock).mtimeMs > LOCK_TTL_MS;
2135
- } catch {
2136
- return false;
2137
- }
2138
- }
2139
- /**
2140
- * Run `fn` under the track lock of `dir`, synchronously.
2141
- * @param dir - The track state directory (lockfile lives inside).
2142
- * @param fn - The protected read-modify-write.
2143
- * @returns `fn`'s result, or {@link LOCK_FAILED} when the lock stayed busy.
2144
- */
2145
- function withTrackLockSync(dir, fn) {
2146
- mkdirSync(dir, { recursive: true });
2147
- const lock = join(dir, "track.lock");
2148
- const deadline = Date.now() + 400;
2149
- for (;;) try {
2150
- closeSync(openSync(lock, "wx"));
2151
- break;
2152
- } catch {
2153
- if (isStale(lock)) {
2154
- try {
2155
- unlinkSync(lock);
2156
- } catch {}
2157
- continue;
2158
- }
2159
- if (Date.now() >= deadline) {
2160
- process.stderr.write(`harness: track lock busy, write skipped (${lock})\n`);
2161
- return LOCK_FAILED;
2162
- }
2163
- sleepSync(8);
2164
- }
2165
- try {
2166
- return fn();
2167
- } finally {
2168
- try {
2169
- unlinkSync(lock);
2170
- } catch {}
2171
- }
2172
- }
2173
- //#endregion
2174
2116
  //#region src/freshness/evidence-harvest-io.ts
2175
2117
  /**
2176
2118
  * SubagentStop wiring for {@link harvestAgentEvidence}. The core SubagentStop
@@ -2204,6 +2146,14 @@ function harvestSubagentTrack(payload, cwd, now, baseDir = defaultStateDir(cwd))
2204
2146
  const transcriptPath = typeof payload.agent_transcript_path === "string" ? payload.agent_transcript_path : void 0;
2205
2147
  if (!transcriptPath) return;
2206
2148
  const file = trackFile(typeof payload.session_id === "string" ? payload.session_id : "unknown", baseDir);
2149
+ if (trackJournalEnabled()) {
2150
+ const track = readTrackSync(file, true);
2151
+ const next = harvestAgentEvidence(transcriptPath, track, now);
2152
+ if (next === track) return;
2153
+ const log = journalLogPath(file);
2154
+ for (const ev of diffTrackEvents(track, next, now)) appendEvent(log, ev.field, ev.op, ev.value, ev.ts);
2155
+ return;
2156
+ }
2207
2157
  if (withTrackLockSync(dirname(file), () => {
2208
2158
  const track = loadTrackSync(file);
2209
2159
  const next = harvestAgentEvidence(transcriptPath, track, now);
@@ -7752,15 +7702,9 @@ function uiDesignSkillGate(tool, filePath, content, ev) {
7752
7702
  //#endregion
7753
7703
  //#region src/policy/design/skill-evidence.ts
7754
7704
  /**
7755
- * @module design/skill-evidence
7756
- * IO side of the UI design-skill gate: read the verified session track and
7757
- * derive {@link DesignEvidence} (read references + doc-consulted) for
7758
- * {@link uiDesignSkillGate}. Kept separate from the pure gate logic (SRP).
7759
- * @packageDocumentation
7760
- */
7761
- /**
7762
- * Read the verified session track and derive {@link DesignEvidence}. Fail-closed
7763
- * on a missing/corrupt/tampered track (no evidence → the gate blocks).
7705
+ * Read the verified session track (snapshot ⊕ journal) and derive
7706
+ * {@link DesignEvidence}. Fail-closed on a missing/corrupt/tampered track (no
7707
+ * evidence the gate blocks).
7764
7708
  * @param sessionId - the Claude session id.
7765
7709
  * @param cwd - the project root (selects the per-project state dir).
7766
7710
  * @param baseDir - override the track base dir (tests); defaults to the project state dir.
@@ -7768,7 +7712,7 @@ function uiDesignSkillGate(tool, filePath, content, ev) {
7768
7712
  function collectDesignEvidence(sessionId, cwd, baseDir = defaultStateDir(cwd)) {
7769
7713
  let track = null;
7770
7714
  try {
7771
- track = verifyTrack(JSON.parse(readFileSync(trackFile(sessionId, baseDir), "utf8")));
7715
+ track = readTrackSync(trackFile(sessionId, baseDir), trackJournalEnabled());
7772
7716
  } catch {
7773
7717
  track = null;
7774
7718
  }
@@ -1,6 +1,6 @@
1
1
  import { r as projectLayout } from "../layout-KWoE_Mqn.mjs";
2
2
  import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-oUGFB4ds.mjs";
3
- import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-J5r2f_O_.mjs";
3
+ import { A as trackMcpResearch, At as defaultStateDir, B as generateProjectMap, C as seoPostToolUse, Ct as trimLogFile, D as securityAdvisoryForPatch, Dt as claudeMdKey, E as securityAdvisory, Et as projectContext, F as dispatchAipilot, Ft as loadSecurityState, G as countFiles, H as writeTree, I as dispatchLessons, It as saveSecurityState, J as lessonsArchiveFileFor, K as getFileDesc, L as cartoSessionStart, Lt as securityStateDir, M as trackEnrichment, Mt as trackFile, N as dispatchLifecycle, Nt as normalizeEvent, O as postTrackingSideEffects, Ot as promptSubmitContext, P as aipilotPostToolUse, Pt as isoUtc, R as generateEcosystemMap, Rt as securityStatePath, S as postEditContext, St as removeOldFiles, T as dispatchMemory, Tt as gitContext, U as loadEnriched, V as isProject, W as mergeLines, X as lessonsStateFileFor, Y as lessonsFileFor, _ as preCommitGate, _t as readRules, a as recordActivity, at as saveApexState, b as extractSymbols, bt as pruneEmptyDirs, c as MCP_TTL_MS, ct as trackAgentMemory, d as isMcpTool, dt as validateSolidGate, f as queryOf, ft as checkFileSize, g as gate, gt as injectRules, h as TRIVIAL_BUDGET, ht as solidDetectStart, i as respond, it as cleanupSession, j as trackSkillRead, jt as projectHash, k as trackWatchResearch, kt as taskContext, l as WEBFETCH_TTL_MS, lt as subagentCacheContext, m as REQUIRED_AGENTS, mt as detectSolidProfile, n as activityFor, nt as trackSessionChanges, o as mcpPostStore, ot as logToolFailure, p as DEFAULT_WINDOW_MS, pt as countLoc, q as listChildren, r as handlePre, rt as validateRulesLoaded, s as mcpPreIntercept, st as validateTeammateOutput, t as handleHook, tt as postEditTypescript, u as cacheQueryOf, ut as validateTailwind, v as detectDuplication, vt as runSessionStartCleanups, w as seoPostToolUseResponse, wt as devContext, x as lifecycleStdout, xt as purgeTtlTree, y as dryGate, yt as sessionStartCore, z as writePluginMap, zt as todayUtc } from "../handle-F35Gx-QS.mjs";
4
4
  //#region src/runtime/storage.ts
5
5
  /**
6
6
  * The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,