@sentropic/h2a-runtime 0.98.6 → 0.98.7-rc.10

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 (236) hide show
  1. package/dist/agent-launch-args.d.ts.map +1 -1
  2. package/dist/agent-launch-args.js +5 -5
  3. package/dist/agent-launch-args.js.map +1 -1
  4. package/dist/agy-claude.d.ts +42 -0
  5. package/dist/agy-claude.d.ts.map +1 -0
  6. package/dist/agy-claude.js +148 -0
  7. package/dist/agy-claude.js.map +1 -0
  8. package/dist/agy-headless.d.ts +11 -0
  9. package/dist/agy-headless.d.ts.map +1 -0
  10. package/dist/agy-headless.js +104 -0
  11. package/dist/agy-headless.js.map +1 -0
  12. package/dist/attach.d.ts +5 -0
  13. package/dist/attach.d.ts.map +1 -1
  14. package/dist/attach.js +75 -6
  15. package/dist/attach.js.map +1 -1
  16. package/dist/auth-login.d.ts +24 -1
  17. package/dist/auth-login.d.ts.map +1 -1
  18. package/dist/auth-login.js +132 -4
  19. package/dist/auth-login.js.map +1 -1
  20. package/dist/central-mcp.d.ts +34 -0
  21. package/dist/central-mcp.d.ts.map +1 -1
  22. package/dist/central-mcp.js +275 -71
  23. package/dist/central-mcp.js.map +1 -1
  24. package/dist/claude-code-cli-isolation.d.ts +53 -0
  25. package/dist/claude-code-cli-isolation.d.ts.map +1 -0
  26. package/dist/claude-code-cli-isolation.js +277 -0
  27. package/dist/claude-code-cli-isolation.js.map +1 -0
  28. package/dist/claude-code-cli-lifecycle.d.ts +80 -0
  29. package/dist/claude-code-cli-lifecycle.d.ts.map +1 -0
  30. package/dist/claude-code-cli-lifecycle.js +402 -0
  31. package/dist/claude-code-cli-lifecycle.js.map +1 -0
  32. package/dist/claude-code-cli-runner.d.ts +89 -0
  33. package/dist/claude-code-cli-runner.d.ts.map +1 -0
  34. package/dist/claude-code-cli-runner.js +659 -0
  35. package/dist/claude-code-cli-runner.js.map +1 -0
  36. package/dist/claude-seat-input.d.ts +47 -0
  37. package/dist/claude-seat-input.d.ts.map +1 -0
  38. package/dist/claude-seat-input.js +272 -0
  39. package/dist/claude-seat-input.js.map +1 -0
  40. package/dist/claude-seat-mesh-contract.d.ts +88 -0
  41. package/dist/claude-seat-mesh-contract.d.ts.map +1 -0
  42. package/dist/claude-seat-mesh-contract.js +13 -0
  43. package/dist/claude-seat-mesh-contract.js.map +1 -0
  44. package/dist/cli-help-groups.js +1 -1
  45. package/dist/cli-help-groups.js.map +1 -1
  46. package/dist/cli-seat-codecs.d.ts +86 -0
  47. package/dist/cli-seat-codecs.d.ts.map +1 -0
  48. package/dist/cli-seat-codecs.js +255 -0
  49. package/dist/cli-seat-codecs.js.map +1 -0
  50. package/dist/cli-seat-fixtures.d.ts +86 -0
  51. package/dist/cli-seat-fixtures.d.ts.map +1 -0
  52. package/dist/cli-seat-fixtures.js +114 -0
  53. package/dist/cli-seat-fixtures.js.map +1 -0
  54. package/dist/cli-seat-materialization.d.ts +245 -0
  55. package/dist/cli-seat-materialization.d.ts.map +1 -0
  56. package/dist/cli-seat-materialization.js +646 -0
  57. package/dist/cli-seat-materialization.js.map +1 -0
  58. package/dist/conv-guard.d.ts.map +1 -1
  59. package/dist/conv-guard.js +6 -2
  60. package/dist/conv-guard.js.map +1 -1
  61. package/dist/crash-log.d.ts +112 -0
  62. package/dist/crash-log.d.ts.map +1 -0
  63. package/dist/crash-log.js +560 -0
  64. package/dist/crash-log.js.map +1 -0
  65. package/dist/delegate.d.ts +10 -0
  66. package/dist/delegate.d.ts.map +1 -1
  67. package/dist/delegate.js +29 -0
  68. package/dist/delegate.js.map +1 -1
  69. package/dist/file-lock.d.ts +4 -2
  70. package/dist/file-lock.d.ts.map +1 -1
  71. package/dist/file-lock.js +24 -10
  72. package/dist/file-lock.js.map +1 -1
  73. package/dist/gateway-host/claude-seat-client.d.ts +32 -0
  74. package/dist/gateway-host/claude-seat-client.d.ts.map +1 -0
  75. package/dist/gateway-host/claude-seat-client.js +38 -0
  76. package/dist/gateway-host/claude-seat-client.js.map +1 -0
  77. package/dist/gateway-host/config-file.d.ts +2 -0
  78. package/dist/gateway-host/config-file.d.ts.map +1 -1
  79. package/dist/gateway-host/config-file.js +5 -1
  80. package/dist/gateway-host/config-file.js.map +1 -1
  81. package/dist/gateway-host/daemon.d.ts.map +1 -1
  82. package/dist/gateway-host/daemon.js +1 -2
  83. package/dist/gateway-host/daemon.js.map +1 -1
  84. package/dist/gateway-host/host.d.ts +6 -0
  85. package/dist/gateway-host/host.d.ts.map +1 -1
  86. package/dist/gateway-host/host.js +24 -3
  87. package/dist/gateway-host/host.js.map +1 -1
  88. package/dist/gateway-host/live-auth.d.ts +62 -0
  89. package/dist/gateway-host/live-auth.d.ts.map +1 -0
  90. package/dist/gateway-host/live-auth.js +87 -0
  91. package/dist/gateway-host/live-auth.js.map +1 -0
  92. package/dist/gateway-host/live-declarations.d.ts +58 -0
  93. package/dist/gateway-host/live-declarations.d.ts.map +1 -0
  94. package/dist/gateway-host/live-declarations.js +429 -0
  95. package/dist/gateway-host/live-declarations.js.map +1 -0
  96. package/dist/gateway-host/live-disclosure.d.ts +124 -0
  97. package/dist/gateway-host/live-disclosure.d.ts.map +1 -0
  98. package/dist/gateway-host/live-disclosure.js +347 -0
  99. package/dist/gateway-host/live-disclosure.js.map +1 -0
  100. package/dist/gateway-host/live-github.d.ts +11 -0
  101. package/dist/gateway-host/live-github.d.ts.map +1 -0
  102. package/dist/gateway-host/live-github.js +374 -0
  103. package/dist/gateway-host/live-github.js.map +1 -0
  104. package/dist/gateway-host/live-host.d.ts +20 -0
  105. package/dist/gateway-host/live-host.d.ts.map +1 -0
  106. package/dist/gateway-host/live-host.js +14 -0
  107. package/dist/gateway-host/live-host.js.map +1 -0
  108. package/dist/gateway-host/live-object-store.d.ts +35 -0
  109. package/dist/gateway-host/live-object-store.d.ts.map +1 -0
  110. package/dist/gateway-host/live-object-store.js +447 -0
  111. package/dist/gateway-host/live-object-store.js.map +1 -0
  112. package/dist/gateway-host/live-registration.d.ts +162 -0
  113. package/dist/gateway-host/live-registration.d.ts.map +1 -0
  114. package/dist/gateway-host/live-registration.js +455 -0
  115. package/dist/gateway-host/live-registration.js.map +1 -0
  116. package/dist/gateway-model-catalog/model-catalog.d.ts +38 -0
  117. package/dist/gateway-model-catalog/model-catalog.d.ts.map +1 -0
  118. package/dist/gateway-model-catalog/model-catalog.js +167 -0
  119. package/dist/gateway-model-catalog/model-catalog.js.map +1 -0
  120. package/dist/identity-cull/cull.js +1 -1
  121. package/dist/identity-cull/cull.js.map +1 -1
  122. package/dist/identity-cull/identity-fence.d.ts +48 -0
  123. package/dist/identity-cull/identity-fence.d.ts.map +1 -0
  124. package/dist/identity-cull/identity-fence.js +221 -0
  125. package/dist/identity-cull/identity-fence.js.map +1 -0
  126. package/dist/identity-operator-break.d.ts +43 -0
  127. package/dist/identity-operator-break.d.ts.map +1 -0
  128. package/dist/identity-operator-break.js +85 -0
  129. package/dist/identity-operator-break.js.map +1 -0
  130. package/dist/index.d.ts +8 -1
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +525 -132
  133. package/dist/index.js.map +1 -1
  134. package/dist/launch-layout.d.ts.map +1 -1
  135. package/dist/launch-layout.js +10 -2
  136. package/dist/launch-layout.js.map +1 -1
  137. package/dist/llm-mesh-accounts.d.ts +128 -6
  138. package/dist/llm-mesh-accounts.d.ts.map +1 -1
  139. package/dist/llm-mesh-accounts.js +370 -14
  140. package/dist/llm-mesh-accounts.js.map +1 -1
  141. package/dist/native-host.d.ts +25 -1
  142. package/dist/native-host.d.ts.map +1 -1
  143. package/dist/native-host.js +211 -28
  144. package/dist/native-host.js.map +1 -1
  145. package/dist/native-restart.d.ts +2 -0
  146. package/dist/native-restart.d.ts.map +1 -1
  147. package/dist/native-restart.js +7 -0
  148. package/dist/native-restart.js.map +1 -1
  149. package/dist/native-terminal/fleet.d.ts +11 -0
  150. package/dist/native-terminal/fleet.d.ts.map +1 -1
  151. package/dist/native-terminal/fleet.js +58 -1
  152. package/dist/native-terminal/fleet.js.map +1 -1
  153. package/dist/native-terminal/host-scope.d.ts +18 -0
  154. package/dist/native-terminal/host-scope.d.ts.map +1 -0
  155. package/dist/native-terminal/host-scope.js +65 -0
  156. package/dist/native-terminal/host-scope.js.map +1 -0
  157. package/dist/native-terminal/host.d.ts +7 -1
  158. package/dist/native-terminal/host.d.ts.map +1 -1
  159. package/dist/native-terminal/host.js +10 -26
  160. package/dist/native-terminal/host.js.map +1 -1
  161. package/dist/native-terminal/journal.d.ts +5 -0
  162. package/dist/native-terminal/journal.d.ts.map +1 -1
  163. package/dist/native-terminal/journal.js +28 -16
  164. package/dist/native-terminal/journal.js.map +1 -1
  165. package/dist/native-terminal/op.d.ts +49 -1
  166. package/dist/native-terminal/op.d.ts.map +1 -1
  167. package/dist/native-terminal/op.js +254 -38
  168. package/dist/native-terminal/op.js.map +1 -1
  169. package/dist/native-terminal/process.d.ts.map +1 -1
  170. package/dist/native-terminal/process.js +10 -1
  171. package/dist/native-terminal/process.js.map +1 -1
  172. package/dist/native-terminal/screen.d.ts.map +1 -1
  173. package/dist/native-terminal/screen.js +1 -1
  174. package/dist/native-terminal/screen.js.map +1 -1
  175. package/dist/native-terminal/server.d.ts +12 -2
  176. package/dist/native-terminal/server.d.ts.map +1 -1
  177. package/dist/native-terminal/server.js +143 -42
  178. package/dist/native-terminal/server.js.map +1 -1
  179. package/dist/native-terminal/session-scope.d.ts +2 -0
  180. package/dist/native-terminal/session-scope.d.ts.map +1 -0
  181. package/dist/native-terminal/session-scope.js +32 -0
  182. package/dist/native-terminal/session-scope.js.map +1 -0
  183. package/dist/proc-identity.d.ts +31 -0
  184. package/dist/proc-identity.d.ts.map +1 -1
  185. package/dist/proc-identity.js +54 -0
  186. package/dist/proc-identity.js.map +1 -1
  187. package/dist/prompt-delivery.d.ts +3 -0
  188. package/dist/prompt-delivery.d.ts.map +1 -1
  189. package/dist/prompt-delivery.js +8 -3
  190. package/dist/prompt-delivery.js.map +1 -1
  191. package/dist/protocol-local.d.ts +16 -0
  192. package/dist/protocol-local.d.ts.map +1 -1
  193. package/dist/protocol-local.js +14 -0
  194. package/dist/protocol-local.js.map +1 -1
  195. package/dist/registry.d.ts +16 -12
  196. package/dist/registry.d.ts.map +1 -1
  197. package/dist/registry.js +39 -5
  198. package/dist/registry.js.map +1 -1
  199. package/dist/relaunch.d.ts +16 -0
  200. package/dist/relaunch.d.ts.map +1 -1
  201. package/dist/relaunch.js +42 -1
  202. package/dist/relaunch.js.map +1 -1
  203. package/dist/restore.d.ts +4 -0
  204. package/dist/restore.d.ts.map +1 -1
  205. package/dist/restore.js +9 -37
  206. package/dist/restore.js.map +1 -1
  207. package/dist/resume-launch-options.d.ts +3 -3
  208. package/dist/resume-launch-options.d.ts.map +1 -1
  209. package/dist/resume-launch-options.js +7 -4
  210. package/dist/resume-launch-options.js.map +1 -1
  211. package/dist/run.d.ts +2 -0
  212. package/dist/run.d.ts.map +1 -1
  213. package/dist/run.js +88 -20
  214. package/dist/run.js.map +1 -1
  215. package/dist/soft-refresh.d.ts.map +1 -1
  216. package/dist/soft-refresh.js +6 -0
  217. package/dist/soft-refresh.js.map +1 -1
  218. package/dist/terminal-reset.d.ts +104 -0
  219. package/dist/terminal-reset.d.ts.map +1 -0
  220. package/dist/terminal-reset.js +229 -0
  221. package/dist/terminal-reset.js.map +1 -0
  222. package/dist/tmux.d.ts +54 -8
  223. package/dist/tmux.d.ts.map +1 -1
  224. package/dist/tmux.js +143 -36
  225. package/dist/tmux.js.map +1 -1
  226. package/dist/vendor/engram-live/contracts.d.ts +825 -0
  227. package/dist/vendor/engram-live/contracts.js +1734 -0
  228. package/dist/vendor/engram-live/integration.d.ts +113 -0
  229. package/dist/vendor/engram-live/integration.js +254 -0
  230. package/dist/vendor/engram-live/pr-sync.d.ts +144 -0
  231. package/dist/vendor/engram-live/pr-sync.js +411 -0
  232. package/dist/vendor/engram-live/registration.d.ts +138 -0
  233. package/dist/vendor/engram-live/registration.js +358 -0
  234. package/dist/vendor/engram-live/retention.d.ts +56 -0
  235. package/dist/vendor/engram-live/retention.js +179 -0
  236. package/package.json +6 -6
