harnery 0.8.0 → 0.10.0

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 (234) hide show
  1. package/README.md +2 -0
  2. package/dist/commander.d.ts.map +1 -1
  3. package/dist/commander.js +8 -0
  4. package/dist/commands/agents.d.ts +24 -0
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +244 -13
  7. package/dist/commands/backup.d.ts +5 -4
  8. package/dist/commands/backup.d.ts.map +1 -1
  9. package/dist/commands/backup.js +15 -14
  10. package/dist/commands/browse.d.ts.map +1 -1
  11. package/dist/commands/browse.js +27 -1
  12. package/dist/commands/claude-desktop.d.ts +19 -0
  13. package/dist/commands/claude-desktop.d.ts.map +1 -0
  14. package/dist/commands/claude-desktop.js +168 -0
  15. package/dist/commands/context.d.ts.map +1 -1
  16. package/dist/commands/context.js +149 -1
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +37 -0
  19. package/dist/commands/eml.d.ts +32 -0
  20. package/dist/commands/eml.d.ts.map +1 -1
  21. package/dist/commands/eml.js +16 -3
  22. package/dist/commands/grep.d.ts +35 -2
  23. package/dist/commands/grep.d.ts.map +1 -1
  24. package/dist/commands/grep.js +427 -139
  25. package/dist/commands/harness.d.ts +11 -0
  26. package/dist/commands/harness.d.ts.map +1 -0
  27. package/dist/commands/harness.js +118 -0
  28. package/dist/commands/init.d.ts +15 -5
  29. package/dist/commands/init.d.ts.map +1 -1
  30. package/dist/commands/init.js +125 -14
  31. package/dist/commands/presence.d.ts +9 -4
  32. package/dist/commands/presence.d.ts.map +1 -1
  33. package/dist/commands/presence.js +88 -5
  34. package/dist/commands/relay.d.ts +9 -0
  35. package/dist/commands/relay.d.ts.map +1 -0
  36. package/dist/commands/relay.js +143 -0
  37. package/dist/commands/sync.d.ts.map +1 -1
  38. package/dist/commands/sync.js +5 -0
  39. package/dist/commands/workflow.d.ts +4 -0
  40. package/dist/commands/workflow.d.ts.map +1 -0
  41. package/dist/commands/workflow.js +82 -0
  42. package/dist/core/agents/canonical-emit.d.ts +15 -0
  43. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  44. package/dist/core/agents/canonical-emit.js +41 -3
  45. package/dist/core/agents/cli.js +14 -5
  46. package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
  47. package/dist/core/agents/render/prompt-context.js +44 -3
  48. package/dist/core/agents/render/session-context.d.ts.map +1 -1
  49. package/dist/core/agents/render/session-context.js +26 -1
  50. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  51. package/dist/core/agents/rules/claim-conflict.js +26 -1
  52. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  53. package/dist/core/agents/rules/commit-conflict.js +4 -37
  54. package/dist/core/agents/rules/stop-hook.d.ts +8 -0
  55. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  56. package/dist/core/agents/rules/stop-hook.js +8 -0
  57. package/dist/core/agents/session-events.d.ts.map +1 -1
  58. package/dist/core/agents/session-events.js +16 -43
  59. package/dist/core/agents/state/heartbeat-projector.d.ts +2 -0
  60. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
  61. package/dist/core/agents/state/heartbeat-projector.js +24 -4
  62. package/dist/core/agents/state/heartbeat-writer.d.ts +16 -2
  63. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  64. package/dist/core/agents/state/heartbeat-writer.js +24 -7
  65. package/dist/core/agents/state/names.d.ts +38 -3
  66. package/dist/core/agents/state/names.d.ts.map +1 -1
  67. package/dist/core/agents/state/names.js +46 -7
  68. package/dist/core/agents/state/pidmap.d.ts +5 -1
  69. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  70. package/dist/core/agents/state/pidmap.js +58 -2
  71. package/dist/core/agents/state/stale-sweep.d.ts +3 -2
  72. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  73. package/dist/core/agents/state/stale-sweep.js +6 -6
  74. package/dist/core/config.d.ts +77 -11
  75. package/dist/core/config.d.ts.map +1 -1
  76. package/dist/core/config.js +210 -25
  77. package/dist/core/context/index.d.ts +144 -0
  78. package/dist/core/context/index.d.ts.map +1 -0
  79. package/dist/core/context/index.js +380 -0
  80. package/dist/core/harnesses/bench.d.ts +29 -0
  81. package/dist/core/harnesses/bench.d.ts.map +1 -0
  82. package/dist/core/harnesses/bench.js +151 -0
  83. package/dist/core/harnesses/index.d.ts +9 -0
  84. package/dist/core/harnesses/index.d.ts.map +1 -0
  85. package/dist/core/harnesses/index.js +4 -0
  86. package/dist/core/harnesses/profiles.d.ts +62 -0
  87. package/dist/core/harnesses/profiles.d.ts.map +1 -0
  88. package/dist/core/harnesses/profiles.js +115 -0
  89. package/dist/core/harnesses/registry.d.ts +15 -0
  90. package/dist/core/harnesses/registry.d.ts.map +1 -0
  91. package/dist/core/harnesses/registry.js +113 -0
  92. package/dist/core/harnesses/types.d.ts +64 -0
  93. package/dist/core/harnesses/types.d.ts.map +1 -0
  94. package/dist/core/harnesses/types.js +20 -0
  95. package/dist/core/hooks/cli.js +309 -66
  96. package/dist/core/hooks/events/schema.d.ts +42 -1
  97. package/dist/core/hooks/events/schema.d.ts.map +1 -1
  98. package/dist/core/hooks/harness/events.d.ts +7 -0
  99. package/dist/core/hooks/harness/events.d.ts.map +1 -1
  100. package/dist/core/hooks/harness/events.js +4 -0
  101. package/dist/core/hooks/harness/parse.d.ts +1 -1
  102. package/dist/core/hooks/harness/parse.d.ts.map +1 -1
  103. package/dist/core/hooks/harness/parse.js +4 -0
  104. package/dist/core/hooks/resolve/coord-root.d.ts +11 -0
  105. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -1
  106. package/dist/core/hooks/resolve/coord-root.js +28 -6
  107. package/dist/core/presence/blob.d.ts +40 -0
  108. package/dist/core/presence/blob.d.ts.map +1 -0
  109. package/dist/core/presence/blob.js +91 -0
  110. package/dist/core/presence/git.d.ts +68 -0
  111. package/dist/core/presence/git.d.ts.map +1 -0
  112. package/dist/core/presence/git.js +173 -0
  113. package/dist/core/presence/index.d.ts +74 -0
  114. package/dist/core/presence/index.d.ts.map +1 -0
  115. package/dist/core/presence/index.js +224 -0
  116. package/dist/core/presence/relay-client.d.ts +39 -0
  117. package/dist/core/presence/relay-client.d.ts.map +1 -0
  118. package/dist/core/presence/relay-client.js +295 -0
  119. package/dist/core/presence/relay-protocol.d.ts +94 -0
  120. package/dist/core/presence/relay-protocol.d.ts.map +1 -0
  121. package/dist/core/presence/relay-protocol.js +169 -0
  122. package/dist/core/workflow/billing.d.ts +48 -0
  123. package/dist/core/workflow/billing.d.ts.map +1 -0
  124. package/dist/core/workflow/billing.js +108 -0
  125. package/dist/core/workflow/child-env.d.ts +30 -0
  126. package/dist/core/workflow/child-env.d.ts.map +1 -0
  127. package/dist/core/workflow/child-env.js +43 -0
  128. package/dist/core/workflow/engine.d.ts +21 -0
  129. package/dist/core/workflow/engine.d.ts.map +1 -0
  130. package/dist/core/workflow/engine.js +349 -0
  131. package/dist/core/workflow/harnesses.d.ts +17 -0
  132. package/dist/core/workflow/harnesses.d.ts.map +1 -0
  133. package/dist/core/workflow/harnesses.js +21 -0
  134. package/dist/core/workflow/spawn-claude.d.ts +25 -0
  135. package/dist/core/workflow/spawn-claude.d.ts.map +1 -0
  136. package/dist/core/workflow/spawn-claude.js +103 -0
  137. package/dist/core/workflow/spawn-codex.d.ts +22 -0
  138. package/dist/core/workflow/spawn-codex.d.ts.map +1 -0
  139. package/dist/core/workflow/spawn-codex.js +88 -0
  140. package/dist/core/workflow/spawn-cursor.d.ts +29 -0
  141. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -0
  142. package/dist/core/workflow/spawn-cursor.js +91 -0
  143. package/dist/core/workflow/types.d.ts +152 -0
  144. package/dist/core/workflow/types.d.ts.map +1 -0
  145. package/dist/core/workflow/types.js +9 -0
  146. package/dist/core/workflow/validate.d.ts +15 -0
  147. package/dist/core/workflow/validate.d.ts.map +1 -0
  148. package/dist/core/workflow/validate.js +70 -0
  149. package/dist/lib/browser/client.d.ts +16 -0
  150. package/dist/lib/browser/client.d.ts.map +1 -1
  151. package/dist/lib/browser/client.js +22 -0
  152. package/dist/lib/browser/index.d.ts +1 -0
  153. package/dist/lib/browser/index.d.ts.map +1 -1
  154. package/dist/lib/browser/index.js +1 -0
  155. package/dist/lib/browser/launch-args.d.ts +21 -0
  156. package/dist/lib/browser/launch-args.d.ts.map +1 -0
  157. package/dist/lib/browser/launch-args.js +32 -0
  158. package/dist/lib/claude-desktop.d.ts +105 -0
  159. package/dist/lib/claude-desktop.d.ts.map +1 -0
  160. package/dist/lib/claude-desktop.js +217 -0
  161. package/dist/lib/docs-lint.d.ts.map +1 -1
  162. package/dist/lib/docs-lint.js +5 -2
  163. package/dist/lib/identities/assume.d.ts +51 -0
  164. package/dist/lib/identities/assume.d.ts.map +1 -0
  165. package/dist/lib/identities/assume.js +274 -0
  166. package/dist/lib/identities/index.d.ts +7 -7
  167. package/dist/lib/identities/index.d.ts.map +1 -1
  168. package/dist/lib/identities/index.js +24 -24
  169. package/dist/lib/instructions/templates.d.ts.map +1 -1
  170. package/dist/lib/instructions/templates.js +15 -2
  171. package/package.json +12 -1
  172. package/schemas/config.schema.json +88 -19
  173. package/src/commander.ts +8 -0
  174. package/src/commands/agents.ts +280 -14
  175. package/src/commands/backup.ts +17 -16
  176. package/src/commands/browse.ts +32 -0
  177. package/src/commands/claude-desktop.ts +215 -0
  178. package/src/commands/context.ts +174 -1
  179. package/src/commands/doctor.ts +44 -0
  180. package/src/commands/eml.ts +22 -4
  181. package/src/commands/grep.ts +535 -142
  182. package/src/commands/harness.ts +147 -0
  183. package/src/commands/init.ts +138 -17
  184. package/src/commands/presence.ts +111 -5
  185. package/src/commands/relay.ts +160 -0
  186. package/src/commands/sync.ts +4 -0
  187. package/src/commands/workflow.ts +123 -0
  188. package/src/core/agents/canonical-emit.ts +41 -3
  189. package/src/core/agents/cli.ts +15 -6
  190. package/src/core/agents/render/prompt-context.ts +48 -3
  191. package/src/core/agents/render/session-context.ts +27 -1
  192. package/src/core/agents/rules/claim-conflict.ts +26 -1
  193. package/src/core/agents/rules/commit-conflict.ts +4 -33
  194. package/src/core/agents/rules/stop-hook.ts +17 -0
  195. package/src/core/agents/session-events.ts +15 -39
  196. package/src/core/agents/state/heartbeat-projector.ts +23 -2
  197. package/src/core/agents/state/heartbeat-writer.ts +33 -7
  198. package/src/core/agents/state/names.ts +68 -9
  199. package/src/core/agents/state/pidmap.ts +59 -2
  200. package/src/core/agents/state/stale-sweep.ts +6 -11
  201. package/src/core/config.ts +271 -24
  202. package/src/core/context/index.ts +575 -0
  203. package/src/core/harnesses/bench.ts +214 -0
  204. package/src/core/harnesses/index.ts +31 -0
  205. package/src/core/harnesses/profiles.ts +141 -0
  206. package/src/core/harnesses/registry.ts +140 -0
  207. package/src/core/harnesses/types.ts +90 -0
  208. package/src/core/hooks/cli.ts +361 -65
  209. package/src/core/hooks/events/schema.ts +72 -0
  210. package/src/core/hooks/harness/events.ts +11 -0
  211. package/src/core/hooks/harness/parse.ts +7 -1
  212. package/src/core/hooks/resolve/coord-root.ts +28 -6
  213. package/src/core/presence/blob.ts +125 -0
  214. package/src/core/presence/git.ts +191 -0
  215. package/src/core/presence/index.ts +274 -0
  216. package/src/core/presence/relay-client.ts +322 -0
  217. package/src/core/presence/relay-protocol.ts +246 -0
  218. package/src/core/workflow/billing.ts +152 -0
  219. package/src/core/workflow/child-env.ts +47 -0
  220. package/src/core/workflow/engine.ts +403 -0
  221. package/src/core/workflow/harnesses.ts +33 -0
  222. package/src/core/workflow/spawn-claude.ts +118 -0
  223. package/src/core/workflow/spawn-codex.ts +93 -0
  224. package/src/core/workflow/spawn-cursor.ts +108 -0
  225. package/src/core/workflow/types.ts +160 -0
  226. package/src/core/workflow/validate.ts +75 -0
  227. package/src/lib/browser/client.ts +37 -0
  228. package/src/lib/browser/index.ts +1 -0
  229. package/src/lib/browser/launch-args.ts +33 -0
  230. package/src/lib/claude-desktop.ts +293 -0
  231. package/src/lib/docs-lint.ts +5 -2
  232. package/src/lib/identities/assume.ts +363 -0
  233. package/src/lib/identities/index.ts +28 -24
  234. package/src/lib/instructions/templates.ts +16 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Pid-map writer.
