@corenel/harness 0.1.0 → 0.3.2

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 (180) hide show
  1. package/dist/acp/agentMap.d.ts +34 -0
  2. package/dist/acp/agentMap.d.ts.map +1 -0
  3. package/dist/acp/agentMap.js +110 -0
  4. package/dist/acp/agentMap.js.map +1 -0
  5. package/dist/acp/index.d.ts +4 -0
  6. package/dist/acp/index.d.ts.map +1 -0
  7. package/dist/acp/index.js +6 -0
  8. package/dist/acp/index.js.map +1 -0
  9. package/dist/acp/policyMap.d.ts +55 -0
  10. package/dist/acp/policyMap.d.ts.map +1 -0
  11. package/dist/acp/policyMap.js +121 -0
  12. package/dist/acp/policyMap.js.map +1 -0
  13. package/dist/acp/types.d.ts +41 -0
  14. package/dist/acp/types.d.ts.map +1 -0
  15. package/dist/acp/types.js +14 -0
  16. package/dist/acp/types.js.map +1 -0
  17. package/dist/api/backend.d.ts +39 -0
  18. package/dist/api/backend.d.ts.map +1 -0
  19. package/dist/api/backend.js +126 -0
  20. package/dist/api/backend.js.map +1 -0
  21. package/dist/api/client.d.ts +95 -0
  22. package/dist/api/client.d.ts.map +1 -0
  23. package/dist/api/client.js +212 -0
  24. package/dist/api/client.js.map +1 -0
  25. package/dist/api/fileMirrorSource.d.ts +24 -0
  26. package/dist/api/fileMirrorSource.d.ts.map +1 -0
  27. package/dist/api/fileMirrorSource.js +59 -0
  28. package/dist/api/fileMirrorSource.js.map +1 -0
  29. package/dist/api/mirror.d.ts +102 -0
  30. package/dist/api/mirror.d.ts.map +1 -0
  31. package/dist/api/mirror.js +204 -0
  32. package/dist/api/mirror.js.map +1 -0
  33. package/dist/api/mirrorScheduler.d.ts +56 -0
  34. package/dist/api/mirrorScheduler.d.ts.map +1 -0
  35. package/dist/api/mirrorScheduler.js +167 -0
  36. package/dist/api/mirrorScheduler.js.map +1 -0
  37. package/dist/conversation.d.ts +0 -11
  38. package/dist/conversation.d.ts.map +1 -1
  39. package/dist/conversation.js +1 -24
  40. package/dist/conversation.js.map +1 -1
  41. package/dist/core/budget.d.ts +40 -0
  42. package/dist/core/budget.d.ts.map +1 -0
  43. package/dist/core/budget.js +62 -0
  44. package/dist/core/budget.js.map +1 -0
  45. package/dist/core/loop.d.ts +11 -0
  46. package/dist/core/loop.d.ts.map +1 -1
  47. package/dist/core/loop.js +89 -20
  48. package/dist/core/loop.js.map +1 -1
  49. package/dist/core/pricing.d.ts +8 -0
  50. package/dist/core/pricing.d.ts.map +1 -0
  51. package/dist/core/pricing.js +12 -0
  52. package/dist/core/pricing.js.map +1 -0
  53. package/dist/core/recovery.d.ts +60 -0
  54. package/dist/core/recovery.d.ts.map +1 -0
  55. package/dist/core/recovery.js +128 -0
  56. package/dist/core/recovery.js.map +1 -0
  57. package/dist/guardrail/builtins.js +6 -6
  58. package/dist/guardrail/builtins.js.map +1 -1
  59. package/dist/guardrail/permission-service.js +1 -1
  60. package/dist/guardrail/permission-service.js.map +1 -1
  61. package/dist/guardrail/serialize.d.ts.map +1 -1
  62. package/dist/guardrail/serialize.js +6 -0
  63. package/dist/guardrail/serialize.js.map +1 -1
  64. package/dist/guardrail/validate.d.ts.map +1 -1
  65. package/dist/guardrail/validate.js +13 -0
  66. package/dist/guardrail/validate.js.map +1 -1
  67. package/dist/install/lockfile.d.ts +79 -0
  68. package/dist/install/lockfile.d.ts.map +1 -0
  69. package/dist/install/lockfile.js +155 -0
  70. package/dist/install/lockfile.js.map +1 -0
  71. package/dist/install/store.d.ts +13 -0
  72. package/dist/install/store.d.ts.map +1 -0
  73. package/dist/install/store.js +38 -0
  74. package/dist/install/store.js.map +1 -0
  75. package/dist/log.d.ts.map +1 -1
  76. package/dist/log.js +6 -1
  77. package/dist/log.js.map +1 -1
  78. package/dist/prompts/host.d.ts +4 -0
  79. package/dist/prompts/host.d.ts.map +1 -1
  80. package/dist/prompts/host.js.map +1 -1
  81. package/dist/prompts/index.d.ts +6 -0
  82. package/dist/prompts/index.d.ts.map +1 -1
  83. package/dist/prompts/index.js +16 -6
  84. package/dist/prompts/index.js.map +1 -1
  85. package/dist/prompts/library/defaults.generated.d.ts.map +1 -1
  86. package/dist/prompts/library/defaults.generated.js +22 -4
  87. package/dist/prompts/library/defaults.generated.js.map +1 -1
  88. package/dist/prompts/toolsMarker.d.ts +13 -0
  89. package/dist/prompts/toolsMarker.d.ts.map +1 -0
  90. package/dist/prompts/toolsMarker.js +21 -0
  91. package/dist/prompts/toolsMarker.js.map +1 -0
  92. package/dist/providers/gateway.d.ts +19 -1
  93. package/dist/providers/gateway.d.ts.map +1 -1
  94. package/dist/providers/gateway.js +33 -4
  95. package/dist/providers/gateway.js.map +1 -1
  96. package/dist/services.d.ts +13 -0
  97. package/dist/services.d.ts.map +1 -1
  98. package/dist/services.js +2 -0
  99. package/dist/services.js.map +1 -1
  100. package/dist/sessions/apiLeaseStore.d.ts +10 -0
  101. package/dist/sessions/apiLeaseStore.d.ts.map +1 -0
  102. package/dist/sessions/apiLeaseStore.js +41 -0
  103. package/dist/sessions/apiLeaseStore.js.map +1 -0
  104. package/dist/sessions/fileRecallStore.d.ts +33 -0
  105. package/dist/sessions/fileRecallStore.d.ts.map +1 -0
  106. package/dist/sessions/fileRecallStore.js +112 -0
  107. package/dist/sessions/fileRecallStore.js.map +1 -0
  108. package/dist/sessions/fileSessionStore.d.ts +20 -0
  109. package/dist/sessions/fileSessionStore.d.ts.map +1 -0
  110. package/dist/sessions/fileSessionStore.js +151 -0
  111. package/dist/sessions/fileSessionStore.js.map +1 -0
  112. package/dist/sessions/head.d.ts +90 -0
  113. package/dist/sessions/head.d.ts.map +1 -0
  114. package/dist/sessions/head.js +82 -0
  115. package/dist/sessions/head.js.map +1 -0
  116. package/dist/sessions/inflight.d.ts +58 -0
  117. package/dist/sessions/inflight.d.ts.map +1 -0
  118. package/dist/sessions/inflight.js +0 -0
  119. package/dist/sessions/inflight.js.map +1 -0
  120. package/dist/sessions/jsonl.d.ts +23 -0
  121. package/dist/sessions/jsonl.d.ts.map +1 -0
  122. package/dist/sessions/jsonl.js +52 -0
  123. package/dist/sessions/jsonl.js.map +1 -0
  124. package/dist/sessions/lease.d.ts +59 -0
  125. package/dist/sessions/lease.d.ts.map +1 -0
  126. package/dist/sessions/lease.js +115 -0
  127. package/dist/sessions/lease.js.map +1 -0
  128. package/dist/sessions/leaseStore.d.ts +46 -0
  129. package/dist/sessions/leaseStore.d.ts.map +1 -0
  130. package/dist/sessions/leaseStore.js +58 -0
  131. package/dist/sessions/leaseStore.js.map +1 -0
  132. package/dist/sessions/testFs.d.ts +5 -0
  133. package/dist/sessions/testFs.d.ts.map +1 -0
  134. package/dist/sessions/testFs.js +85 -0
  135. package/dist/sessions/testFs.js.map +1 -0
  136. package/dist/sessions/timeline.d.ts +134 -0
  137. package/dist/sessions/timeline.d.ts.map +1 -0
  138. package/dist/sessions/timeline.js +127 -0
  139. package/dist/sessions/timeline.js.map +1 -0
  140. package/dist/sidecar/host.d.ts +284 -2
  141. package/dist/sidecar/host.d.ts.map +1 -1
  142. package/dist/sidecar/host.js +725 -1
  143. package/dist/sidecar/host.js.map +1 -1
  144. package/dist/sidecar/server.d.ts +217 -2
  145. package/dist/sidecar/server.d.ts.map +1 -1
  146. package/dist/sidecar/server.js +828 -3
  147. package/dist/sidecar/server.js.map +1 -1
  148. package/dist/skills.d.ts +8 -3
  149. package/dist/skills.d.ts.map +1 -1
  150. package/dist/skills.js +71 -35
  151. package/dist/skills.js.map +1 -1
  152. package/dist/snapshot/effects.d.ts +69 -0
  153. package/dist/snapshot/effects.d.ts.map +1 -0
  154. package/dist/snapshot/effects.js +67 -0
  155. package/dist/snapshot/effects.js.map +1 -0
  156. package/dist/snapshot/manifest.d.ts +73 -0
  157. package/dist/snapshot/manifest.d.ts.map +1 -0
  158. package/dist/snapshot/manifest.js +141 -0
  159. package/dist/snapshot/manifest.js.map +1 -0
  160. package/dist/snapshot/tree.d.ts +62 -0
  161. package/dist/snapshot/tree.d.ts.map +1 -0
  162. package/dist/snapshot/tree.js +147 -0
  163. package/dist/snapshot/tree.js.map +1 -0
  164. package/dist/tools/builtins.d.ts +1 -0
  165. package/dist/tools/builtins.d.ts.map +1 -1
  166. package/dist/tools/builtins.js +150 -5
  167. package/dist/tools/builtins.js.map +1 -1
  168. package/dist/tools/files.d.ts.map +1 -1
  169. package/dist/tools/files.js +13 -0
  170. package/dist/tools/files.js.map +1 -1
  171. package/dist/tools/http.d.ts.map +1 -1
  172. package/dist/tools/http.js.map +1 -1
  173. package/dist/tools/namespaces.d.ts +1 -1
  174. package/dist/tools/namespaces.d.ts.map +1 -1
  175. package/dist/tools/namespaces.js +24 -0
  176. package/dist/tools/namespaces.js.map +1 -1
  177. package/dist/tools/todo.d.ts.map +1 -1
  178. package/dist/tools/todo.js +12 -0
  179. package/dist/tools/todo.js.map +1 -1
  180. package/package.json +26 -34
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.js","sourceRoot":"","sources":["../../sessions/jsonl.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACrC,CAAC;AAED;uCACuC;AACvC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,IAAI,GAA+C,IAAI,CAAC;IAC5D,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,WAAgC,CAAC;IACrC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,IAAI,IAAiB,CAAC;QACtB,IAAI,CAAC;YAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAgB,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAChE,IAAI,IAAI,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACpG,IAAI,GAAG,IAAI,CAAC;YACZ,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,KAAK,aAAa,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YACnE,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;gBAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACvD,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS;gBAAE,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;YAC1E,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,KAAK,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YACvG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS;QACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;QAC9C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ export interface Lease {
2
+ sessionId: string;
3
+ /** Who holds it — a client id, a daemon id, "hosted". For diagnostics and for
4
+ * a human-readable refusal, never for authorization on its own. */
5
+ holder: string;
6
+ /** Monotonic per session. The whole mechanism. */
7
+ epoch: number;
8
+ }
9
+ export declare class StaleLease extends Error {
10
+ readonly sessionId: string;
11
+ readonly held: number;
12
+ readonly current: number;
13
+ constructor(lease: Lease, current: number);
14
+ }
15
+ export interface LeaseSnapshot {
16
+ epoch: number;
17
+ holder: string | null;
18
+ }
19
+ /**
20
+ * Tracks the current lease epoch per session.
21
+ *
22
+ * In-process today. When a session genuinely spans two machines the same record
23
+ * has to live where both can see it (the session directory, or the hosted
24
+ * control plane) — but the RULE does not change, only where the epoch is stored,
25
+ * which is why this is a small pure class rather than a protocol.
26
+ */
27
+ export declare class LeaseRegistry {
28
+ private readonly state;
29
+ /**
30
+ * Take the lease, invalidating whoever held it.
31
+ *
32
+ * Deliberately unconditional. A handoff must be able to complete even when the
33
+ * previous holder is unreachable — a laptop that closed, a container that was
34
+ * killed — and requiring the old holder to cooperate would mean a crashed
35
+ * process could strand a session forever. Safety comes from the OLD holder
36
+ * failing on its next write, not from asking permission.
37
+ */
38
+ acquire(sessionId: string, holder: string): Lease;
39
+ /** Is this lease the current one? */
40
+ isCurrent(lease: Lease): boolean;
41
+ /** Throw unless the lease is current — the guard a writer calls first. */
42
+ assertCurrent(lease: Lease): void;
43
+ /**
44
+ * Give up the lease without handing it to anyone.
45
+ *
46
+ * Bumps the epoch too, so a released lease is as dead as a superseded one —
47
+ * otherwise a holder could release, wander off, and later resume writing with
48
+ * a lease that still looked valid. Releasing a lease that is already stale is a
49
+ * no-op rather than an error: the caller's intent (stop writing) is already
50
+ * satisfied, and throwing would make cleanup paths fragile.
51
+ */
52
+ release(lease: Lease): void;
53
+ current(sessionId: string): LeaseSnapshot;
54
+ /** Sessions with a holder right now. */
55
+ held(): string[];
56
+ /** Test seam. */
57
+ forget(sessionId: string): void;
58
+ }
59
+ //# sourceMappingURL=lease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.d.ts","sourceRoot":"","sources":["../../sessions/lease.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB;uEACmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM;CAU1C;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAE1D;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK;IAMjD,qCAAqC;IACrC,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAIhC,0EAA0E;IAC1E,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAKjC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAM3B,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAIzC,wCAAwC;IACxC,IAAI,IAAI,MAAM,EAAE;IAIhB,iBAAiB;IACjB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAGhC"}
@@ -0,0 +1,115 @@
1
+ /* The write lease — exactly one writer per session, enforced.
2
+ *
3
+ * `JsonlSessionStore` is documented single-writer: save() diffs against an
4
+ * in-memory cursor rather than re-reading the file, so a second concurrent
5
+ * writer is silently clobbered. Spec s4 states the consequence plainly: "If both
6
+ * ends ever hold the same session open, turns are lost with no error. This must
7
+ * be enforced in the protocol, not left to convention."
8
+ *
9
+ * A boolean flag is not enforcement — it cannot tell a stale holder from the
10
+ * current one, and a promote/demote is exactly the moment two processes both
11
+ * believe they hold the session. So the lease carries an EPOCH: taking it bumps
12
+ * the epoch, which makes every previously-issued lease detectably stale. The old
13
+ * holder's next write fails loudly instead of silently winning or silently
14
+ * losing.
15
+ *
16
+ * Pure: no clock, no I/O, no transport. The transport carries the lease; this
17
+ * decides whether it is still good.
18
+ */
19
+ export class StaleLease extends Error {
20
+ constructor(lease, current) {
21
+ super(`write refused: your lease on ${lease.sessionId} is epoch ${lease.epoch}, `
22
+ + `but the session moved to epoch ${current}. Another holder took it — re-read before writing.`);
23
+ Object.defineProperty(this, "sessionId", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: void 0
28
+ });
29
+ Object.defineProperty(this, "held", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: void 0
34
+ });
35
+ Object.defineProperty(this, "current", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: void 0
40
+ });
41
+ this.name = 'StaleLease';
42
+ this.sessionId = lease.sessionId;
43
+ this.held = lease.epoch;
44
+ this.current = current;
45
+ }
46
+ }
47
+ /**
48
+ * Tracks the current lease epoch per session.
49
+ *
50
+ * In-process today. When a session genuinely spans two machines the same record
51
+ * has to live where both can see it (the session directory, or the hosted
52
+ * control plane) — but the RULE does not change, only where the epoch is stored,
53
+ * which is why this is a small pure class rather than a protocol.
54
+ */
55
+ export class LeaseRegistry {
56
+ constructor() {
57
+ Object.defineProperty(this, "state", {
58
+ enumerable: true,
59
+ configurable: true,
60
+ writable: true,
61
+ value: new Map()
62
+ });
63
+ }
64
+ /**
65
+ * Take the lease, invalidating whoever held it.
66
+ *
67
+ * Deliberately unconditional. A handoff must be able to complete even when the
68
+ * previous holder is unreachable — a laptop that closed, a container that was
69
+ * killed — and requiring the old holder to cooperate would mean a crashed
70
+ * process could strand a session forever. Safety comes from the OLD holder
71
+ * failing on its next write, not from asking permission.
72
+ */
73
+ acquire(sessionId, holder) {
74
+ const epoch = (this.state.get(sessionId)?.epoch ?? 0) + 1;
75
+ this.state.set(sessionId, { epoch, holder });
76
+ return { sessionId, holder, epoch };
77
+ }
78
+ /** Is this lease the current one? */
79
+ isCurrent(lease) {
80
+ return this.state.get(lease.sessionId)?.epoch === lease.epoch;
81
+ }
82
+ /** Throw unless the lease is current — the guard a writer calls first. */
83
+ assertCurrent(lease) {
84
+ const current = this.state.get(lease.sessionId)?.epoch ?? 0;
85
+ if (current !== lease.epoch)
86
+ throw new StaleLease(lease, current);
87
+ }
88
+ /**
89
+ * Give up the lease without handing it to anyone.
90
+ *
91
+ * Bumps the epoch too, so a released lease is as dead as a superseded one —
92
+ * otherwise a holder could release, wander off, and later resume writing with
93
+ * a lease that still looked valid. Releasing a lease that is already stale is a
94
+ * no-op rather than an error: the caller's intent (stop writing) is already
95
+ * satisfied, and throwing would make cleanup paths fragile.
96
+ */
97
+ release(lease) {
98
+ const current = this.state.get(lease.sessionId);
99
+ if (!current || current.epoch !== lease.epoch)
100
+ return;
101
+ this.state.set(lease.sessionId, { epoch: current.epoch + 1, holder: null });
102
+ }
103
+ current(sessionId) {
104
+ return this.state.get(sessionId) ?? { epoch: 0, holder: null };
105
+ }
106
+ /** Sessions with a holder right now. */
107
+ held() {
108
+ return [...this.state.entries()].filter(([, s]) => s.holder !== null).map(([id]) => id);
109
+ }
110
+ /** Test seam. */
111
+ forget(sessionId) {
112
+ this.state.delete(sessionId);
113
+ }
114
+ }
115
+ //# sourceMappingURL=lease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease.js","sourceRoot":"","sources":["../../sessions/lease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAInC,YAAY,KAAY,EAAE,OAAe;QACvC,KAAK,CACH,gCAAgC,KAAK,CAAC,SAAS,aAAa,KAAK,CAAC,KAAK,IAAI;cACzE,kCAAkC,OAAO,oDAAoD,CAChG,CAAC;QAPK;;;;;WAAkB;QAClB;;;;;WAAa;QACb;;;;;WAAgB;QAMvB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAOD;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IAA1B;QACmB;;;;mBAAQ,IAAI,GAAG,EAAyB;WAAC;IAwD5D,CAAC;IAtDC;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAiB,EAAE,MAAc;QACvC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACtC,CAAC;IAED,qCAAqC;IACrC,SAAS,CAAC,KAAY;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;IAChE,CAAC;IAED,0EAA0E;IAC1E,aAAa,CAAC,KAAY;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;QAC5D,IAAI,OAAO,KAAK,KAAK,CAAC,KAAK;YAAE,MAAM,IAAI,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAY;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;YAAE,OAAO;QACtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjE,CAAC;IAED,wCAAwC;IACxC,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,iBAAiB;IACjB,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ import type { Lease } from './lease';
2
+ export interface LeaseRecord {
3
+ sessionId: string;
4
+ epoch: number;
5
+ holder: string | null;
6
+ /** Epoch ms of the last change. Diagnostic only — never used to decide
7
+ * anything, because clocks disagree and this must not depend on that. */
8
+ updatedAt: number;
9
+ }
10
+ export interface LeaseStore {
11
+ read(sessionId: string): Promise<LeaseRecord | null>;
12
+ /**
13
+ * Take the lease, but only if the record still shows `expectedEpoch`.
14
+ *
15
+ * Returns the record that won. A caller whose write lost gets the CURRENT
16
+ * record back rather than an error, because the useful next step is always
17
+ * "re-read and decide", never "retry blindly".
18
+ */
19
+ compareAndSet(sessionId: string, expectedEpoch: number, holder: string): Promise<{
20
+ ok: boolean;
21
+ record: LeaseRecord;
22
+ }>;
23
+ }
24
+ /** The minimum a store needs from a filesystem. Matches FileService's shape so
25
+ * the sidecar can pass one straight in. */
26
+ export interface LeaseFiles {
27
+ read(path: string): Promise<string>;
28
+ write(path: string, content: string): Promise<void>;
29
+ }
30
+ /**
31
+ * A lease record in the session directory.
32
+ *
33
+ * HONEST LIMIT, stated because it decides where this may be used: this is
34
+ * last-write-wins under a read-then-write, so two processes racing on shared
35
+ * storage can both observe epoch N and both write N+1. It is correct for a
36
+ * restart (the epoch survives) and for handoffs mediated by one daemon. It is
37
+ * NOT an arbiter between two machines, and must not be presented as one.
38
+ */
39
+ export declare function makeFileLeaseStore(deps: {
40
+ files: () => Promise<LeaseFiles>;
41
+ sessionsRoot: string;
42
+ now: () => number;
43
+ }): LeaseStore;
44
+ /** The lease a record describes, for handing to `LeaseRegistry`-shaped callers. */
45
+ export declare function leaseOf(record: LeaseRecord): Lease;
46
+ //# sourceMappingURL=leaseStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaseStore.d.ts","sourceRoot":"","sources":["../../sessions/leaseStore.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;6EACyE;IACzE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD;;;;;;OAMG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;CACxH;AAED;2CAC2C;AAC3C,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB,GAAG,UAAU,CAyBb;AAED,mFAAmF;AACnF,wBAAgB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CAElD"}
@@ -0,0 +1,58 @@
1
+ const FILE = 'LEASE.json';
2
+ function parse(text, sessionId) {
3
+ let parsed;
4
+ try {
5
+ parsed = JSON.parse(text);
6
+ }
7
+ catch {
8
+ return null;
9
+ }
10
+ if (!parsed || typeof parsed !== 'object')
11
+ return null;
12
+ const r = parsed;
13
+ if (typeof r.epoch !== 'number' || !Number.isInteger(r.epoch) || r.epoch < 0)
14
+ return null;
15
+ return {
16
+ sessionId,
17
+ epoch: r.epoch,
18
+ holder: typeof r.holder === 'string' ? r.holder : null,
19
+ updatedAt: typeof r.updatedAt === 'number' ? r.updatedAt : 0,
20
+ };
21
+ }
22
+ /**
23
+ * A lease record in the session directory.
24
+ *
25
+ * HONEST LIMIT, stated because it decides where this may be used: this is
26
+ * last-write-wins under a read-then-write, so two processes racing on shared
27
+ * storage can both observe epoch N and both write N+1. It is correct for a
28
+ * restart (the epoch survives) and for handoffs mediated by one daemon. It is
29
+ * NOT an arbiter between two machines, and must not be presented as one.
30
+ */
31
+ export function makeFileLeaseStore(deps) {
32
+ const path = (sessionId) => `${deps.sessionsRoot}/${sessionId}/${FILE}`;
33
+ const load = async (sessionId) => {
34
+ const files = await deps.files();
35
+ const text = await files.read(path(sessionId)).catch(() => '');
36
+ return text ? parse(text, sessionId) : null;
37
+ };
38
+ return {
39
+ read: load,
40
+ async compareAndSet(sessionId, expectedEpoch, holder) {
41
+ const current = await load(sessionId);
42
+ const epoch = current?.epoch ?? 0;
43
+ if (epoch !== expectedEpoch) {
44
+ // Someone else moved it. Hand back what is true now; the caller decides.
45
+ return { ok: false, record: current ?? { sessionId, epoch, holder: null, updatedAt: 0 } };
46
+ }
47
+ const record = { sessionId, epoch: epoch + 1, holder, updatedAt: deps.now() };
48
+ const files = await deps.files();
49
+ await files.write(path(sessionId), JSON.stringify(record, null, 2) + '\n');
50
+ return { ok: true, record };
51
+ },
52
+ };
53
+ }
54
+ /** The lease a record describes, for handing to `LeaseRegistry`-shaped callers. */
55
+ export function leaseOf(record) {
56
+ return { sessionId: record.sessionId, holder: record.holder ?? '', epoch: record.epoch };
57
+ }
58
+ //# sourceMappingURL=leaseStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaseStore.js","sourceRoot":"","sources":["../../sessions/leaseStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { FileService } from '@corenel/protocol';
2
+ export declare function makeTestFs(): FileService & {
3
+ files: Map<string, string>;
4
+ };
5
+ //# sourceMappingURL=testFs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testFs.d.ts","sourceRoot":"","sources":["../../sessions/testFs.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,mBAAmB,CAAC;AAGzE,wBAAgB,UAAU,IAAI,WAAW,GAAG;IAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAyEzE"}
@@ -0,0 +1,85 @@
1
+ import { normalizeFilePath, extOf } from '@corenel/protocol';
2
+ export function makeTestFs() {
3
+ const files = new Map();
4
+ function list(dir) {
5
+ const baseN = dir ? normalizeFilePath(dir) : '';
6
+ const prefix = baseN ? baseN + '/' : '';
7
+ const roots = [];
8
+ const dirNodes = new Map();
9
+ const ensureDir = (dirPath) => {
10
+ if (!dirPath)
11
+ return null;
12
+ const existing = dirNodes.get(dirPath);
13
+ if (existing)
14
+ return existing;
15
+ const parts = dirPath.split('/');
16
+ const node = { name: parts[parts.length - 1], path: dirPath, kind: 'directory', children: [] };
17
+ dirNodes.set(dirPath, node);
18
+ const parent = ensureDir(parts.slice(0, -1).join('/'));
19
+ (parent ? parent.children : roots).push(node);
20
+ return node;
21
+ };
22
+ for (const [path, content] of files) {
23
+ if (prefix && !path.startsWith(prefix))
24
+ continue;
25
+ const parts = path.split('/').filter(Boolean);
26
+ if (!parts.length)
27
+ continue;
28
+ const name = parts[parts.length - 1];
29
+ const parent = parts.length > 1 ? ensureDir(parts.slice(0, -1).join('/')) : null;
30
+ (parent ? parent.children : roots).push({ name, path, kind: 'file', size: content.length, ext: extOf(name) });
31
+ }
32
+ return roots;
33
+ }
34
+ return {
35
+ kind: 'memory',
36
+ label: 'test',
37
+ isLocalDisk: false,
38
+ writable: true,
39
+ files,
40
+ list: async (dir) => list(dir),
41
+ read: async (path) => {
42
+ const v = files.get(normalizeFilePath(path));
43
+ if (v === undefined)
44
+ throw new Error(`not found: ${path}`);
45
+ return v;
46
+ },
47
+ stat: async (path) => {
48
+ const n = normalizeFilePath(path);
49
+ if (files.has(n))
50
+ return { path: n, kind: 'file', size: files.get(n).length, ext: extOf(n) };
51
+ for (const key of files.keys())
52
+ if (key.startsWith(n + '/'))
53
+ return { path: n, kind: 'directory', size: 0 };
54
+ return null;
55
+ },
56
+ write: async (path, content) => {
57
+ files.set(normalizeFilePath(path), content);
58
+ },
59
+ create: async (path, content) => {
60
+ const n = normalizeFilePath(path);
61
+ if (files.has(n))
62
+ throw new Error(`exists: ${path}`);
63
+ files.set(n, content ?? '');
64
+ },
65
+ remove: async (path) => {
66
+ const n = normalizeFilePath(path);
67
+ files.delete(n);
68
+ for (const key of [...files.keys()])
69
+ if (key.startsWith(n + '/'))
70
+ files.delete(key);
71
+ },
72
+ rename: async (from, to) => {
73
+ const f = normalizeFilePath(from);
74
+ const t = normalizeFilePath(to);
75
+ const v = files.get(f);
76
+ if (v !== undefined) {
77
+ files.delete(f);
78
+ files.set(t, v);
79
+ }
80
+ },
81
+ search: async () => [],
82
+ onChange: () => () => { },
83
+ };
84
+ }
85
+ //# sourceMappingURL=testFs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testFs.js","sourceRoot":"","sources":["../../sessions/testFs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ import type { EffectClass } from '../snapshot/effects';
2
+ /** Where a tool call stands. Closed set (spec s4.3.1). */
3
+ export type ToolStatus =
4
+ /** Started, no result recorded. The only ambiguous state on resume. */
5
+ 'executing'
6
+ /** Result recorded, in `resultRef` or `resultInline`. */
7
+ | 'completed'
8
+ /** Ran and did not succeed; a paired `error` entry carries the detail. */
9
+ | 'failed'
10
+ /** The policy gate denied it. It never ran, and must never be re-run. */
11
+ | 'refused'
12
+ /** An `external` call whose completion could not be determined. Needs a human. */
13
+ | 'unknown';
14
+ export declare const TOOL_STATUSES: readonly ToolStatus[];
15
+ /** ACP-normalized content, deliberately loose: this module stores it, the
16
+ * surfaces interpret it. Normalizing to ACP rather than a provider's wire format
17
+ * is what lets a session survive a provider switch mid-task. */
18
+ export type ContentBlock = {
19
+ type: string;
20
+ [k: string]: unknown;
21
+ };
22
+ interface Base {
23
+ index: number;
24
+ at: string;
25
+ }
26
+ export type TimelineEntry = (Base & {
27
+ type: 'user_message';
28
+ content: ContentBlock[];
29
+ }) | (Base & {
30
+ type: 'agent_message';
31
+ content: ContentBlock[];
32
+ model?: string;
33
+ stopReason?: string;
34
+ }) | (Base & {
35
+ type: 'tool_call';
36
+ callId: string;
37
+ name: string;
38
+ input: Record<string, unknown>;
39
+ effectClass: EffectClass;
40
+ status: ToolStatus;
41
+ startedAt?: string;
42
+ completedAt?: string;
43
+ /** Exactly one of these two is non-null. */
44
+ resultRef: string | null;
45
+ resultInline: string | null;
46
+ }) | (Base & {
47
+ type: 'error';
48
+ callId: string | null;
49
+ kind: string;
50
+ message: string;
51
+ retryable: boolean;
52
+ }) | (Base & {
53
+ type: 'summary';
54
+ /** The span this replaces. Both ends inclusive. */
55
+ replaces: {
56
+ from: number;
57
+ to: number;
58
+ };
59
+ /** The original span, retained in the snapshot blob store. */
60
+ spanRef: string;
61
+ content: ContentBlock[];
62
+ });
63
+ /** The `k` these lines carry in SESSION.jsonl. Distinct from 'meta'/'turn' so the
64
+ * chat fold and this one can share a file and ignore each other. */
65
+ export declare const TIMELINE_KIND: "tl";
66
+ export interface TimelineLine {
67
+ k: typeof TIMELINE_KIND;
68
+ e: TimelineEntry;
69
+ }
70
+ export declare function encodeEntry(entry: TimelineEntry): string;
71
+ /** Payloads at or under this go inline; larger ones become a blob ref. Keeps a
72
+ * line bounded and the file readable (spec s4.3.1, decision 6). */
73
+ export declare const INLINE_LIMIT_BYTES = 4096;
74
+ /**
75
+ * Decide where a tool result lives.
76
+ *
77
+ * Measured in UTF-8 bytes, not characters: a 3000-character CJK result is ~9 KB
78
+ * on disk, and a limit that counts characters would quietly blow the bound it
79
+ * exists to hold.
80
+ */
81
+ export declare function fitsInline(text: string, limit?: number): boolean;
82
+ /** A tool call carrying its result in the right place. Throws rather than
83
+ * silently storing both, because "exactly one is non-null" is the invariant every
84
+ * reader depends on. */
85
+ export declare function withResult(entry: Extract<TimelineEntry, {
86
+ type: 'tool_call';
87
+ }>, result: {
88
+ inline: string;
89
+ } | {
90
+ ref: string;
91
+ }): Extract<TimelineEntry, {
92
+ type: 'tool_call';
93
+ }>;
94
+ export interface FoldedTimeline {
95
+ /** Entries in index order. Sparse indices are fine; gaps are simply absent. */
96
+ entries: TimelineEntry[];
97
+ /** One past the highest index SEEN, not the entry count. */
98
+ nextIndex: number;
99
+ }
100
+ /**
101
+ * Fold a SESSION.jsonl text into the timeline.
102
+ *
103
+ * Unparseable lines are skipped, exactly as the chat fold does, so a crash
104
+ * mid-append costs the final line and nothing else. Later lines at the same index
105
+ * supersede earlier ones, which is how a tool call moves
106
+ * executing -> completed without ever editing a byte.
107
+ *
108
+ * Compaction is applied on READ: entries covered by a `summary` are dropped from
109
+ * the result, while their indices stay reserved. The raw span is still in the
110
+ * file and still in the blob store, so nothing is lost — the fold just stops
111
+ * handing it to the model.
112
+ */
113
+ export declare function foldTimeline(text: string): FoldedTimeline;
114
+ /**
115
+ * Build the summary entry that compacts a span.
116
+ *
117
+ * Refuses to swallow a `tool_call`: those carry replay semantics (s4.4), and a
118
+ * compaction that dropped one would make resume unable to tell whether the call
119
+ * ran. Summarizing prose is a size optimization; summarizing away a side effect
120
+ * is data loss.
121
+ */
122
+ export declare function compactSpan(input: {
123
+ index: number;
124
+ at: string;
125
+ from: number;
126
+ to: number;
127
+ spanRef: string;
128
+ content: ContentBlock[];
129
+ entries: readonly TimelineEntry[];
130
+ }): Extract<TimelineEntry, {
131
+ type: 'summary';
132
+ }>;
133
+ export {};
134
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../sessions/timeline.ts"],"names":[],"mappings":"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"}