@@ -0,0 +1,358 @@
1
+ // Vendored from rhanka/engram@545a57b8911ab108e84fa1778e0f268fe11bf1de: engram-live/registration.ts.
2
+ // Compiled with TypeScript 5.9.3 (ES2022/NodeNext, declarations); do not hand-edit.
3
+ // @sentropic/engram-live/registration — pure neutral registration helpers
4
+ // (spec/SPEC_LIVE_GIT_INDEXING.md §§2–3, 10, 12; plan/09 L6a; owner Q4 A).
5
+ //
6
+ // Pure functions over ./contracts.js DTOs: declaration creation, revision,
7
+ // revocation, the ONE canonical clone/worktree/declaration identity
8
+ // derivation, clone equality and lifecycle classification. No host state, no
9
+ // storage, no network, no projection provider: every function is
10
+ // deterministic over its arguments, never mutates its inputs, and keeps no
11
+ // module-level mutable state. Durable declarations, replay, cancellation
12
+ // and purge execution belong to the h2a (H) and cluster-mesh/sentropic (S)
13
+ // hosts consuming the contract documented in ./contracts.js.
14
+ //
15
+ // Both seats mint their opaque ids through these functions (canonicalIdKey
16
+ // in ./contracts.js + the sha256 minting below): identical identity
17
+ // components give byte-identical cloneId/worktreeId/declarationId on every
18
+ // side, so identity equality is comparison by canonical identifier. h2a
19
+ // identifiers persisted before this freeze were derived from
20
+ // newline-joined components; re-derive and rewrite them locally
21
+ // (see the MIGRATION note in ./contracts.js).
22
+ import { createHash } from "node:crypto";
23
+ import { ID_PREFIXES, IDENTITY_UNCERTAINTY_STATES, canonicalIdKey, isIdentityUncertaintyState, isNonActivatingLifecycleKind, isRevocationReason, normalizeCommonDirRealpath, validateCloneIdentity, validateDeclarationRevocation, validateWorkspaceDeclaration, validateWorktreeIdentity, INITIAL_BINDING_REVISION, INITIAL_DECLARATION_REVISION, } from "./contracts.js";
24
+ const LIVE_DECLARATION_PATCH_KEYS = [
25
+ "authorizedRoots",
26
+ "policyVersion",
27
+ "serverWorkspaceId",
28
+ "serverIssuer",
29
+ "bindingRevision",
30
+ ];
31
+ function isRecord(value) {
32
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33
+ }
34
+ function isNonEmptyString(value) {
35
+ return typeof value === "string" && value.trim().length > 0;
36
+ }
37
+ function isCloneIdReference(value) {
38
+ return isNonEmptyString(value) && value.startsWith(ID_PREFIXES.clone);
39
+ }
40
+ function isWorktreeIdReference(value) {
41
+ return isNonEmptyString(value) && value.startsWith(ID_PREFIXES.worktree);
42
+ }
43
+ function sha256Hex(canonical) {
44
+ return createHash("sha256").update(canonical, "utf8").digest("hex");
45
+ }
46
+ function checkUncertainty(uncertainty) {
47
+ const states = [...(uncertainty ?? [])];
48
+ if (states.some((state) => !isIdentityUncertaintyState(state))) {
49
+ throw new Error(`identity uncertainty states must be one of ${IDENTITY_UNCERTAINTY_STATES.join(", ")}`);
50
+ }
51
+ return states;
52
+ }
53
+ /**
54
+ * Create the initial live declaration for one workspace. Requires the
55
+ * explicit "live" capability and the full identity triangle
56
+ * (declarationId/cloneId/worktreeId, minted by the canonical derivation
57
+ * below): any other opt-in value (bind, upload, session kinds) is refused,
58
+ * so no lifecycle event can implicitly activate live. The declaration
59
+ * revision starts at the frozen INITIAL_DECLARATION_REVISION.
60
+ */
61
+ export function createLiveDeclaration(input) {
62
+ if (!isRecord(input)) {
63
+ throw new Error("createLiveDeclaration requires a declaration input object");
64
+ }
65
+ const declaration = {
66
+ tenant: input.tenant,
67
+ principal: input.principal,
68
+ ...(input.hostFingerprint !== undefined ? { hostFingerprint: input.hostFingerprint } : {}),
69
+ identityUncertainty: [...input.identityUncertainty],
70
+ declarationId: input.declarationId,
71
+ cloneId: input.cloneId,
72
+ worktreeId: input.worktreeId,
73
+ authorizedRoots: [...input.authorizedRoots],
74
+ liveCapability: input.liveCapability,
75
+ policyVersion: input.policyVersion,
76
+ declarationRevision: INITIAL_DECLARATION_REVISION,
77
+ ...(input.serverWorkspaceId !== undefined ? { serverWorkspaceId: input.serverWorkspaceId } : {}),
78
+ ...(input.serverIssuer !== undefined ? { serverIssuer: input.serverIssuer } : {}),
79
+ ...(input.bindingRevision !== undefined ? { bindingRevision: input.bindingRevision } : {}),
80
+ };
81
+ if (input.bindingRevision !== undefined && input.bindingRevision !== INITIAL_BINDING_REVISION) {
82
+ throw new Error(`createLiveDeclaration requires the first product binding to carry bindingRevision ${INITIAL_BINDING_REVISION} (FROZEN L6a convention; rebinds and revalidations grow strictly from there)`);
83
+ }
84
+ const checked = validateWorkspaceDeclaration(declaration);
85
+ if (!checked.ok) {
86
+ throw new Error(`invalid live workspace declaration: ${checked.errors.join("; ")}`);
87
+ }
88
+ return declaration;
89
+ }
90
+ /**
91
+ * Revise a declaration as a pure revision increment. The revision grows by
92
+ * exactly one; tenant, principal, hostFingerprint, the identity triangle
93
+ * and the live capability are immutable. The product binding is the
94
+ * complete (serverWorkspaceId, serverIssuer, bindingRevision) triplet.
95
+ * A revision outside the binding (roots, policy version) PRESERVES the
96
+ * binding: no renewal is imposed. Only a change of serverWorkspaceId or
97
+ * serverIssuer, or an explicit bindingRevision revalidation, renews the
98
+ * binding — and then strictly increasingly: the first binding carries
99
+ * exactly INITIAL_BINDING_REVISION, and every rebind/revalidation carries
100
+ * a strictly greater bindingRevision; binding revisions never go
101
+ * backwards. The host must additionally refuse to redirect in-flight
102
+ * collection/publication when a rebind lands.
103
+ */
104
+ export function reviseLiveDeclaration(previous, patch) {
105
+ const previousCheck = validateWorkspaceDeclaration(previous);
106
+ if (!previousCheck.ok) {
107
+ throw new Error(`invalid previous declaration: ${previousCheck.errors.join("; ")}`);
108
+ }
109
+ if (!isRecord(patch)) {
110
+ throw new Error("reviseLiveDeclaration requires a patch object");
111
+ }
112
+ const patchRecord = patch;
113
+ for (const key of Object.keys(patchRecord)) {
114
+ if (!LIVE_DECLARATION_PATCH_KEYS.includes(key)) {
115
+ throw new Error(`reviseLiveDeclaration refuses patch key "${key}": identity fields are immutable across revisions`);
116
+ }
117
+ }
118
+ const workspaceChanged = patch.serverWorkspaceId !== undefined &&
119
+ patch.serverWorkspaceId !== previous.serverWorkspaceId;
120
+ const issuerChanged = patch.serverIssuer !== undefined &&
121
+ patch.serverIssuer !== previous.serverIssuer;
122
+ const bindingTouched = patch.bindingRevision !== undefined || workspaceChanged || issuerChanged;
123
+ // A revision outside the product binding preserves it; only a workspace or
124
+ // issuer change, or an explicit revalidation, renews it — strictly
125
+ // increasingly, starting the first binding at INITIAL_BINDING_REVISION.
126
+ let nextBindingRevision = previous.bindingRevision;
127
+ if (bindingTouched) {
128
+ if (patch.bindingRevision === undefined) {
129
+ throw new Error("reviseLiveDeclaration requires a fresh strictly increasing bindingRevision when the server workspace or issuer changes (revalidation)");
130
+ }
131
+ if (previous.bindingRevision === undefined) {
132
+ if (patch.bindingRevision !== INITIAL_BINDING_REVISION) {
133
+ throw new Error(`reviseLiveDeclaration requires the first product binding to carry bindingRevision ${INITIAL_BINDING_REVISION} (FROZEN L6a convention)`);
134
+ }
135
+ }
136
+ else if (patch.bindingRevision <= previous.bindingRevision) {
137
+ throw new Error("reviseLiveDeclaration requires a strictly increasing bindingRevision (binding revisions are monotonic)");
138
+ }
139
+ nextBindingRevision = patch.bindingRevision;
140
+ }
141
+ const next = {
142
+ ...previous,
143
+ authorizedRoots: patch.authorizedRoots !== undefined ? [...patch.authorizedRoots] : [...previous.authorizedRoots],
144
+ policyVersion: patch.policyVersion !== undefined ? patch.policyVersion : previous.policyVersion,
145
+ serverWorkspaceId: patch.serverWorkspaceId !== undefined
146
+ ? patch.serverWorkspaceId
147
+ : previous.serverWorkspaceId,
148
+ serverIssuer: patch.serverIssuer !== undefined ? patch.serverIssuer : previous.serverIssuer,
149
+ bindingRevision: nextBindingRevision,
150
+ declarationRevision: previous.declarationRevision + 1,
151
+ };
152
+ const checked = validateWorkspaceDeclaration(next);
153
+ if (!checked.ok) {
154
+ throw new Error(`invalid revised declaration: ${checked.errors.join("; ")}`);
155
+ }
156
+ return next;
157
+ }
158
+ /**
159
+ * Revoke exactly one declaration as a pure private-purge request. The
160
+ * returned DTO names the single target by its opaque declarationId and
161
+ * carries the declaration's cloneId/worktreeId attachment and revision, so
162
+ * the recipient can never confuse two declarations of one tenant that
163
+ * happen to share public fingerprints or revisions. The host stops work,
164
+ * rejects delayed jobs and purges private snapshots, derived indexes and
165
+ * decryptability, while preserving authorized committed history.
166
+ * Revocation deletes no committed facts.
167
+ */
168
+ export function revokeLiveDeclaration(declaration, reason) {
169
+ const checked = validateWorkspaceDeclaration(declaration);
170
+ if (!checked.ok) {
171
+ throw new Error(`invalid declaration: ${checked.errors.join("; ")}`);
172
+ }
173
+ if (!isRevocationReason(reason)) {
174
+ throw new Error(`invalid revocation reason: ${String(reason)}`);
175
+ }
176
+ const revocation = {
177
+ declarationId: declaration.declarationId,
178
+ tenant: declaration.tenant,
179
+ cloneId: declaration.cloneId,
180
+ worktreeId: declaration.worktreeId,
181
+ declarationRevision: declaration.declarationRevision,
182
+ reason,
183
+ requestPrivatePurge: {
184
+ purgePrivateSnapshots: true,
185
+ purgeDerivedIndexes: true,
186
+ revokeDecryptability: true,
187
+ preserveCommittedHistory: true,
188
+ },
189
+ };
190
+ const revocationCheck = validateDeclarationRevocation(revocation);
191
+ if (!revocationCheck.ok) {
192
+ throw new Error(`invalid revocation: ${revocationCheck.errors.join("; ")}`);
193
+ }
194
+ return revocation;
195
+ }
196
+ /**
197
+ * Derive the clone identity for one common-dir — the ONE canonical clone
198
+ * derivation. The key components (hostScope, normalized common-dir
199
+ * realpath, common-dir project fingerprint) serialize through
200
+ * canonicalIdKey (unambiguous, NUL-refusing) and hash to the opaque
201
+ * cloneId: equal fingerprints under different realpaths derive distinct
202
+ * clones, while linked worktrees sharing one common-dir derive the same
203
+ * clone. The fingerprint may be absent only under explicit
204
+ * "missing_history" uncertainty; the hostScope + realpath components keep
205
+ * clones distinct even then (LIVE §12). The realpath stays host-local;
206
+ * only cloneId is shared. Deterministic and free of I/O.
207
+ */
208
+ export function deriveCloneIdentity(input) {
209
+ if (!isRecord(input)) {
210
+ throw new Error("deriveCloneIdentity requires a clone input object");
211
+ }
212
+ if (!isNonEmptyString(input.hostScope)) {
213
+ throw new Error("deriveCloneIdentity requires a non-empty hostScope");
214
+ }
215
+ if (!isNonEmptyString(input.commonDirRealpath)) {
216
+ throw new Error("deriveCloneIdentity requires a non-empty commonDirRealpath");
217
+ }
218
+ const uncertainty = checkUncertainty(input.identityUncertainty);
219
+ if (input.commonDirProjectFingerprint !== undefined &&
220
+ !isNonEmptyString(input.commonDirProjectFingerprint)) {
221
+ throw new Error("deriveCloneIdentity requires a non-empty commonDirProjectFingerprint when present");
222
+ }
223
+ if (input.commonDirProjectFingerprint === undefined &&
224
+ !uncertainty.includes("missing_history")) {
225
+ throw new Error('deriveCloneIdentity omits commonDirProjectFingerprint only under explicit "missing_history" uncertainty');
226
+ }
227
+ if (input.oidAlgorithm !== "sha1" && input.oidAlgorithm !== "sha256") {
228
+ throw new Error('deriveCloneIdentity requires oidAlgorithm "sha1" or "sha256"');
229
+ }
230
+ const normalizedRealpath = normalizeCommonDirRealpath(input.commonDirRealpath);
231
+ // canonicalIdKey refuses NUL inside any component.
232
+ const canonical = canonicalIdKey("clone", [
233
+ input.hostScope,
234
+ normalizedRealpath,
235
+ input.commonDirProjectFingerprint ?? "",
236
+ ]);
237
+ const identity = {
238
+ hostScope: input.hostScope,
239
+ commonDirRealpath: normalizedRealpath,
240
+ ...(input.commonDirProjectFingerprint !== undefined
241
+ ? { commonDirProjectFingerprint: input.commonDirProjectFingerprint }
242
+ : {}),
243
+ cloneId: `${ID_PREFIXES.clone}${sha256Hex(canonical)}`,
244
+ oidAlgorithm: input.oidAlgorithm,
245
+ identityUncertainty: [...uncertainty],
246
+ };
247
+ const checked = validateCloneIdentity(identity);
248
+ if (!checked.ok) {
249
+ throw new Error(`invalid derived clone identity: ${checked.errors.join("; ")}`);
250
+ }
251
+ return identity;
252
+ }
253
+ /**
254
+ * Derive the identity of one worktree under an already derived clone —
255
+ * the ONE canonical worktree derivation. Worktrees of one clone share
256
+ * cloneId and keep an isolated host-scoped worktreeId plus per-worktree
257
+ * git-dir, fingerprint and path. The worktree fingerprint may be absent
258
+ * only under explicit "missing_history" uncertainty (LIVE §12).
259
+ */
260
+ export function deriveWorktreeIdentity(input) {
261
+ if (!isRecord(input)) {
262
+ throw new Error("deriveWorktreeIdentity requires a worktree input object");
263
+ }
264
+ if (!isRecord(input.clone) || !isCloneIdReference(input.clone.cloneId)) {
265
+ throw new Error("deriveWorktreeIdentity requires a derived clone carrying a canonical cloneId");
266
+ }
267
+ if (!isNonEmptyString(input.hostScope)) {
268
+ throw new Error("deriveWorktreeIdentity requires a non-empty hostScope");
269
+ }
270
+ if (!isNonEmptyString(input.worktreePath)) {
271
+ throw new Error("deriveWorktreeIdentity requires a non-empty worktreePath");
272
+ }
273
+ if (!isNonEmptyString(input.gitDir)) {
274
+ throw new Error("deriveWorktreeIdentity requires a non-empty gitDir");
275
+ }
276
+ const uncertainty = checkUncertainty(input.identityUncertainty);
277
+ if (input.worktreeFingerprint !== undefined &&
278
+ !isNonEmptyString(input.worktreeFingerprint)) {
279
+ throw new Error("deriveWorktreeIdentity requires a non-empty worktreeFingerprint when present");
280
+ }
281
+ if (input.worktreeFingerprint === undefined && !uncertainty.includes("missing_history")) {
282
+ throw new Error('deriveWorktreeIdentity omits worktreeFingerprint only under explicit "missing_history" uncertainty');
283
+ }
284
+ const canonical = canonicalIdKey("worktree", [input.hostScope, input.gitDir]);
285
+ const identity = {
286
+ cloneId: input.clone.cloneId,
287
+ worktreeId: `${ID_PREFIXES.worktree}${sha256Hex(canonical)}`,
288
+ worktreePath: input.worktreePath,
289
+ gitDir: input.gitDir,
290
+ ...(input.worktreeFingerprint !== undefined
291
+ ? { worktreeFingerprint: input.worktreeFingerprint }
292
+ : {}),
293
+ identityUncertainty: [...uncertainty],
294
+ };
295
+ const checked = validateWorktreeIdentity(identity);
296
+ if (!checked.ok) {
297
+ throw new Error(`invalid derived worktree identity: ${checked.errors.join("; ")}`);
298
+ }
299
+ return identity;
300
+ }
301
+ /**
302
+ * Derive the identity of one declaration — the ONE canonical declaration
303
+ * derivation. One declaration per activated worktree: the declarationId
304
+ * binds the host scope, the clone and the worktree, so two worktrees of
305
+ * one clone (and two clones of one project) derive distinct declarationIds
306
+ * even without fingerprints. Stable across every revision of the same
307
+ * declaration. Deterministic and free of I/O.
308
+ */
309
+ export function deriveDeclarationIdentity(input) {
310
+ if (!isRecord(input)) {
311
+ throw new Error("deriveDeclarationIdentity requires a declaration identity input object");
312
+ }
313
+ if (!isRecord(input.clone) || !isCloneIdReference(input.clone.cloneId)) {
314
+ throw new Error("deriveDeclarationIdentity requires a derived clone carrying a canonical cloneId");
315
+ }
316
+ if (!isRecord(input.worktree) || !isWorktreeIdReference(input.worktree.worktreeId)) {
317
+ throw new Error("deriveDeclarationIdentity requires a derived worktree carrying a canonical worktreeId");
318
+ }
319
+ if (!isNonEmptyString(input.hostScope)) {
320
+ throw new Error("deriveDeclarationIdentity requires a non-empty hostScope");
321
+ }
322
+ const canonical = canonicalIdKey("declaration", [
323
+ input.hostScope,
324
+ input.clone.cloneId,
325
+ input.worktree.worktreeId,
326
+ ]);
327
+ const identity = {
328
+ declarationId: `${ID_PREFIXES.declaration}${sha256Hex(canonical)}`,
329
+ cloneId: input.clone.cloneId,
330
+ worktreeId: input.worktree.worktreeId,
331
+ };
332
+ return identity;
333
+ }
334
+ /**
335
+ * Decide whether two clone references denote the same clone, by canonical
336
+ * identifier only: the cloneId minted by the shared canonical derivation is
337
+ * the clone identity, so the comparison is an equivalence (transitive and
338
+ * consistent between full DTOs and opaque references). Throws when either
339
+ * reference lacks a cloneId — equality is never guessed from components.
340
+ */
341
+ export function isSameClone(a, b) {
342
+ if (!isNonEmptyString(a.cloneId) || !isNonEmptyString(b.cloneId)) {
343
+ throw new Error("isSameClone compares canonical clone ids: both references must carry a non-empty cloneId");
344
+ }
345
+ return a.cloneId === b.cloneId;
346
+ }
347
+ /**
348
+ * Classify a host lifecycle event against live declarations. Bind, upload,
349
+ * code-workspace creation and session open/end never activate or remove a
350
+ * declaration: the only outcome is "no-activation-change". Unknown kinds
351
+ * are refused.
352
+ */
353
+ export function classifyLifecycleEvent(kind) {
354
+ if (!isNonActivatingLifecycleKind(kind)) {
355
+ throw new Error(`unknown lifecycle kind: ${String(kind)}`);
356
+ }
357
+ return "no-activation-change";
358
+ }
@@ -0,0 +1,56 @@
1
+ // Vendored from rhanka/engram@545a57b8911ab108e84fa1778e0f268fe11bf1de: engram-live/retention.ts.
2
+ // Compiled with TypeScript 5.9.3 (ES2022/NodeNext, declarations); do not hand-edit.
3
+ import { type LiveScope, type RetentionCoverageReportDto, type RetentionReceiptDto, type RetentionReceiptQuery, type RetentionRequestDto, type SelectedRootDto } from "./contracts.js";
4
+ export declare class RetentionError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * THE injected retention port (plan/09 R1: a port, never a base). Engram
9
+ * defines the contract only; h2a implements durable acquisition and the
10
+ * private store behind it. Receipts are scoped by tenant + clone and
11
+ * durable across passes; the port never carries file content.
12
+ */
13
+ export interface RetentionPort {
14
+ recordRequest(request: RetentionRequestDto): Promise<{
15
+ received: true;
16
+ }>;
17
+ recordReceipt(receipt: RetentionReceiptDto): Promise<{
18
+ received: true;
19
+ }>;
20
+ loadReceipts(query: RetentionReceiptQuery): Promise<{
21
+ receipts: readonly RetentionReceiptDto[];
22
+ }>;
23
+ }
24
+ export interface RetentionPassInput {
25
+ readonly roots: readonly SelectedRootDto[];
26
+ readonly scope: LiveScope;
27
+ /** Injected evaluation time for requestedAt (ms); the engine reads no clock. */
28
+ readonly now: number;
29
+ }
30
+ export interface RetentionPassStats {
31
+ readonly requests: number;
32
+ readonly receipts: number;
33
+ readonly missing: number;
34
+ }
35
+ export interface RetentionPassOutcome {
36
+ /** "complete": every request has its scoped durable receipt. "gaps": otherwise. */
37
+ readonly state: "complete" | "gaps";
38
+ readonly requests: readonly RetentionRequestDto[];
39
+ readonly coverage: readonly RetentionCoverageReportDto[];
40
+ readonly stats: RetentionPassStats;
41
+ }
42
+ /**
43
+ * Derive one retention request per distinct root OID, sorted by OID.
44
+ * Pure and total over valid inputs: no count cap, no age filter, no
45
+ * pruning — an old default branch, an expired window branch kept as a
46
+ * previously-ingested root, a PR head of any state and a detached HEAD
47
+ * each yield exactly one request.
48
+ */
49
+ export declare function deriveRetentionRequests(roots: readonly SelectedRootDto[], scope: LiveScope, now: number): RetentionRequestDto[];
50
+ /**
51
+ * Run one retention pass: record every request, then require every scoped
52
+ * durable receipt. Deterministic over the injected inputs; the port is the
53
+ * only effect. A receipt satisfies a request only under the exact same
54
+ * tenant, clone, algorithm and OID; cross-scope receipts never satisfy.
55
+ */
56
+ export declare function runRetentionPass(input: RetentionPassInput, port: RetentionPort): Promise<RetentionPassOutcome>;
@@ -0,0 +1,179 @@
1
+ // Vendored from rhanka/engram@545a57b8911ab108e84fa1778e0f268fe11bf1de: engram-live/retention.ts.
2
+ // Compiled with TypeScript 5.9.3 (ES2022/NodeNext, declarations); do not hand-edit.
3
+ // @sentropic/engram-live/retention — R1 private retention of every ingested root
4
+ // (spec/SPEC_LIVE_GIT_INDEXING.md §4, A02/A04; plan/09 R1).
5
+ //
6
+ // The neutral retention engine. Engram derives one scoped retention request
7
+ // per distinct ingested root OID and requires one scoped durable receipt
8
+ // before any reconstructible-coverage claim:
9
+ //
10
+ // 1. derive the requests from the L2b selected roots (./refs.js): default
11
+ // branch even when old, active local/remote branches, PR heads of all
12
+ // states, detached worktree HEADs, previously ingested roots — with no
13
+ // commit-count and no commit-age limit, and nothing ever erased here;
14
+ // 2. record every request through the INJECTED retention port (a port,
15
+ // never a base; engram owns no database and no object store);
16
+ // 3. load the scoped durable receipts from that same port and classify
17
+ // coverage: every request needs its tenant + clone receipt; a missing
18
+ // receipt is an explicit object-availability gap, never a silent
19
+ // current claim.
20
+ //
21
+ // Neutrality: this module imports ONLY ./contracts.js (data-pure DTOs and
22
+ // validators). No h2a, no cluster-mesh, no sentropic, no storage driver,
23
+ // no network, no filesystem, no clock read — the evaluation time `now` is
24
+ // injected, and durability comes from the host port alone.
25
+ //
26
+ // Host contract (H: live-object-store.ts, delegate.ts — NOT implemented
27
+ // here): h2a consumes RetentionRequestDto through RetentionPort, acquires
28
+ // the pinned committed objects into its protected private Git store under
29
+ // private refs (no writes into the observed clone, no borrowed alternates
30
+ // store, no repository-controlled programs), then records a
31
+ // RetentionReceiptDto with an opaque coordinated-backup backupRef for L8.
32
+ // If removal wins the acquisition race, H records no receipt and the engine
33
+ // reports the gap; quotas pause further work without deleting protected
34
+ // roots. P1 reuses this facility for PR roots. Uncommitted plaintext never
35
+ // enters a request or a receipt: requests carry OIDs only, by construction.
36
+ import { RETENTION_COVERAGE_AXIS, validateLiveScope, validateQualifiedOid, validateRetentionReceipt, validateRetentionRequest, } from "./contracts.js";
37
+ export class RetentionError extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "RetentionError";
41
+ }
42
+ }
43
+ function checkScope(scope) {
44
+ const checked = validateLiveScope(scope);
45
+ if (!checked.ok) {
46
+ throw new RetentionError(`invalid retention scope: ${checked.errors.join("; ")}`);
47
+ }
48
+ if (scope.worktree !== undefined) {
49
+ throw new RetentionError("retention scope must be clone-scoped: worktree must be absent (retention is per tenant + clone)");
50
+ }
51
+ }
52
+ function checkNow(now) {
53
+ if (typeof now !== "number" || !Number.isSafeInteger(now) || now <= 0) {
54
+ throw new RetentionError("retention pass requires a positive safe-integer now (milliseconds)");
55
+ }
56
+ }
57
+ /**
58
+ * Derive one retention request per distinct root OID, sorted by OID.
59
+ * Pure and total over valid inputs: no count cap, no age filter, no
60
+ * pruning — an old default branch, an expired window branch kept as a
61
+ * previously-ingested root, a PR head of any state and a detached HEAD
62
+ * each yield exactly one request.
63
+ */
64
+ export function deriveRetentionRequests(roots, scope, now) {
65
+ checkScope(scope);
66
+ checkNow(now);
67
+ // Immutable snapshot: the caller-owned scope object may be shared and
68
+ // mutated while the pass awaits the port; every derived request carries
69
+ // the pass-entry scope, never a later mutation.
70
+ const frozen = { tenant: scope.tenant, clone: scope.clone };
71
+ const seen = new Set();
72
+ const requests = [];
73
+ for (const root of roots) {
74
+ const checked = validateQualifiedOid(root.qualifiedOid);
75
+ if (!checked.ok) {
76
+ throw new RetentionError(`invalid root OID ${root.qualifiedOid.oid}: ${checked.errors.join("; ")}`);
77
+ }
78
+ if (seen.has(root.qualifiedOid.oid))
79
+ continue;
80
+ seen.add(root.qualifiedOid.oid);
81
+ if (root.sources.length === 0) {
82
+ throw new RetentionError(`root ${root.qualifiedOid.oid} carries no provenance source`);
83
+ }
84
+ const first = root.sources[0];
85
+ const refBacked = root.sources.find((source) => source.refName !== null);
86
+ const request = {
87
+ scope: { tenant: frozen.tenant, clone: frozen.clone },
88
+ algorithm: root.qualifiedOid.algorithm,
89
+ oid: root.qualifiedOid.oid,
90
+ firstSignal: first.signal,
91
+ refName: refBacked !== undefined ? refBacked.refName : null,
92
+ requestedAt: now,
93
+ };
94
+ const valid = validateRetentionRequest(request);
95
+ if (!valid.ok) {
96
+ throw new RetentionError(`derived invalid retention request for ${request.oid}: ${valid.errors.join("; ")}`);
97
+ }
98
+ requests.push(request);
99
+ }
100
+ requests.sort((left, right) => left.oid.localeCompare(right.oid));
101
+ return requests;
102
+ }
103
+ function receiptKey(receipt) {
104
+ return `${receipt.scope.tenant}:${receipt.scope.clone}:${receipt.algorithm}:${receipt.oid}`;
105
+ }
106
+ /**
107
+ * Run one retention pass: record every request, then require every scoped
108
+ * durable receipt. Deterministic over the injected inputs; the port is the
109
+ * only effect. A receipt satisfies a request only under the exact same
110
+ * tenant, clone, algorithm and OID; cross-scope receipts never satisfy.
111
+ */
112
+ export async function runRetentionPass(input, port) {
113
+ checkScope(input.scope);
114
+ checkNow(input.now);
115
+ // Immutable snapshot taken before the first await: the caller-owned
116
+ // input.scope object may be shared and mutated while the pass awaits the
117
+ // port. Every query, receipt check and missing-key below uses this
118
+ // pass-entry scope; re-reading input.scope after an await would let a
119
+ // foreign-scope receipt falsely satisfy a request (false acquittement).
120
+ const scope = { tenant: input.scope.tenant, clone: input.scope.clone };
121
+ const requests = deriveRetentionRequests(input.roots, scope, input.now);
122
+ for (const request of requests) {
123
+ await port.recordRequest(request);
124
+ }
125
+ const algorithms = new Set();
126
+ for (const request of requests)
127
+ algorithms.add(request.algorithm);
128
+ const receipts = [];
129
+ if (algorithms.size === 0) {
130
+ return { state: "complete", requests, coverage: [], stats: { requests: 0, receipts: 0, missing: 0 } };
131
+ }
132
+ for (const algorithm of algorithms) {
133
+ const loaded = await port.loadReceipts({ scope, algorithm });
134
+ for (const receipt of loaded.receipts) {
135
+ const checked = validateRetentionReceipt(receipt);
136
+ if (!checked.ok) {
137
+ throw new RetentionError(`invalid retention receipt for ${receipt.oid}: ${checked.errors.join("; ")}`);
138
+ }
139
+ // The port is scoped by the query, but the engine re-checks scope:
140
+ // a foreign receipt that slipped through never satisfies a request.
141
+ if (receipt.scope.tenant !== scope.tenant || receipt.scope.clone !== scope.clone)
142
+ continue;
143
+ if (receipt.algorithm !== algorithm)
144
+ continue;
145
+ receipts.push(receipt);
146
+ }
147
+ }
148
+ const byKey = new Map();
149
+ for (const receipt of receipts)
150
+ byKey.set(receiptKey(receipt), receipt);
151
+ const missing = [];
152
+ for (const request of requests) {
153
+ const key = `${scope.tenant}:${scope.clone}:${request.algorithm}:${request.oid}`;
154
+ if (!byKey.has(key))
155
+ missing.push({ algorithm: request.algorithm, oid: request.oid });
156
+ }
157
+ missing.sort((left, right) => left.oid.localeCompare(right.oid));
158
+ if (missing.length === 0) {
159
+ return {
160
+ state: "complete",
161
+ requests,
162
+ coverage: [],
163
+ stats: { requests: requests.length, receipts: byKey.size, missing: 0 },
164
+ };
165
+ }
166
+ const preview = missing.slice(0, 3).map((entry) => entry.oid.slice(0, 12)).join(", ");
167
+ return {
168
+ state: "gaps",
169
+ requests,
170
+ coverage: [{
171
+ axis: RETENTION_COVERAGE_AXIS,
172
+ state: "unavailable",
173
+ reason: `private retention incomplete: ${missing.length} of ${requests.length} ingested root(s) without a scoped durable retention receipt (${preview}…); no reconstructible coverage is claimed and nothing was pruned`,
174
+ cause: "missing-receipt",
175
+ missing,
176
+ }],
177
+ stats: { requests: requests.length, receipts: byKey.size, missing: missing.length },
178
+ };
179
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/h2a-runtime",
3
- "version": "0.98.6",
3
+ "version": "0.98.7-rc.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -16,14 +16,14 @@
16
16
  "dependencies": {
17
17
  "@aws-sdk/client-s3": "^3.1075.0",
18
18
  "@hono/node-server": "^2.0.12",
19
- "@sentropic/cluster-mesh": "0.13.3",
20
- "@sentropic/llm-gateway": "0.19.5",
21
- "@sentropic/llm-mesh": "0.25.0",
19
+ "@sentropic/cluster-mesh": "0.16.0",
20
+ "@sentropic/llm-gateway": "0.20.0",
21
+ "@sentropic/llm-mesh": "0.26.0",
22
22
  "@xterm/headless": "^6.0.0",
23
23
  "ajv": "^8.20.0",
24
24
  "ajv-formats": "^3.0.1",
25
25
  "commander": "^15.0.0",
26
- "hono": "^4.13.7",
26
+ "hono": "^4.13.13",
27
27
  "node-pty": "^1.1.0",
28
28
  "ws": "^8.20.1"
29
29
  },
@@ -54,6 +54,6 @@
54
54
  "main": "./dist/index.js",
55
55
  "types": "./dist/index.d.ts",
56
56
  "scripts": {
57
- "build": "node ../../scripts/clean-workspace-dist.mjs && tsc -b --force"
57
+ "build": "node ../../scripts/clean-workspace-dist.mjs && tsc -b --force && node ../../scripts/copy-vendored-engram-live.mjs"
58
58
  }
59
59
  }