@fusengine/harness 0.1.48 → 0.1.49

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.
Files changed (49) hide show
  1. package/dist/adapters/claude/index.d.mts +2 -2
  2. package/dist/adapters/claude/index.mjs +2 -2
  3. package/dist/adapters/cline/index.mjs +1 -1
  4. package/dist/adapters/codex/index.d.mts +1 -1
  5. package/dist/adapters/codex/index.mjs +1 -1
  6. package/dist/adapters/cursor/index.d.mts +10 -3
  7. package/dist/adapters/cursor/index.mjs +3 -3
  8. package/dist/adapters/gemini/index.mjs +1 -1
  9. package/dist/adapters/hermes/index.d.mts +54 -0
  10. package/dist/adapters/hermes/index.mjs +2 -0
  11. package/dist/apex-Wdi1nq_w.d.mts +73 -0
  12. package/dist/{claude-U1KTaRG4.mjs → claude-BxC9semG.mjs} +30 -9
  13. package/dist/cli/bin.mjs +5 -5
  14. package/dist/cli/index.mjs +1 -1
  15. package/dist/config/index.d.mts +1 -1
  16. package/dist/config/index.mjs +1 -1
  17. package/dist/detect/index.d.mts +1 -1
  18. package/dist/detect/index.mjs +1 -1
  19. package/dist/{doc-helpers-BNfYWvYv.d.mts → doc-helpers-CEKzGg2u.d.mts} +2 -0
  20. package/dist/{dotenv-B9nM4cuQ.mjs → dotenv-Jj8aL1FL.mjs} +2 -1
  21. package/dist/{evaluate-qYC5xClb.mjs → evaluate-CNAzgxnN.mjs} +3 -85
  22. package/dist/freshness/index.d.mts +1 -1
  23. package/dist/{handle-CCV0ycdr.mjs → handle-D1hHqHvh.mjs} +404 -52
  24. package/dist/{harness-C8Nxxyn_.mjs → harness-Cb9xR8dC.mjs} +6 -3
  25. package/dist/{harness-BPPu5CrN.d.mts → harness-s1Fce_dL.d.mts} +1 -1
  26. package/dist/hermes-DWXCRFZU.mjs +54 -0
  27. package/dist/home-state-D0RLWP8J.mjs +134 -0
  28. package/dist/{index-BqfOFKm7.d.mts → index-BfMsILmg.d.mts} +2 -63
  29. package/dist/{index-BxjzFraL.d.mts → index-DZCLmSoO.d.mts} +11 -9
  30. package/dist/{index-BXPySPxE.d.mts → index-jj_EzZRv.d.mts} +1 -1
  31. package/dist/index.d.mts +7 -6
  32. package/dist/index.mjs +6 -5
  33. package/dist/init/index.d.mts +1 -1
  34. package/dist/policy/index.d.mts +3 -2
  35. package/dist/policy/index.mjs +4 -3
  36. package/dist/prompt/index.d.mts +1 -1
  37. package/dist/{run-CaqeQFYp.mjs → run-DZvP_9xB.mjs} +1 -1
  38. package/dist/runtime/index.d.mts +22 -8
  39. package/dist/runtime/index.mjs +2 -2
  40. package/dist/{session-state-V8Jp-KE5.d.mts → session-state-DMpotbRz.d.mts} +26 -4
  41. package/dist/skill-path-DhItkBzk.mjs +95 -0
  42. package/dist/store-QSSTO3lY.mjs +349 -0
  43. package/dist/tracking/index.d.mts +2 -2
  44. package/dist/tracking/index.mjs +2 -2
  45. package/dist/{types-D56jSgD9.d.mts → types-DVbIl9md.d.mts} +6 -0
  46. package/dist/{validate-ebDjIyOa.mjs → validate-DCQ8dkdL.mjs} +16 -100
  47. package/package.json +7 -2
  48. package/dist/home-state-mKZxP4oZ.mjs +0 -52
  49. package/dist/store-ARx-GQSQ.mjs +0 -200