2
+ * Pid-map writer + liveness helpers.
3
3
  *
4
4
  * Per-harness pid-map at `.harnery/pid-map/<pid>` containing
5
5
  * `<instance_id>\t<platform>`. `harn agents whoami` walks ppid up 20 hops looking
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Atomic temp+rename. Idempotent: re-writing the same row is a no-op.
9
9
  */
10
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
10
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
11
11
  import { dirname, join } from "node:path";
12
12
  function atomicWrite(path, content) {
13
13
  mkdirSync(dirname(path), { recursive: true });
@@ -30,3 +30,59 @@ export function writePidmapRow(coordRoot, pid, instanceId, platform) {
30
30
  }
31
31
  atomicWrite(path, row);
32
32
  }
33
+ /** True when any pid-map row for `instanceId` still belongs to a live process. */
34
+ export function instanceHasLivePid(coordRoot, instanceId) {
35
+ const dir = join(coordRoot, ".harnery", "pid-map");
36
+ if (!existsSync(dir))
37
+ return false;
38
+ for (const f of readdirSync(dir)) {
39
+ let row = "";
40
+ try {
41
+ row = readFileSync(join(dir, f), "utf8").trim();
42
+ }
43
+ catch {
44
+ continue;
45
+ }
46
+ const owner = row.split("\t")[0]?.trim() ?? "";
47
+ if (owner !== instanceId)
48
+ continue;
49
+ const pid = Number.parseInt(f, 10);
50
+ if (!Number.isFinite(pid))
51
+ continue;
52
+ try {
53
+ process.kill(pid, 0); // signal 0 = liveness probe
54
+ return true;
55
+ }
56
+ catch {
57
+ // ESRCH (no such process): pid-map entry is stale, keep scanning
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+ /** Drop every pid-map row owned by `instanceId`. Returns how many files were removed. */
63
+ export function removePidmapRowsForInstance(coordRoot, instanceId) {
64
+ const dir = join(coordRoot, ".harnery", "pid-map");
65
+ if (!existsSync(dir))
66
+ return 0;
67
+ let removed = 0;
68
+ for (const f of readdirSync(dir)) {
69
+ let row = "";
70
+ try {
71
+ row = readFileSync(join(dir, f), "utf8").trim();
72
+ }
73
+ catch {
74
+ continue;
75
+ }
76
+ const owner = row.split("\t")[0]?.trim() ?? "";
77
+ if (owner !== instanceId)
78
+ continue;
79
+ try {
80
+ unlinkSync(join(dir, f));
81
+ removed += 1;
82
+ }
83
+ catch {
84
+ /* best-effort cleanup */
85
+ }
86
+ }
87
+ return removed;
88
+ }
@@ -5,8 +5,9 @@
5
5
  * Fires at session.start to clean up crashed-peer detritus before the new
6
6
  * session's UX layer reads peer state.
7
7
  *
8
- * Freshness threshold defaults to 600s; configurable via
9
- * HARNERY_AGENT_COORD_FRESHNESS env var.
8
+ * Freshness threshold defaults to 600s; configurable via the
9
+ * HARNERY_AGENT_COORD_FRESHNESS env var or `.harnery/config.jsonc`
10
+ * `coord.freshness_seconds` (see `coordFreshnessSeconds`).
10
11
  */
11
12
  export declare function staleSweep(coordRoot: string): {
12
13
  heartbeatsRemoved: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"stale-sweep.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/stale-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiDH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG;IAC7C,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CA+HA"}
1
+ {"version":3,"file":"stale-sweep.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/stale-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA+CH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG;IAC7C,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CA2HA"}
@@ -5,14 +5,14 @@
5
5
  * Fires at session.start to clean up crashed-peer detritus before the new
6
6
  * session's UX layer reads peer state.
7
7
  *
8
- * Freshness threshold defaults to 600s; configurable via
9
- * HARNERY_AGENT_COORD_FRESHNESS env var.
8
+ * Freshness threshold defaults to 600s; configurable via the
9
+ * HARNERY_AGENT_COORD_FRESHNESS env var or `.harnery/config.jsonc`
10
+ * `coord.freshness_seconds` (see `coordFreshnessSeconds`).
10
11
  */
11
12
  import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
12
13
  import { join } from "node:path";
13
- import { coordEnv } from "../../../lib/env.js";
14
+ import { coordFreshnessSeconds } from "../../config.js";
14
15
  import { emit } from "../events/emit.js";
15
- const DEFAULT_FRESHNESS_SECS = 600;
16
16
  /** platform → harness, for the swept-event envelope (mirrors heartbeat-writer's harnessOf). */
17
17
  function harnessFromPlatform(platform) {
18
18
  if (platform === "cursor")
@@ -51,9 +51,9 @@ export function staleSweep(coordRoot) {
51
51
  const heartbeatsRemoved = [];
52
52
  let pidmapsRemoved = 0;
53
53
  let peerHashesRemoved = 0;
54
- const freshness = Number.parseInt(coordEnv("AGENT_COORD_FRESHNESS") ?? String(DEFAULT_FRESHNESS_SECS), 10);
54
+ const freshness = coordFreshnessSeconds(coordRoot);
55
55
  const nowSec = Math.floor(Date.now() / 1000);
56
- const cutoff = nowSec - (Number.isFinite(freshness) && freshness > 0 ? freshness : DEFAULT_FRESHNESS_SECS);
56
+ const cutoff = nowSec - freshness;
57
57
  // 1. Prune stale heartbeats from the canonical `.harnery/active/` dir.
58
58
  //
59
59
  // Two deletion regimes, deliberately asymmetric:
@@ -1,20 +1,25 @@
1
1
  /**
2
- * `.harnery/config.jsonc` reader: the host-project config the coord/hook layer
3
- * consults when it can't see the consumer CLI's own process.
2
+ * Harnery config reader: the settings the coord/hook layer consults when it
3
+ * can't see the consumer CLI's own process.
4
4
  *
5
- * The single field this module owns today is `binName`: the host CLI's binary
6
- * name (e.g. `myapp`), used in user-facing strings the agent reads and runs:
7
- * council prompts, end-of-turn nudges, command help/errors. The coord binaries
8
- * (`agent-hook`, `agent-coord`) and the web UI run as harnery itself, so they
9
- * have no other way to learn the consumer's bin name; `harn init` stamps it here
10
- * for them to read back. The `files` deny/override section is parsed separately
11
- * by `web/lib/files.ts`.
5
+ * Two layers, project-over-user (project wins field-by-field):
6
+ * 1. `~/.config/harnery/config.jsonc` — user-global base (optional)
7
+ * 2. `<project-root>/.harnery/config.jsonc` — project override (authoritative)
12
8
  *
13
- * Dependency-free (no jsonc npm dep) so it runs on both Bun and Node, and so the
14
- * ADR-009 vendored copies stay portable.
9
+ * Fields owned here: `binName` (host CLI name for agent-facing strings),
10
+ * `hooksSetupHint`, `tools`, `workflow`, `skills`, `presence`, plus the tunable
11
+ * `coord` (heartbeat freshness), `backup` (restic repo/password/prune policy),
12
+ * and `sync` (rclone remote/prefix) sections. The `files` deny/override section
13
+ * is parsed separately by `web/lib/files.ts`.
14
+ *
15
+ * Env vars and CLI flags override any config value per invocation (each accessor
16
+ * documents its own precedence). Dependency-free (no jsonc npm dep) so it runs on
17
+ * both Bun and Node, and so the ADR-009 vendored copies stay portable.
15
18
  */
16
19
  /** The standalone CLI's bin name: the resolution floor when nothing else is set. */
17
20
  export declare const DEFAULT_BIN_NAME = "harn";
21
+ /** Heartbeat-freshness default (seconds): the sweep window when nothing overrides it. */
22
+ export declare const DEFAULT_FRESHNESS_SECS = 600;
18
23
  /** Strip `//` and `/* *​/` comments from JSONC, ignoring comment-like runs inside strings. */
19
24
  export declare function stripJsonComments(input: string): string;
20
25
  /**
@@ -52,4 +57,65 @@ export declare function resolveHooksSetupHint(coordRoot?: string | null): string
52
57
  * `coordRoot` is resolved via `findCoordRoot()` when not passed.
53
58
  */
54
59
  export declare function ripgrepAutoInstall(coordRoot?: string | null): boolean;
60
+ /**
61
+ * Whether this repo pins workflow runs to subscription billing:
62
+ * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
63
+ * The `workflow run --subscription-only` flag turns it on per invocation;
64
+ * `HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0` overrides per process (the `0`
65
+ * escape hatch exists for a key-only CI job inside a pinned repo).
66
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
67
+ */
68
+ export declare function workflowSubscriptionOnly(coordRoot?: string | null): boolean;
69
+ /**
70
+ * Whether cross-machine presence (ADR 0016) is enabled for this repo.
71
+ * Default ON — the transport itself additionally gates on an origin remote
72
+ * existing and fails silent everywhere. Opt out via
73
+ * `.harnery/config.jsonc` `{ "presence": { "enabled": false } }`;
74
+ * `HARNERY_PRESENCE=1|0` overrides per process.
75
+ */
76
+ export declare function presenceEnabled(coordRoot?: string | null): boolean;
77
+ /**
78
+ * The presence relay URL for this repo, or null when the relay transport is
79
+ * not configured (git-refs only). `HARNERY_PRESENCE_RELAY` overrides per
80
+ * process (empty string or "0" disables). Requires `presenceEnabled()` to be
81
+ * true — a disabled presence section disables the relay too.
82
+ */
83
+ export declare function presenceRelayUrl(coordRoot?: string | null): string | null;
84
+ /**
85
+ * The heartbeat-freshness window (seconds): the age above which the sweeper
86
+ * prunes an agent, and the cutoff the `agents` surface uses to fold stale peers.
87
+ * Precedence:
88
+ * 1. `HARNERY_AGENT_COORD_FRESHNESS` env (canonical), or `HARNERY_AGENT_FRESHNESS` (legacy alias)
89
+ * 2. `.harnery/config.jsonc` `coord.freshness_seconds`
90
+ * 3. `600` (10 minutes)
91
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
92
+ */
93
+ export declare function coordFreshnessSeconds(coordRoot?: string | null): number;
94
+ /** Resolved `harn backup` defaults (restic repo/password + prune policy). */
95
+ export interface BackupConfig {
96
+ repo: string;
97
+ passwordFile: string;
98
+ keepDaily: number;
99
+ keepWeekly: number;
100
+ keepMonthly: number;
101
+ }
102
+ /**
103
+ * `harn backup` (restic) defaults. Per field, precedence is env → config → built-in:
104
+ * repo: `HARNERY_RESTIC_REPO` → `backup.repo` → `~/.cache/harnery/restic-repo`
105
+ * passwordFile: `HARNERY_RESTIC_PASSWORD_FILE` → `backup.password_file` → `~/.config/harnery/restic-password`
106
+ * keepDaily/Weekly/Monthly: `backup.keep_*` → 7 / 4 / 6
107
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
108
+ */
109
+ export declare function backupConfig(coordRoot?: string | null): BackupConfig;
110
+ /**
111
+ * `harn sync` (rclone) remote/prefix from `.harnery/config.jsonc` `sync`, or null
112
+ * when unset. This is the config-file layer only; `harn sync` consults env
113
+ * (`HARNERY_SYNC_REMOTE`/`_PREFIX`) first and the `~/.config/harnery/sync.json`
114
+ * file (written by `harn sync init`) as a lower-precedence fallback.
115
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
116
+ */
117
+ export declare function syncJsoncConfig(coordRoot?: string | null): {
118
+ remote: string;
119
+ prefix: string;
120
+ } | null;
55
121
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAuBvC,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqCvD;AAyBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAShE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAOrE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC,yFAAyF;AACzF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAiE1C,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqCvD;AAwFD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAShE;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAK9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAOrE;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAO3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAOlE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAWzE;AAQD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CASvE;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAmBpE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAS3C"}
@@ -1,24 +1,30 @@
1
1
  /**
2
- * `.harnery/config.jsonc` reader: the host-project config the coord/hook layer
3
- * consults when it can't see the consumer CLI's own process.
2
+ * Harnery config reader: the settings the coord/hook layer consults when it
3
+ * can't see the consumer CLI's own process.
4
4
  *
5
- * The single field this module owns today is `binName`: the host CLI's binary
6
- * name (e.g. `myapp`), used in user-facing strings the agent reads and runs:
7
- * council prompts, end-of-turn nudges, command help/errors. The coord binaries
8
- * (`agent-hook`, `agent-coord`) and the web UI run as harnery itself, so they
9
- * have no other way to learn the consumer's bin name; `harn init` stamps it here
10
- * for them to read back. The `files` deny/override section is parsed separately
11
- * by `web/lib/files.ts`.
5
+ * Two layers, project-over-user (project wins field-by-field):
6
+ * 1. `~/.config/harnery/config.jsonc` — user-global base (optional)
7
+ * 2. `<project-root>/.harnery/config.jsonc` — project override (authoritative)
12
8
  *
13
- * Dependency-free (no jsonc npm dep) so it runs on both Bun and Node, and so the
14
- * ADR-009 vendored copies stay portable.
9
+ * Fields owned here: `binName` (host CLI name for agent-facing strings),
10
+ * `hooksSetupHint`, `tools`, `workflow`, `skills`, `presence`, plus the tunable
11
+ * `coord` (heartbeat freshness), `backup` (restic repo/password/prune policy),
12
+ * and `sync` (rclone remote/prefix) sections. The `files` deny/override section
13
+ * is parsed separately by `web/lib/files.ts`.
14
+ *
15
+ * Env vars and CLI flags override any config value per invocation (each accessor
16
+ * documents its own precedence). Dependency-free (no jsonc npm dep) so it runs on
17
+ * both Bun and Node, and so the ADR-009 vendored copies stay portable.
15
18
  */
16
19
  import { readFileSync, statSync } from "node:fs";
20
+ import { homedir } from "node:os";
17
21
  import { join } from "node:path";
18
22
  import { coordEnv } from "../lib/env.js";
19
23
  import { findCoordRoot } from "./hooks/resolve/coord-root.js";
20
24
  /** The standalone CLI's bin name: the resolution floor when nothing else is set. */
21
25
  export const DEFAULT_BIN_NAME = "harn";
26
+ /** Heartbeat-freshness default (seconds): the sweep window when nothing overrides it. */
27
+ export const DEFAULT_FRESHNESS_SECS = 600;
22
28
  /** Strip `//` and `/* *​/` comments from JSONC, ignoring comment-like runs inside strings. */
23
29
  export function stripJsonComments(input) {
24
30
  let out = "";
@@ -61,31 +67,85 @@ export function stripJsonComments(input) {
61
67
  }
62
68
  return out;
63
69
  }
64
- // mtime-keyed per-process cache: a stat is cheap, a parse on every render isn't.
65
- let cache = null;
66
- function readConfig(root) {
67
- const p = join(root, ".harnery", "config.jsonc");
68
- let mtimeMs = -1;
70
+ /**
71
+ * The user-global config file, the lower-precedence base under the project file.
72
+ * Honors `XDG_CONFIG_HOME` (falling back to `~/.config`) per the XDG base-dir spec.
73
+ */
74
+ function userConfigPath() {
75
+ const xdg = process.env.XDG_CONFIG_HOME;
76
+ const base = xdg?.trim() ? xdg : join(homedir(), ".config");
77
+ return join(base, "harnery", "config.jsonc");
78
+ }
79
+ /** File mtime in ms, or -1 when the file can't be stat'd (missing). */
80
+ function statMtime(p) {
69
81
  try {
70
- mtimeMs = statSync(p).mtimeMs;
82
+ return statSync(p).mtimeMs;
71
83
  }
72
84
  catch {
73
- /* missing → -1; cache still valid until the file appears */
85
+ return -1;
74
86
  }
75
- if (cache && cache.root === root && cache.mtimeMs === mtimeMs)
76
- return cache.cfg;
77
- let cfg = {};
87
+ }
88
+ /** Parse one JSONC config file to an object; missing/unparseable → `{}`. */
89
+ function parseConfigFile(p) {
78
90
  try {
79
91
  const parsed = JSON.parse(stripJsonComments(readFileSync(p, "utf8")));
80
92
  if (parsed && typeof parsed === "object")
81
- cfg = parsed;
93
+ return parsed;
82
94
  }
83
95
  catch {
84
- /* missing or unparseable → defaults (the files-section resolver fails loud; bin name is non-critical) */
96
+ /* missing or unparseable → defaults (files-section resolver fails loud; the rest is non-critical) */
97
+ }
98
+ return {};
99
+ }
100
+ function isPlainObject(v) {
101
+ return typeof v === "object" && v !== null && !Array.isArray(v);
102
+ }
103
+ /**
104
+ * Deep-merge `override` onto `base` (override wins), recursing into plain
105
+ * objects only so a project that sets just `web.port` doesn't wipe a
106
+ * user-global `web.bind`. Arrays + scalars replace wholesale.
107
+ */
108
+ function mergeConfig(base, override) {
109
+ const out = { ...base };
110
+ for (const [k, v] of Object.entries(override)) {
111
+ const prev = out[k];
112
+ out[k] = isPlainObject(prev) && isPlainObject(v) ? mergeConfig(prev, v) : v;
113
+ }
114
+ return out;
115
+ }
116
+ // mtime-keyed per-process cache (both layers): a stat is cheap, a parse on every render isn't.
117
+ let cache = null;
118
+ /**
119
+ * The effective config for `root`: user-global (`~/.config/harnery/config.jsonc`)
120
+ * as the base, the project file (`<root>/.harnery/config.jsonc`) merged on top.
121
+ * Project values win field-by-field.
122
+ */
123
+ function readConfig(root) {
124
+ const projPath = join(root, ".harnery", "config.jsonc");
125
+ const userPath = userConfigPath();
126
+ const projMtime = statMtime(projPath);
127
+ const userMtime = statMtime(userPath);
128
+ if (cache &&
129
+ cache.root === root &&
130
+ cache.projMtime === projMtime &&
131
+ cache.userMtime === userMtime) {
132
+ return cache.cfg;
85
133
  }
86
- cache = { root, mtimeMs, cfg };
134
+ const user = userMtime === -1 ? {} : parseConfigFile(userPath);
135
+ const project = projMtime === -1 ? {} : parseConfigFile(projPath);
136
+ const cfg = mergeConfig(user, project);
137
+ cache = { root, projMtime, userMtime, cfg };
87
138
  return cfg;
88
139
  }
140
+ /**
141
+ * The PROJECT config file only — no user-global merge. `pinnedBinName` uses this
142
+ * so a user-global `binName` can never masquerade as a deliberate project pin
143
+ * (the pin guards committed, public surfaces; see `pinnedBinName`).
144
+ */
145
+ function readProjectConfig(root) {
146
+ const p = join(root, ".harnery", "config.jsonc");
147
+ return statMtime(p) === -1 ? {} : parseConfigFile(p);
148
+ }
89
149
  /**
90
150
  * Resolve the host CLI's bin name for user-facing strings. Precedence:
91
151
  * 1. `HARNERY_BIN` env (explicit per-process override)
@@ -116,7 +176,7 @@ export function resolveBinName(coordRoot) {
116
176
  * CLI would otherwise stamp the host's name into public, committed surfaces).
117
177
  */
118
178
  export function pinnedBinName(projectRoot) {
119
- const binName = readConfig(projectRoot).binName;
179
+ const binName = readProjectConfig(projectRoot).binName;
120
180
  return typeof binName === "string" && binName.trim() ? binName.trim() : null;
121
181
  }
122
182
  /**
@@ -151,3 +211,128 @@ export function ripgrepAutoInstall(coordRoot) {
151
211
  return false;
152
212
  return readConfig(root).tools?.ripgrep?.autoInstall === true;
153
213
  }
214
+ /**
215
+ * Whether this repo pins workflow runs to subscription billing:
216
+ * `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
217
+ * The `workflow run --subscription-only` flag turns it on per invocation;
218
+ * `HARNERY_WORKFLOW_SUBSCRIPTION_ONLY=1|0` overrides per process (the `0`
219
+ * escape hatch exists for a key-only CI job inside a pinned repo).
220
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
221
+ */
222
+ export function workflowSubscriptionOnly(coordRoot) {
223
+ const env = coordEnv("WORKFLOW_SUBSCRIPTION_ONLY");
224
+ if (env === "1")
225
+ return true;
226
+ if (env === "0")
227
+ return false;
228
+ const root = coordRoot ?? findCoordRoot();
229
+ if (!root)
230
+ return false;
231
+ return readConfig(root).workflow?.subscriptionOnly === true;
232
+ }
233
+ /**
234
+ * Whether cross-machine presence (ADR 0016) is enabled for this repo.
235
+ * Default ON — the transport itself additionally gates on an origin remote
236
+ * existing and fails silent everywhere. Opt out via
237
+ * `.harnery/config.jsonc` `{ "presence": { "enabled": false } }`;
238
+ * `HARNERY_PRESENCE=1|0` overrides per process.
239
+ */
240
+ export function presenceEnabled(coordRoot) {
241
+ const env = coordEnv("PRESENCE");
242
+ if (env === "1")
243
+ return true;
244
+ if (env === "0")
245
+ return false;
246
+ const root = coordRoot ?? findCoordRoot();
247
+ if (!root)
248
+ return false;
249
+ return readConfig(root).presence?.enabled !== false;
250
+ }
251
+ /**
252
+ * The presence relay URL for this repo, or null when the relay transport is
253
+ * not configured (git-refs only). `HARNERY_PRESENCE_RELAY` overrides per
254
+ * process (empty string or "0" disables). Requires `presenceEnabled()` to be
255
+ * true — a disabled presence section disables the relay too.
256
+ */
257
+ export function presenceRelayUrl(coordRoot) {
258
+ const root = coordRoot ?? findCoordRoot();
259
+ if (!presenceEnabled(root))
260
+ return null;
261
+ const env = coordEnv("PRESENCE_RELAY");
262
+ if (env !== undefined && env !== null) {
263
+ const t = env.trim();
264
+ return t && t !== "0" ? t : null;
265
+ }
266
+ if (!root)
267
+ return null;
268
+ const relay = readConfig(root).presence?.relay;
269
+ return typeof relay === "string" && relay.trim() ? relay.trim() : null;
270
+ }
271
+ /** A positive integer from `v`, else `fallback`. Floors non-integer numbers. */
272
+ function posIntOr(v, fallback) {
273
+ const n = typeof v === "number" ? v : Number.NaN;
274
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
275
+ }
276
+ /**
277
+ * The heartbeat-freshness window (seconds): the age above which the sweeper
278
+ * prunes an agent, and the cutoff the `agents` surface uses to fold stale peers.
279
+ * Precedence:
280
+ * 1. `HARNERY_AGENT_COORD_FRESHNESS` env (canonical), or `HARNERY_AGENT_FRESHNESS` (legacy alias)
281
+ * 2. `.harnery/config.jsonc` `coord.freshness_seconds`
282
+ * 3. `600` (10 minutes)
283
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
284
+ */
285
+ export function coordFreshnessSeconds(coordRoot) {
286
+ const env = coordEnv("AGENT_COORD_FRESHNESS") ?? coordEnv("AGENT_FRESHNESS");
287
+ if (env !== undefined) {
288
+ const n = Number.parseInt(env, 10);
289
+ if (Number.isFinite(n) && n > 0)
290
+ return n;
291
+ }
292
+ const root = coordRoot ?? findCoordRoot();
293
+ if (root)
294
+ return posIntOr(readConfig(root).coord?.freshness_seconds, DEFAULT_FRESHNESS_SECS);
295
+ return DEFAULT_FRESHNESS_SECS;
296
+ }
297
+ /**
298
+ * `harn backup` (restic) defaults. Per field, precedence is env → config → built-in:
299
+ * repo: `HARNERY_RESTIC_REPO` → `backup.repo` → `~/.cache/harnery/restic-repo`
300
+ * passwordFile: `HARNERY_RESTIC_PASSWORD_FILE` → `backup.password_file` → `~/.config/harnery/restic-password`
301
+ * keepDaily/Weekly/Monthly: `backup.keep_*` → 7 / 4 / 6
302
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
303
+ */
304
+ export function backupConfig(coordRoot) {
305
+ const home = homedir();
306
+ const root = coordRoot ?? findCoordRoot();
307
+ const b = root ? (readConfig(root).backup ?? {}) : {};
308
+ const cfgStr = (v) => typeof v === "string" && v.trim() ? v.trim() : undefined;
309
+ const repo = coordEnv("RESTIC_REPO") ?? cfgStr(b.repo) ?? join(home, ".cache", "harnery", "restic-repo");
310
+ const passwordFile = coordEnv("RESTIC_PASSWORD_FILE") ??
311
+ cfgStr(b.password_file) ??
312
+ join(home, ".config", "harnery", "restic-password");
313
+ return {
314
+ repo,
315
+ passwordFile,
316
+ keepDaily: posIntOr(b.keep_daily, 7),
317
+ keepWeekly: posIntOr(b.keep_weekly, 4),
318
+ keepMonthly: posIntOr(b.keep_monthly, 6),
319
+ };
320
+ }
321
+ /**
322
+ * `harn sync` (rclone) remote/prefix from `.harnery/config.jsonc` `sync`, or null
323
+ * when unset. This is the config-file layer only; `harn sync` consults env
324
+ * (`HARNERY_SYNC_REMOTE`/`_PREFIX`) first and the `~/.config/harnery/sync.json`
325
+ * file (written by `harn sync init`) as a lower-precedence fallback.
326
+ * `coordRoot` is resolved via `findCoordRoot()` when not passed.
327
+ */
328
+ export function syncJsoncConfig(coordRoot) {
329
+ const root = coordRoot ?? findCoordRoot();
330
+ if (!root)
331
+ return null;
332
+ const s = readConfig(root).sync ?? {};
333
+ if (typeof s.remote === "string" && s.remote.trim()) {
334
+ const prefix = typeof s.prefix === "string" && s.prefix.trim() ? s.prefix.trim() : "harnery";
335
+ return { remote: s.remote.trim(), prefix };
336
+ }
337
+ return null;
338
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Context continuity: durable, structured state around native harness
3
+ * compaction. Harnery does not decide when a harness compacts. It records
4
+ * context telemetry when the harness exposes it, checkpoints external work
5
+ * state before compaction, and renders a verified recovery briefing after it.
6
+ */
7
+ import type { Harness } from "../hooks/events/schema.js";
8
+ export declare const CONTEXT_SCHEMA_VERSION: 1;
9
+ export declare const MAX_CAPSULE_BYTES: number;
10
+ export type ContextSampleSource = "hook" | "native_event" | "result" | "transcript" | "estimate";
11
+ export type ContextSampleConfidence = "exact" | "reported" | "estimated";
12
+ export type ContextPhase = "observing" | "checkpointed" | "recovered" | "degraded";
13
+ export type CheckpointReason = "manual" | "pressure" | "pre_compact" | "session_end";
14
+ export interface ContextSample {
15
+ session_id: string;
16
+ harness: Harness;
17
+ model?: string;
18
+ used_tokens?: number;
19
+ window_tokens?: number;
20
+ used_percent?: number;
21
+ source: ContextSampleSource;
22
+ confidence: ContextSampleConfidence;
23
+ observed_at: string;
24
+ }
25
+ export interface ContextContinuityState {
26
+ schema_version: typeof CONTEXT_SCHEMA_VERSION;
27
+ session_id: string;
28
+ instance_id: string;
29
+ phase: ContextPhase;
30
+ generation: number;
31
+ latest_capsule?: string;
32
+ latest_context?: ContextSample;
33
+ updated_at: string;
34
+ recovered_at?: string;
35
+ compaction_completed_at?: string;
36
+ degraded_reason?: string;
37
+ }
38
+ export interface RepoSnapshot {
39
+ cwd: string;
40
+ root?: string;
41
+ branch?: string;
42
+ head?: string;
43
+ dirty_paths: string[];
44
+ dirty_paths_truncated?: boolean;
45
+ }
46
+ export interface ContinuityCapsule {
47
+ schema_version: typeof CONTEXT_SCHEMA_VERSION;
48
+ capsule_id: string;
49
+ generation: number;
50
+ created_at: string;
51
+ reason: CheckpointReason;
52
+ session: {
53
+ session_id: string;
54
+ instance_id: string;
55
+ harness: Harness;
56
+ model?: string;
57
+ };
58
+ context?: ContextSample;
59
+ work: {
60
+ task?: string;
61
+ turn_summary?: string;
62
+ continuation_note?: string;
63
+ files_held: string[];
64
+ files_held_truncated?: boolean;
65
+ last_tool?: string;
66
+ last_tool_target?: string;
67
+ };
68
+ repo: RepoSnapshot;
69
+ }
70
+ export interface ExtractContextSampleOptions {
71
+ sessionId: string;
72
+ harness: Harness;
73
+ model?: string;
74
+ source?: ContextSampleSource;
75
+ confidence?: ContextSampleConfidence;
76
+ observedAt?: string;
77
+ }
78
+ export interface CheckpointContextInput {
79
+ sessionId: string;
80
+ instanceId: string;
81
+ harness: Harness;
82
+ cwd: string;
83
+ reason: CheckpointReason;
84
+ model?: string;
85
+ continuationNote?: string;
86
+ }
87
+ export interface CheckpointContextResult {
88
+ capsule: ContinuityCapsule;
89
+ path: string;
90
+ state: ContextContinuityState;
91
+ reused: boolean;
92
+ }
93
+ export interface RecoverContextResult {
94
+ state: ContextContinuityState;
95
+ capsule: ContinuityCapsule | null;
96
+ briefing: string;
97
+ recovered: boolean;
98
+ }
99
+ export interface PreparedContextRecovery {
100
+ state: ContextContinuityState;
101
+ capsule: ContinuityCapsule;
102
+ briefing: string;
103
+ }
104
+ /** Parse the context-window shapes exposed by current harness hook/status data. */
105
+ export declare function extractContextSample(payload: Record<string, unknown>, options: ExtractContextSampleOptions): ContextSample | null;
106
+ /** Persist a latest context sample. Identical measurements are a no-op. */
107
+ export declare function recordContextSample(coordRoot: string, instanceId: string, sample: ContextSample): {
108
+ state: ContextContinuityState;
109
+ changed: boolean;
110
+ };
111
+ /**
112
+ * Snapshot external work state. Repeated PreCompact events reuse the existing
113
+ * checkpoint until a matching recovery completes; manual checkpoints always
114
+ * create a new generation.
115
+ */
116
+ export declare function checkpointContext(coordRoot: string, input: CheckpointContextInput): CheckpointContextResult;
117
+ /** Prepare a briefing without claiming that a harness has accepted it yet. */
118
+ export declare function prepareContextRecovery(coordRoot: string, input: {
119
+ sessionId: string;
120
+ instanceId: string;
121
+ cwd: string;
122
+ }): PreparedContextRecovery | null;
123
+ /** Advance continuity only after the harness context channel accepted the briefing. */
124
+ export declare function completeContextRecovery(coordRoot: string, input: {
125
+ sessionId: string;
126
+ instanceId: string;
127
+ }): ContextContinuityState;
128
+ /** Record that the harness, rather than Harnery, reported compaction complete. */
129
+ export declare function markContextCompactionCompleted(coordRoot: string, input: {
130
+ sessionId: string;
131
+ instanceId: string;
132
+ observedAt?: string;
133
+ }): ContextContinuityState;
134
+ /** Convenience API for callers whose injection is synchronous and guaranteed. */
135
+ export declare function recoverContext(coordRoot: string, input: {
136
+ sessionId: string;
137
+ instanceId: string;
138
+ cwd: string;
139
+ }): RecoverContextResult;
140
+ export declare function readContextState(coordRoot: string, sessionId: string): ContextContinuityState | null;
141
+ export declare function readLatestCapsule(coordRoot: string, sessionId: string): ContinuityCapsule | null;
142
+ export declare function renderRecoveryBriefing(capsule: ContinuityCapsule, liveRepo: RepoSnapshot): string;
143
+ export declare function snapshotRepo(cwd: string): RepoSnapshot;
144
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/context/index.ts"],"names":[],"mappings":"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"}