@@ -1,52 +0,0 @@
1
- import { t as atomicWrite } from "./json-io-DisYd2fb.mjs";
2
- import { join } from "node:path";
3
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
4
- import { homedir } from "node:os";
5
- //#region src/runtime/home-state.ts
6
- /** Home `~/.claude` dir — per-harness config (CLAUDE.md, logs, plugins). */
7
- function claudeHome(home = homedir()) {
8
- return join(home, ".claude");
9
- }
10
- /** Neutral, harness-agnostic home for fuse-harness's OWN cache/state: `~/.fuse-harness`. */
11
- function fuseHarnessHome(home = homedir()) {
12
- return join(home, ".fuse-harness");
13
- }
14
- /** `~/.fuse-harness/cache` base dir for session/cache state (shared across harnesses). */
15
- function fusengineCache(home = homedir()) {
16
- return join(fuseHarnessHome(home), "cache");
17
- }
18
- /** `~/.fuse-harness/cache/sessions` — per-session JSON state dir. */
19
- function sessionsDir(home = homedir()) {
20
- return join(fusengineCache(home), "sessions");
21
- }
22
- const SID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
23
- /** Validate a session id (1-128 url-safe chars); null when invalid. */
24
- function sanitizeSessionId(sid) {
25
- const s = String(sid ?? "").trim();
26
- return SID_RE.test(s) ? s : null;
27
- }
28
- /** Unified per-session state file path: `sessions/session-<sid>.json`. */
29
- function sessionStatePath(sid, home = homedir()) {
30
- return join(sessionsDir(home), `session-${sid}.json`);
31
- }
32
- /** Load a session-state dict, or `{}` when missing/corrupt (mirrors Python). */
33
- function loadSessionState(sid, home = homedir()) {
34
- const path = sessionStatePath(sid, home);
35
- try {
36
- if (!existsSync(path)) return {};
37
- const data = JSON.parse(readFileSync(path, "utf-8"));
38
- return typeof data === "object" && data !== null && !Array.isArray(data) ? data : {};
39
- } catch {
40
- return {};
41
- }
42
- }
43
- /** Atomically persist a session-state dict (0o600 via atomicWrite, indent 2). */
44
- function saveSessionState(sid, state, home = homedir()) {
45
- mkdirSync(sessionsDir(home), {
46
- recursive: true,
47
- mode: 448
48
- });
49
- atomicWrite(sessionStatePath(sid, home), JSON.stringify(state, null, 2));
50
- }
51
- //#endregion
52
- export { sanitizeSessionId as a, sessionsDir as c, loadSessionState as i, fuseHarnessHome as n, saveSessionState as o, fusengineCache as r, sessionStatePath as s, claudeHome as t };
@@ -1,200 +0,0 @@
1
- import { a as writeJsonFile, i as readJsonFile } from "./json-io-DisYd2fb.mjs";
2
- import { n as fuseHarnessHome } from "./home-state-mKZxP4oZ.mjs";
3
- import { join } from "node:path";
4
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { createHmac, randomBytes } from "node:crypto";
6
- //#region src/tracking/session-state.ts
7
- /** A fresh, empty track. */
8
- function emptyTrack() {
9
- return {
10
- authorizations: {},
11
- refsRead: [],
12
- agents: [],
13
- trivialEdits: []
14
- };
15
- }
16
- /** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
17
- function recordDoc(track, framework, sessionId, source) {
18
- const prev = track.authorizations[framework] ?? {};
19
- const sessions = new Set(prev.doc_sessions ?? []);
20
- sessions.add(sessionId);
21
- const sources = new Set(prev.sources ?? (prev.source ? [prev.source] : []));
22
- sources.add(source);
23
- return {
24
- ...track,
25
- authorizations: {
26
- ...track.authorizations,
27
- [framework]: {
28
- ...prev,
29
- doc_sessions: [...sessions],
30
- sources: [...sources]
31
- }
32
- }
33
- };
34
- }
35
- /**
36
- * Record that a SOLID reference file was read (deduped). Immutable. When `now`
37
- * (epoch ms — the tool event's own timestamp, never a fresh `Date.now()`) is
38
- * supplied, the read is stamped in `refsReadAt`, refreshed on re-reads so the
39
- * LATEST read drives the SOLID TTL (parity track-solid-reads.py, which appends
40
- * a timestamped entry per read; require-solid-read.py checks the most recent).
41
- * Callers that omit `now` keep the legacy untimestamped behavior.
42
- */
43
- function recordRefRead(track, path, now) {
44
- const refsRead = track.refsRead.includes(path) ? track.refsRead : [...track.refsRead, path];
45
- if (now === void 0) return refsRead === track.refsRead ? track : {
46
- ...track,
47
- refsRead
48
- };
49
- return {
50
- ...track,
51
- refsRead,
52
- refsReadAt: {
53
- ...track.refsReadAt,
54
- [path]: now
55
- }
56
- };
57
- }
58
- /** Record an agent/tool call with a timestamp + optional quality. Immutable. */
59
- function recordAgent(track, name, ts, quality) {
60
- const entry = quality ? {
61
- name,
62
- ts,
63
- quality
64
- } : {
65
- name,
66
- ts
67
- };
68
- return {
69
- ...track,
70
- agents: [...track.agents, entry]
71
- };
72
- }
73
- /** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
74
- function agentsFresh(track, names, windowMs, now) {
75
- const cutoff = now - windowMs;
76
- return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff && a.quality !== "insufficient"));
77
- }
78
- /** Record a trivial edit timestamp (sliding window; old evicted). Immutable. */
79
- function recordTrivialEdit(track, ts, windowMs, now) {
80
- const cutoff = now - windowMs;
81
- return {
82
- ...track,
83
- trivialEdits: [...(track.trivialEdits ?? []).filter((t) => t > cutoff), ts]
84
- };
85
- }
86
- /** Count trivial edits within the sliding window. */
87
- function trivialCount(track, windowMs, now) {
88
- const cutoff = now - windowMs;
89
- return (track.trivialEdits ?? []).filter((t) => t > cutoff).length;
90
- }
91
- /** Set the brainstorm-required flag (from creation-intent detection). Immutable. */
92
- function recordBrainstormRequired(track, required) {
93
- return {
94
- ...track,
95
- brainstormRequired: required
96
- };
97
- }
98
- //#endregion
99
- //#region src/tracking/integrity.ts
100
- /**
101
- * @module integrity
102
- * HMAC-SHA256 tamper-evident wrapping for {@link SessionTrack}.
103
- *
104
- * Failure policy: ONLY a MAC mismatch causes fail-closed (returns null).
105
- * The nonce is included in the signed payload and written to disk for advisory
106
- * diagnostics, but is NOT checked during verification — concurrent hook
107
- * invocations (PostToolUse, SubagentStop, …) legitimately load the same
108
- * envelope after the nonce watermark advances; a monotonic check would trigger
109
- * spurious fail-closed behaviour mid-session.
110
- *
111
- * The machine key is readable by the same agent process, so this deters naive
112
- * out-of-band tampering — not a determined re-sign. Item B (transcript-grounded
113
- * freshness) is the primary guarantee.
114
- * @packageDocumentation
115
- */
116
- const HARNESS_DIR = fuseHarnessHome();
117
- const KEY_PATH = join(HARNESS_DIR, ".key");
118
- const NONCE_PATH = join(HARNESS_DIR, ".nonce");
119
- /** Load (or create on first use) the per-machine HMAC key stored at mode 0600. */
120
- function loadOrCreateKey() {
121
- mkdirSync(HARNESS_DIR, { recursive: true });
122
- if (!existsSync(KEY_PATH)) {
123
- const key = randomBytes(32).toString("hex");
124
- writeFileSync(KEY_PATH, key, {
125
- encoding: "utf8",
126
- mode: 384
127
- });
128
- return key;
129
- }
130
- return readFileSync(KEY_PATH, "utf8").trim();
131
- }
132
- /**
133
- * Persist the last-seen nonce for advisory diagnostics (mode 0600).
134
- * Never read during {@link verifyTrack} — see module JSDoc for rationale.
135
- */
136
- function writeLastNonce(nonce) {
137
- mkdirSync(HARNESS_DIR, { recursive: true });
138
- writeFileSync(NONCE_PATH, String(nonce), {
139
- encoding: "utf8",
140
- mode: 384
141
- });
142
- }
143
- /** Compute HMAC-SHA256 over the canonical message `"${nonce}:${data}"`. */
144
- function computeMac(key, data, nonce) {
145
- return createHmac("sha256", key).update(`${nonce}:${data}`).digest("hex");
146
- }
147
- /**
148
- * Sign a {@link SessionTrack} into a tamper-evident envelope. The nonce
149
- * (`Date.now()`) is embedded in the MAC to bind the timestamp to the payload.
150
- */
151
- function signTrack(track) {
152
- const key = loadOrCreateKey();
153
- const data = JSON.stringify(track);
154
- const nonce = Date.now();
155
- return {
156
- data,
157
- nonce,
158
- mac: computeMac(key, data, nonce)
159
- };
160
- }
161
- /**
162
- * Verify a {@link TrackEnvelope}. Returns the parsed {@link SessionTrack} on
163
- * success, or `null` ONLY on MAC mismatch / parse failure (fail-closed on
164
- * tampering). The nonce is NOT checked — see module JSDoc.
165
- */
166
- function verifyTrack(envelope) {
167
- try {
168
- const key = loadOrCreateKey();
169
- if (envelope.mac !== computeMac(key, envelope.data, envelope.nonce)) return null;
170
- return JSON.parse(envelope.data);
171
- } catch {
172
- return null;
173
- }
174
- }
175
- //#endregion
176
- //#region src/tracking/store.ts
177
- /**
178
- * Load and verify a session track from a signed envelope file.
179
- *
180
- * Returns {@link emptyTrack} (fail closed) when the file is absent, corrupt, or
181
- * fails MAC validation — so the gates re-require real agents rather than trust a
182
- * forged track. Only a MAC mismatch triggers fail-closed; the nonce is advisory
183
- * and is never checked during load.
184
- */
185
- async function loadTrack(file) {
186
- const envelope = await readJsonFile(file);
187
- if (!envelope) return emptyTrack();
188
- return verifyTrack(envelope) ?? emptyTrack();
189
- }
190
- /**
191
- * Sign and persist a session track as a tamper-evident envelope, then write the
192
- * advisory nonce watermark.
193
- */
194
- async function saveTrack(file, track) {
195
- const envelope = signTrack(track);
196
- await writeJsonFile(file, envelope);
197
- writeLastNonce(envelope.nonce);
198
- }
199
- //#endregion
200
- export { emptyTrack as a, recordDoc as c, trivialCount as d, agentsFresh as i, recordRefRead as l, saveTrack as n, recordAgent as o, verifyTrack as r, recordBrainstormRequired as s, loadTrack as t, recordTrivialEdit as u };