pilotswarm-sdk 0.4.1 → 0.5.1

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 (182) hide show
  1. package/api/src/protocol.js +7 -0
  2. package/dist/blob-store.d.ts +10 -1
  3. package/dist/blob-store.d.ts.map +1 -1
  4. package/dist/blob-store.js +275 -13
  5. package/dist/blob-store.js.map +1 -1
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +15 -1
  8. package/dist/client.js.map +1 -1
  9. package/dist/cms-migrations.d.ts.map +1 -1
  10. package/dist/cms-migrations.js +164 -0
  11. package/dist/cms-migrations.js.map +1 -1
  12. package/dist/cms.d.ts +6 -0
  13. package/dist/cms.d.ts.map +1 -1
  14. package/dist/cms.js +3 -0
  15. package/dist/cms.js.map +1 -1
  16. package/dist/fault-injection.d.ts +2 -0
  17. package/dist/fault-injection.d.ts.map +1 -0
  18. package/dist/fault-injection.js +45 -0
  19. package/dist/fault-injection.js.map +1 -0
  20. package/dist/model-providers.js +1 -1
  21. package/dist/model-providers.js.map +1 -1
  22. package/dist/orchestration/agents.d.ts.map +1 -1
  23. package/dist/orchestration/agents.js +9 -1
  24. package/dist/orchestration/agents.js.map +1 -1
  25. package/dist/orchestration/index.d.ts +3 -3
  26. package/dist/orchestration/index.js +1 -1
  27. package/dist/orchestration/lifecycle.d.ts +9 -2
  28. package/dist/orchestration/lifecycle.d.ts.map +1 -1
  29. package/dist/orchestration/lifecycle.js +21 -55
  30. package/dist/orchestration/lifecycle.js.map +1 -1
  31. package/dist/orchestration/runtime.d.ts +1 -1
  32. package/dist/orchestration/state.d.ts +7 -3
  33. package/dist/orchestration/state.d.ts.map +1 -1
  34. package/dist/orchestration/state.js +13 -4
  35. package/dist/orchestration/state.js.map +1 -1
  36. package/dist/orchestration/turn.d.ts.map +1 -1
  37. package/dist/orchestration/turn.js +126 -66
  38. package/dist/orchestration/turn.js.map +1 -1
  39. package/dist/orchestration-registry.d.ts.map +1 -1
  40. package/dist/orchestration-registry.js +8 -2
  41. package/dist/orchestration-registry.js.map +1 -1
  42. package/dist/orchestration-version.d.ts +1 -1
  43. package/dist/orchestration-version.js +1 -1
  44. package/dist/orchestration.d.ts +2 -2
  45. package/dist/orchestration.js +4 -4
  46. package/dist/orchestration_1_0_46.d.ts +1 -1
  47. package/dist/orchestration_1_0_47.d.ts +1 -1
  48. package/dist/orchestration_1_0_48.d.ts +1 -1
  49. package/dist/orchestration_1_0_49.d.ts +1 -1
  50. package/dist/orchestration_1_0_50.d.ts +1 -1
  51. package/dist/orchestration_1_0_56/agents.d.ts +41 -0
  52. package/dist/orchestration_1_0_56/agents.d.ts.map +1 -0
  53. package/dist/orchestration_1_0_56/agents.js +758 -0
  54. package/dist/orchestration_1_0_56/agents.js.map +1 -0
  55. package/dist/orchestration_1_0_56/index.d.ts +24 -0
  56. package/dist/orchestration_1_0_56/index.d.ts.map +1 -0
  57. package/dist/orchestration_1_0_56/index.js +13 -0
  58. package/dist/orchestration_1_0_56/index.js.map +1 -0
  59. package/dist/orchestration_1_0_56/lifecycle.d.ts +41 -0
  60. package/dist/orchestration_1_0_56/lifecycle.d.ts.map +1 -0
  61. package/dist/orchestration_1_0_56/lifecycle.js +580 -0
  62. package/dist/orchestration_1_0_56/lifecycle.js.map +1 -0
  63. package/dist/orchestration_1_0_56/queue.d.ts +7 -0
  64. package/dist/orchestration_1_0_56/queue.d.ts.map +1 -0
  65. package/dist/orchestration_1_0_56/queue.js +644 -0
  66. package/dist/orchestration_1_0_56/queue.js.map +1 -0
  67. package/dist/orchestration_1_0_56/runtime.d.ts +29 -0
  68. package/dist/orchestration_1_0_56/runtime.d.ts.map +1 -0
  69. package/dist/orchestration_1_0_56/runtime.js +194 -0
  70. package/dist/orchestration_1_0_56/runtime.js.map +1 -0
  71. package/dist/orchestration_1_0_56/state.d.ts +126 -0
  72. package/dist/orchestration_1_0_56/state.d.ts.map +1 -0
  73. package/dist/orchestration_1_0_56/state.js +106 -0
  74. package/dist/orchestration_1_0_56/state.js.map +1 -0
  75. package/dist/orchestration_1_0_56/turn.d.ts +30 -0
  76. package/dist/orchestration_1_0_56/turn.d.ts.map +1 -0
  77. package/dist/orchestration_1_0_56/turn.js +1075 -0
  78. package/dist/orchestration_1_0_56/turn.js.map +1 -0
  79. package/dist/orchestration_1_0_56/utils.d.ts +22 -0
  80. package/dist/orchestration_1_0_56/utils.d.ts.map +1 -0
  81. package/dist/orchestration_1_0_56/utils.js +226 -0
  82. package/dist/orchestration_1_0_56/utils.js.map +1 -0
  83. package/dist/orchestration_1_0_57/agents.d.ts +41 -0
  84. package/dist/orchestration_1_0_57/agents.d.ts.map +1 -0
  85. package/dist/orchestration_1_0_57/agents.js +758 -0
  86. package/dist/orchestration_1_0_57/agents.js.map +1 -0
  87. package/dist/orchestration_1_0_57/index.d.ts +24 -0
  88. package/dist/orchestration_1_0_57/index.d.ts.map +1 -0
  89. package/dist/orchestration_1_0_57/index.js +13 -0
  90. package/dist/orchestration_1_0_57/index.js.map +1 -0
  91. package/dist/orchestration_1_0_57/lifecycle.d.ts +49 -0
  92. package/dist/orchestration_1_0_57/lifecycle.d.ts.map +1 -0
  93. package/dist/orchestration_1_0_57/lifecycle.js +583 -0
  94. package/dist/orchestration_1_0_57/lifecycle.js.map +1 -0
  95. package/dist/orchestration_1_0_57/queue.d.ts +7 -0
  96. package/dist/orchestration_1_0_57/queue.d.ts.map +1 -0
  97. package/dist/orchestration_1_0_57/queue.js +644 -0
  98. package/dist/orchestration_1_0_57/queue.js.map +1 -0
  99. package/dist/orchestration_1_0_57/runtime.d.ts +29 -0
  100. package/dist/orchestration_1_0_57/runtime.d.ts.map +1 -0
  101. package/dist/orchestration_1_0_57/runtime.js +198 -0
  102. package/dist/orchestration_1_0_57/runtime.js.map +1 -0
  103. package/dist/orchestration_1_0_57/state.d.ts +133 -0
  104. package/dist/orchestration_1_0_57/state.d.ts.map +1 -0
  105. package/dist/orchestration_1_0_57/state.js +115 -0
  106. package/dist/orchestration_1_0_57/state.js.map +1 -0
  107. package/dist/orchestration_1_0_57/turn.d.ts +30 -0
  108. package/dist/orchestration_1_0_57/turn.d.ts.map +1 -0
  109. package/dist/orchestration_1_0_57/turn.js +1110 -0
  110. package/dist/orchestration_1_0_57/turn.js.map +1 -0
  111. package/dist/orchestration_1_0_57/utils.d.ts +22 -0
  112. package/dist/orchestration_1_0_57/utils.d.ts.map +1 -0
  113. package/dist/orchestration_1_0_57/utils.js +226 -0
  114. package/dist/orchestration_1_0_57/utils.js.map +1 -0
  115. package/dist/orchestration_1_0_58/agents.d.ts +41 -0
  116. package/dist/orchestration_1_0_58/agents.d.ts.map +1 -0
  117. package/dist/orchestration_1_0_58/agents.js +758 -0
  118. package/dist/orchestration_1_0_58/agents.js.map +1 -0
  119. package/dist/orchestration_1_0_58/index.d.ts +24 -0
  120. package/dist/orchestration_1_0_58/index.d.ts.map +1 -0
  121. package/dist/orchestration_1_0_58/index.js +13 -0
  122. package/dist/orchestration_1_0_58/index.js.map +1 -0
  123. package/dist/orchestration_1_0_58/lifecycle.d.ts +48 -0
  124. package/dist/orchestration_1_0_58/lifecycle.d.ts.map +1 -0
  125. package/dist/orchestration_1_0_58/lifecycle.js +546 -0
  126. package/dist/orchestration_1_0_58/lifecycle.js.map +1 -0
  127. package/dist/orchestration_1_0_58/queue.d.ts +7 -0
  128. package/dist/orchestration_1_0_58/queue.d.ts.map +1 -0
  129. package/dist/orchestration_1_0_58/queue.js +644 -0
  130. package/dist/orchestration_1_0_58/queue.js.map +1 -0
  131. package/dist/orchestration_1_0_58/runtime.d.ts +29 -0
  132. package/dist/orchestration_1_0_58/runtime.d.ts.map +1 -0
  133. package/dist/orchestration_1_0_58/runtime.js +199 -0
  134. package/dist/orchestration_1_0_58/runtime.js.map +1 -0
  135. package/dist/orchestration_1_0_58/state.d.ts +130 -0
  136. package/dist/orchestration_1_0_58/state.d.ts.map +1 -0
  137. package/dist/orchestration_1_0_58/state.js +115 -0
  138. package/dist/orchestration_1_0_58/state.js.map +1 -0
  139. package/dist/orchestration_1_0_58/turn.d.ts +30 -0
  140. package/dist/orchestration_1_0_58/turn.d.ts.map +1 -0
  141. package/dist/orchestration_1_0_58/turn.js +1107 -0
  142. package/dist/orchestration_1_0_58/turn.js.map +1 -0
  143. package/dist/orchestration_1_0_58/utils.d.ts +22 -0
  144. package/dist/orchestration_1_0_58/utils.d.ts.map +1 -0
  145. package/dist/orchestration_1_0_58/utils.js +226 -0
  146. package/dist/orchestration_1_0_58/utils.js.map +1 -0
  147. package/dist/session-lifecycle.d.ts +114 -0
  148. package/dist/session-lifecycle.d.ts.map +1 -0
  149. package/dist/session-lifecycle.js +324 -0
  150. package/dist/session-lifecycle.js.map +1 -0
  151. package/dist/session-manager.d.ts +21 -0
  152. package/dist/session-manager.d.ts.map +1 -1
  153. package/dist/session-manager.js +137 -1
  154. package/dist/session-manager.js.map +1 -1
  155. package/dist/session-proxy.d.ts +5 -1
  156. package/dist/session-proxy.d.ts.map +1 -1
  157. package/dist/session-proxy.js +1158 -954
  158. package/dist/session-proxy.js.map +1 -1
  159. package/dist/session-store.d.ts +48 -3
  160. package/dist/session-store.d.ts.map +1 -1
  161. package/dist/session-store.js +394 -28
  162. package/dist/session-store.js.map +1 -1
  163. package/dist/snapshot-protocol.d.ts +101 -0
  164. package/dist/snapshot-protocol.d.ts.map +1 -0
  165. package/dist/snapshot-protocol.js +120 -0
  166. package/dist/snapshot-protocol.js.map +1 -0
  167. package/dist/types.d.ts +24 -1
  168. package/dist/types.d.ts.map +1 -1
  169. package/dist/types.js.map +1 -1
  170. package/dist/wait-affinity.d.ts +11 -0
  171. package/dist/wait-affinity.d.ts.map +1 -1
  172. package/dist/wait-affinity.js +6 -0
  173. package/dist/wait-affinity.js.map +1 -1
  174. package/dist/web/web-management-client.d.ts +28 -9
  175. package/dist/web/web-management-client.d.ts.map +1 -1
  176. package/dist/web/web-management-client.js +30 -15
  177. package/dist/web/web-management-client.js.map +1 -1
  178. package/dist/worker.d.ts +16 -1
  179. package/dist/worker.d.ts.map +1 -1
  180. package/dist/worker.js +63 -6
  181. package/dist/worker.js.map +1 -1
  182. package/package.json +7 -4
@@ -0,0 +1,324 @@
1
+ /**
2
+ * runTurn preamble/postamble for the session lifecycle protocol
3
+ * (docs/proposals/session-lifecycle-protocol.md §3.2–3.3).
4
+ *
5
+ * The whole lifecycle lives inside the single runTurn activity (principle
6
+ * P5 — no other session activity depends on landing where a previous one
7
+ * did). Reconcile is STORE-WINS (docs/proposals/snapshot-store-wins.md): one
8
+ * probe per turn is the only oracle — never the orchestration's expectation,
9
+ * which goes stale the moment a stopped/zombie turn advances the store the
10
+ * control plane discarded.
11
+ *
12
+ * preamble p1. one probe. Store bears this turn's own turnKey →
13
+ * already-committed recovery (restore, never replay).
14
+ * p2. store empty → trust a clean local dir (only truth), else fresh;
15
+ * legacy snapshot → trust clean local, else hydrate.
16
+ * p3. warm ONLY when a clean local dir's marker matches the store's
17
+ * (version AND content hash); every other state — store ahead
18
+ * (a discarded/foreign turn advanced it — NO fence, we adopt it),
19
+ * a same-version content swap, a torn/dirty dir, or no marker —
20
+ * hydrates the store. Store below the marker is hydrated too and
21
+ * flagged `regressed`.
22
+ * p4. (caller) write the turn sentinel just before the body
23
+ * postamble c1. write .ps-turn-commit.json {turnKey, result} into the dir
24
+ * c2. commit the tar (store.commitSnapshot) at base+1. alreadyCommitted
25
+ * (same turnKey) → restore the winner's snapshot + result. A
26
+ * SnapshotConflictError (store moved off base while we ran) is NOT
27
+ * fatal: the turn is left UNPUBLISHED (superseded), the sentinel
28
+ * stays dirty so the next preamble rehydrates the winner, and the
29
+ * caller emits `session.snapshot_unpublished`. A user-stopped
30
+ * result skips the commit entirely (also unpublished).
31
+ * c3. write the local version marker (published turns only)
32
+ * c4. clear the turn sentinel (published turns only)
33
+ *
34
+ * @internal
35
+ */
36
+ import fs from "node:fs";
37
+ import path from "node:path";
38
+ import { faultPoint } from "./fault-injection.js";
39
+ import { SnapshotConflictError, clearTurnSentinel, readSnapshotMarker, readTurnCommitFile, readTurnSentinel, writeSnapshotMarker, writeTurnCommitFile, } from "./snapshot-protocol.js";
40
+ const COMMIT_TRANSIENT_RETRIES = 3;
41
+ // Tunable so fault-injection tests don't pay real backoff delays.
42
+ const COMMIT_RETRY_BASE_DELAY_MS = Number.parseInt(process.env.PILOTSWARM_COMMIT_RETRY_DELAY_MS ?? "", 10) || 1_000;
43
+ function sessionDirOf(ctx) {
44
+ return path.join(ctx.sessionStateDir, ctx.sessionId);
45
+ }
46
+ /**
47
+ * A marker alone is not proof the dir survived intact (a crashed recursive
48
+ * delete removes files in arbitrary order and can leave the marker while
49
+ * the session files are gone). Require the SDK's layout anchor before
50
+ * trusting local files without consulting the store.
51
+ */
52
+ function hasUsableSessionLayout(sessionDir) {
53
+ return fs.existsSync(path.join(sessionDir, "workspace.yaml"));
54
+ }
55
+ /**
56
+ * Content-identity gate for the warm fast path. Only a mismatch between two
57
+ * PRESENT hashes forces a hydrate (a rule-breaking restore that overwrote the
58
+ * same version number with different content — the ETag CAS can't catch that,
59
+ * §5.1). When either side lacks a hash (a legacy marker/probe), fall back to
60
+ * version-only trust so an upgrade doesn't churn every warm worker into a
61
+ * spurious one-time hydrate.
62
+ */
63
+ function contentMatches(markerHash, probeHash) {
64
+ if (markerHash && probeHash)
65
+ return markerHash === probeHash;
66
+ return true;
67
+ }
68
+ function markerFromHydrate(sessionDir, hydrated) {
69
+ writeSnapshotMarker(sessionDir, {
70
+ version: hydrated.version,
71
+ ...(hydrated.turnKey ? { turnKey: hydrated.turnKey } : {}),
72
+ ...(hydrated.contentHash ? { contentHash: hydrated.contentHash } : {}),
73
+ });
74
+ }
75
+ /** Restore the committed snapshot and return the stored result (§3.2 r1–r3). */
76
+ async function recoverAlreadyCommitted(ctx) {
77
+ const sessionDir = sessionDirOf(ctx);
78
+ await ctx.dropWarmSession();
79
+ const hydrated = await ctx.store.hydrateSnapshot(ctx.sessionId);
80
+ markerFromHydrate(sessionDir, hydrated);
81
+ clearTurnSentinel(sessionDir);
82
+ const commitFile = readTurnCommitFile(sessionDir);
83
+ if (commitFile && commitFile.turnKey === ctx.turnKey) {
84
+ ctx.trace(`session=${ctx.sessionId} already-committed recovery: store at v${hydrated.version} ` +
85
+ `under this turnKey; returning stored result without re-running the turn`);
86
+ return { kind: "already-committed", version: hydrated.version, result: commitFile.result };
87
+ }
88
+ // Snapshot claims our turnKey but carries no readable committed result —
89
+ // degrade to a hydrated re-run (duplicate execution beats a wrong result).
90
+ ctx.trace(`session=${ctx.sessionId} store bears this turnKey (v${hydrated.version}) but the ` +
91
+ `turn-commit file is missing/unreadable; degrading to hydrated re-run`);
92
+ return { kind: "hydrated", baseVersion: hydrated.version, storeBehindExpected: false };
93
+ }
94
+ async function resolveFromStore(ctx, regressed) {
95
+ const sessionDir = sessionDirOf(ctx);
96
+ await ctx.dropWarmSession();
97
+ const hydrated = await ctx.store.hydrateSnapshot(ctx.sessionId);
98
+ markerFromHydrate(sessionDir, hydrated);
99
+ clearTurnSentinel(sessionDir);
100
+ const storeBehindExpected = hydrated.version < ctx.expectedVersion;
101
+ if (storeBehindExpected) {
102
+ ctx.trace(`session=${ctx.sessionId} store snapshot v${hydrated.version} is BEHIND the orchestration's ` +
103
+ `expected v${ctx.expectedVersion} (store restored from backup?); proceeding from stored state`);
104
+ }
105
+ if (regressed) {
106
+ ctx.trace(`session=${ctx.sessionId} store snapshot v${regressed.storeVersion} is BELOW the local marker ` +
107
+ `v${regressed.markerVersion} (store restored/lost data); store wins — hydrating v${hydrated.version}`);
108
+ }
109
+ return {
110
+ kind: "hydrated",
111
+ baseVersion: hydrated.version,
112
+ storeBehindExpected,
113
+ ...(regressed ? { regressed } : {}),
114
+ };
115
+ }
116
+ /**
117
+ * Preamble (store-wins). Runs under the per-session run-turn lock, before
118
+ * getOrCreate. ONE probe per turn is the reconcile oracle — never the
119
+ * orchestration's `expectedVersion`, which goes stale the moment a stopped or
120
+ * zombie turn advances the store the control plane discarded (the divergence
121
+ * this protocol removes). On return the local dir is in exactly one state:
122
+ * trusted at `baseVersion` (warm — local marker matches the store's
123
+ * version+hash), restored at the stored version (hydrated — the store wins),
124
+ * absent (fresh), or restored at this turn's committed version
125
+ * (already-committed — the caller returns the stored result without a body).
126
+ */
127
+ export async function runTurnPreamble(ctx) {
128
+ const sessionDir = sessionDirOf(ctx);
129
+ const sentinel = readTurnSentinel(sessionDir);
130
+ const marker = readSnapshotMarker(sessionDir);
131
+ const localDirUsable = fs.existsSync(sessionDir) && hasUsableSessionLayout(sessionDir);
132
+ // Store-wins: one metadata probe is the ONLY oracle. `expectedVersion`
133
+ // rides in the input for frozen orchestration versions but is load-bearing
134
+ // for nothing here except the lossy-replay observability flag.
135
+ const probe = await ctx.store.probeSnapshot(ctx.sessionId);
136
+ if (sentinel) {
137
+ ctx.trace(`session=${ctx.sessionId} turn sentinel present (started ${sentinel.startedAt || "unknown"}); ` +
138
+ `local dir untrusted, resolving from store`);
139
+ }
140
+ // Idempotency (keeper): the store bears THIS turn's own key — a prior
141
+ // attempt of this very turn already committed (crash-after-commit, or a
142
+ // racing drain/lock-steal duplicate). Restore, never replay.
143
+ if (probe.exists && probe.turnKey && probe.turnKey === ctx.turnKey) {
144
+ return recoverAlreadyCommitted(ctx);
145
+ }
146
+ // Store holds nothing.
147
+ if (!probe.exists) {
148
+ // A clean, usable local dir is the best (only) data — trust it over a
149
+ // fresh replay; the fresh chain publishes from here.
150
+ if (!sentinel && localDirUsable) {
151
+ return { kind: "warm", baseVersion: marker?.version ?? 0 };
152
+ }
153
+ // Dirty or unusable local + empty store → fresh; lossy iff turns had
154
+ // been committed before (marker present, or a frozen orchestration
155
+ // still reports a nonzero expected version). W3 in the protocol doc.
156
+ await ctx.dropWarmSession();
157
+ fs.rmSync(sessionDir, { recursive: true, force: true });
158
+ return { kind: "fresh", baseVersion: 0, lossy: (marker?.version ?? ctx.expectedVersion) > 0 };
159
+ }
160
+ // Legacy snapshot (pre-protocol, no version metadata, counts as v0): a
161
+ // clean local dir is still trustworthy; otherwise hydrate what's stored.
162
+ if (probe.legacy) {
163
+ if (!sentinel && localDirUsable) {
164
+ return { kind: "warm", baseVersion: marker?.version ?? 0 };
165
+ }
166
+ return resolveFromStore(ctx);
167
+ }
168
+ // Regressed: the store sits BELOW this worker's own marker — a restore from
169
+ // an older backup, or store data loss. Anomalous (surfaced by the caller),
170
+ // but the store still wins: we hydrate what it holds.
171
+ const regressed = marker && probe.version < marker.version
172
+ ? { markerVersion: marker.version, storeVersion: probe.version }
173
+ : undefined;
174
+ // Warm fast path: a clean local dir whose marker names EXACTLY the stored
175
+ // state — same version AND (when both carry a hash) same content. The
176
+ // layout anchor guards a marker orphaned by a torn delete; the hash guards
177
+ // a rule-breaking same-version restore (§5.1). Only branch that trusts
178
+ // local files without hydrating.
179
+ if (!sentinel && localDirUsable && marker
180
+ && marker.version === probe.version
181
+ && contentMatches(marker.contentHash, probe.contentHash)) {
182
+ return { kind: "warm", baseVersion: marker.version };
183
+ }
184
+ // Store wins for everything else: store AHEAD (a discarded/foreign turn
185
+ // advanced it — the incident; adopt it, no zombie fence), a same-version
186
+ // content swap, a torn/dirty dir, or no marker.
187
+ return resolveFromStore(ctx, regressed);
188
+ }
189
+ /**
190
+ * Last-resort recovery when getOrCreate fails on missing/lost local state
191
+ * DESPITE the preamble's resolution (e.g. the SDK refused to resume from
192
+ * intact-looking files): restore the committed snapshot if one exists.
193
+ * Returns the hydrated version, or null when the store holds nothing —
194
+ * only then may the caller fall back to the lossy fresh replay, whose
195
+ * store-deleting reset is a no-op against an empty store.
196
+ */
197
+ export async function attemptStoreRecovery(ctx) {
198
+ const probe = await ctx.store.probeSnapshot(ctx.sessionId);
199
+ if (!probe.exists)
200
+ return null;
201
+ const sessionDir = sessionDirOf(ctx);
202
+ await ctx.dropWarmSession();
203
+ const hydrated = await ctx.store.hydrateSnapshot(ctx.sessionId);
204
+ markerFromHydrate(sessionDir, hydrated);
205
+ clearTurnSentinel(sessionDir);
206
+ return hydrated.version;
207
+ }
208
+ /**
209
+ * Postamble (c1–c4). Runs under the per-session run-turn lock after the
210
+ * body produced `result`. Transient store failures retry in place; a CAS
211
+ * conflict (foreign writer) always throws — the activity must fail loudly.
212
+ * An `alreadyCommitted` CAS outcome (a racing duplicate of this same turn
213
+ * won) triggers restore-not-replay: the winner's snapshot and result are
214
+ * adopted, discarding this attempt's divergent local state.
215
+ */
216
+ export async function runTurnCommit(ctx, baseVersion, result) {
217
+ // Stop-turn divergence guard: a user-stopped turn (result type "stopped")
218
+ // is discarded by the orchestration (stop won the ctx.race →
219
+ // handleTurnStopped; this runTurn is the dropped race loser) and its
220
+ // turnKey is never re-run. Committing its snapshot would advance the
221
+ // stored version while the orchestration keeps state.snapshotVersion at the
222
+ // base — leaving the store one ahead so every later turn fails the CAS
223
+ // (zombie-duplicate fence: "expected N, found N+1"). Skip the commit and
224
+ // leave the .ps-turn-inprogress sentinel in place so the next preamble
225
+ // re-hydrates the base version and discards this partial turn. (Drain and
226
+ // lock-steal classify as "cancelled", not "stopped", and DO commit so their
227
+ // same-turnKey re-run adopts the already-committed snapshot.)
228
+ if (result?.type === "stopped") {
229
+ ctx.trace(`session=${ctx.sessionId} user-stopped turn: skipping snapshot commit at ` +
230
+ `base v${baseVersion}; sentinel left dirty for next-turn re-hydrate`);
231
+ return {
232
+ version: baseVersion,
233
+ contentHash: "",
234
+ alreadyCommitted: false,
235
+ published: false,
236
+ unpublishedReason: "stopped",
237
+ };
238
+ }
239
+ const sessionDir = sessionDirOf(ctx);
240
+ writeTurnCommitFile(sessionDir, ctx.turnKey, result);
241
+ faultPoint("turn.commit.before-cas");
242
+ let lastError;
243
+ for (let attempt = 1; attempt <= COMMIT_TRANSIENT_RETRIES; attempt++) {
244
+ try {
245
+ const committed = await ctx.store.commitSnapshot(ctx.sessionId, {
246
+ baseVersion,
247
+ turnKey: ctx.turnKey,
248
+ });
249
+ faultPoint("turn.commit.after-cas");
250
+ if (committed.alreadyCommitted) {
251
+ // §3.2 restore-not-replay: a racing attempt of this same
252
+ // turn committed first. Its snapshot is the durable lineage;
253
+ // this attempt's local files diverge and must be replaced.
254
+ ctx.trace(`session=${ctx.sessionId} commit found this turn already committed at ` +
255
+ `v${committed.version} by a racing attempt; restoring the committed state`);
256
+ await ctx.dropWarmSession();
257
+ const hydrated = await ctx.store.hydrateSnapshot(ctx.sessionId);
258
+ markerFromHydrate(sessionDir, hydrated);
259
+ clearTurnSentinel(sessionDir);
260
+ const commitFile = readTurnCommitFile(sessionDir);
261
+ return {
262
+ version: committed.version,
263
+ contentHash: hydrated.contentHash ?? committed.contentHash,
264
+ ...(hydrated.sizeBytes != null ? { sizeBytes: hydrated.sizeBytes } : {}),
265
+ ...(hydrated.rawSizeBytes != null ? { rawSizeBytes: hydrated.rawSizeBytes } : {}),
266
+ alreadyCommitted: true,
267
+ published: true,
268
+ ...(commitFile && commitFile.turnKey === ctx.turnKey
269
+ ? { storedResult: commitFile.result }
270
+ : {}),
271
+ };
272
+ }
273
+ writeSnapshotMarker(sessionDir, {
274
+ version: committed.version,
275
+ turnKey: ctx.turnKey,
276
+ contentHash: committed.contentHash,
277
+ });
278
+ faultPoint("turn.commit.after-marker");
279
+ clearTurnSentinel(sessionDir);
280
+ faultPoint("turn.commit.after-sentinel-clear");
281
+ return {
282
+ version: committed.version,
283
+ contentHash: committed.contentHash,
284
+ ...(committed.sizeBytes != null ? { sizeBytes: committed.sizeBytes } : {}),
285
+ ...(committed.rawSizeBytes != null ? { rawSizeBytes: committed.rawSizeBytes } : {}),
286
+ alreadyCommitted: false,
287
+ published: true,
288
+ };
289
+ }
290
+ catch (error) {
291
+ if (error instanceof SnapshotConflictError) {
292
+ // Store-wins: a turn the control plane discarded — or a foreign
293
+ // writer — advanced the store off our base while this turn ran.
294
+ // Do NOT brick (the old zombie-duplicate fence). Give up this
295
+ // publish: leave the sentinel dirty so the next preamble
296
+ // rehydrates the winner. The turn's result still returns to the
297
+ // orchestration; only its snapshot memory is superseded, and
298
+ // the caller emits `session.snapshot_unpublished{superseded}`.
299
+ ctx.trace(`session=${ctx.sessionId} commit superseded: store advanced off base v${baseVersion} ` +
300
+ `while this turn ran (${error.message}); leaving turn unpublished, sentinel dirty`);
301
+ return {
302
+ version: baseVersion,
303
+ contentHash: "",
304
+ alreadyCommitted: false,
305
+ published: false,
306
+ unpublishedReason: "superseded",
307
+ observedStoreVersion: error.storedVersion,
308
+ ...(error.storedTurnKey ? { observedStoreTurnKey: error.storedTurnKey } : {}),
309
+ };
310
+ }
311
+ lastError = error;
312
+ const message = error instanceof Error ? error.message : String(error);
313
+ ctx.trace(`session=${ctx.sessionId} turn commit attempt ${attempt}/${COMMIT_TRANSIENT_RETRIES} ` +
314
+ `failed: ${message}`);
315
+ if (attempt < COMMIT_TRANSIENT_RETRIES) {
316
+ await new Promise((resolve) => setTimeout(resolve, COMMIT_RETRY_BASE_DELAY_MS * attempt));
317
+ }
318
+ }
319
+ }
320
+ throw lastError instanceof Error
321
+ ? lastError
322
+ : new Error(`Turn commit failed for ${ctx.sessionId}: ${String(lastError)}`);
323
+ }
324
+ //# sourceMappingURL=session-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-lifecycle.js","sourceRoot":"","sources":["../src/session-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -123,6 +123,8 @@ export declare class SessionManager {
123
123
  private _getLineageSessionIds;
124
124
  /** Per-session critical sections; protects the SDK session handle and local session.db. */
125
125
  private sessionLocks;
126
+ /** Last local activity per session — feeds the autonomous eviction clock. */
127
+ private sessionLastTouchedAt;
126
128
  constructor(githubToken?: string | undefined, sessionStore?: SessionStateStore | null, workerDefaults?: WorkerDefaults, sessionStateDir?: string);
127
129
  /** Store full config (with tools/hooks) for a session. Called by PilotSwarmClient. */
128
130
  setConfig(sessionId: string, config: ManagedSessionConfig): void;
@@ -192,6 +194,16 @@ export declare class SessionManager {
192
194
  withRunTurnLock<T>(sessionId: string, operation: string, fn: () => Promise<T>, options?: {
193
195
  trace?: SessionTraceWriter;
194
196
  }): Promise<T>;
197
+ /**
198
+ * Autonomous eviction sweep (lifecycle protocol §3.4): local session
199
+ * state is a cache. A session idle past `evictAfterMs` is reclaimed
200
+ * without telling anyone — sessions with a committed snapshot marker
201
+ * are simply destroyed + deleted (the store already holds their state;
202
+ * the next runTurn self-validates and hydrates); unmarked (legacy)
203
+ * sessions are dehydrated the old way so their only copy is preserved.
204
+ * Returns the number of sessions reclaimed.
205
+ */
206
+ sweepIdleSessions(evictAfterMs: number): Promise<number>;
195
207
  /**
196
208
  * Stop-turn interrupt primitive: abort the warm session's in-flight turn.
197
209
  *
@@ -223,6 +235,15 @@ export declare class SessionManager {
223
235
  private _getOrCreateUnlocked;
224
236
  /** Get a session by ID (null if not in memory on this node). */
225
237
  get(sessionId: string): ManagedSession | null;
238
+ /** Root directory holding per-session state dirs. */
239
+ getSessionStateDir(): string;
240
+ /**
241
+ * Destroy the in-memory ManagedSession only — disk state untouched.
242
+ * Used by the lifecycle preamble before overwriting local files with a
243
+ * hydrated snapshot (a warm session bound to the old files must not
244
+ * survive the swap). Caller holds the per-session run-turn lock.
245
+ */
246
+ dropWarmSession(sessionId: string): Promise<void>;
226
247
  /**
227
248
  * Dehydrate a session: snapshot to the session store, release in-memory state.
228
249
  *
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { buildKnowledgePromptBlocks, loadKnowledgeIndexFromFactStore, buildEnhan
9
9
  import { composeStructuredSystemMessage, extractPromptContent, mergePromptSections } from "./prompt-layering.js";
10
10
  import { buildPromptLayersEventPayload } from "./prompt-layers.js";
11
11
  import { approvePermissionForSession } from "./permissions.js";
12
+ import { readSnapshotMarker, supportsVersionedSnapshots } from "./snapshot-protocol.js";
12
13
  import fs from "node:fs";
13
14
  import path from "node:path";
14
15
  import os from "node:os";
@@ -148,6 +149,8 @@ export class SessionManager {
148
149
  _getLineageSessionIds = null;
149
150
  /** Per-session critical sections; protects the SDK session handle and local session.db. */
150
151
  sessionLocks = new Map();
152
+ /** Last local activity per session — feeds the autonomous eviction clock. */
153
+ sessionLastTouchedAt = new Map();
151
154
  constructor(githubToken, sessionStore, workerDefaults, sessionStateDir) {
152
155
  this.githubToken = githubToken;
153
156
  this.sessionStore = sessionStore ?? null;
@@ -433,7 +436,119 @@ export class SessionManager {
433
436
  }
434
437
  }
435
438
  async withRunTurnLock(sessionId, operation, fn, options) {
436
- return this._withSessionLock(sessionId, operation, fn, options);
439
+ this.sessionLastTouchedAt.set(sessionId, Date.now());
440
+ try {
441
+ return await this._withSessionLock(sessionId, operation, fn, options);
442
+ }
443
+ finally {
444
+ this.sessionLastTouchedAt.set(sessionId, Date.now());
445
+ }
446
+ }
447
+ /**
448
+ * Autonomous eviction sweep (lifecycle protocol §3.4): local session
449
+ * state is a cache. A session idle past `evictAfterMs` is reclaimed
450
+ * without telling anyone — sessions with a committed snapshot marker
451
+ * are simply destroyed + deleted (the store already holds their state;
452
+ * the next runTurn self-validates and hydrates); unmarked (legacy)
453
+ * sessions are dehydrated the old way so their only copy is preserved.
454
+ * Returns the number of sessions reclaimed.
455
+ */
456
+ async sweepIdleSessions(evictAfterMs) {
457
+ if (!(evictAfterMs > 0))
458
+ return 0;
459
+ const now = Date.now();
460
+ // Only sessions THIS manager has actually served since boot are
461
+ // eviction candidates. Stranger dirs on disk (leftovers from a
462
+ // previous container life, or another embedded worker sharing the
463
+ // sessionStateDir) must never be pushed to the store — a stale dir
464
+ // dehydrated over a newer snapshot silently rolls the session back.
465
+ const candidates = new Set([
466
+ ...this.sessions.keys(),
467
+ ...this.sessionLastTouchedAt.keys(),
468
+ ]);
469
+ let reclaimed = 0;
470
+ for (const sessionId of candidates) {
471
+ if (this.sessionLocks.has(sessionId))
472
+ continue; // busy — never race a turn
473
+ const sessionDir = path.join(this.sessionStateDir, sessionId);
474
+ const lastTouched = this.sessionLastTouchedAt.get(sessionId);
475
+ if (lastTouched == null || now - lastTouched < evictAfterMs)
476
+ continue;
477
+ try {
478
+ await this._withSessionLock(sessionId, "eviction", async () => {
479
+ // Re-check under the lock — a turn may have landed.
480
+ const touched = this.sessionLastTouchedAt.get(sessionId);
481
+ if (touched != null && Date.now() - touched < evictAfterMs)
482
+ return;
483
+ const dirExists = fs.existsSync(sessionDir);
484
+ const committedMarker = dirExists ? readSnapshotMarker(sessionDir) : null;
485
+ const existing = this.sessions.get(sessionId);
486
+ if (existing) {
487
+ try {
488
+ await existing.destroy();
489
+ }
490
+ catch { }
491
+ this.sessions.delete(sessionId);
492
+ }
493
+ if (!dirExists) {
494
+ this.sessionLastTouchedAt.delete(sessionId);
495
+ reclaimed++;
496
+ return;
497
+ }
498
+ if (committedMarker) {
499
+ // Committed snapshot in the store — pure local delete.
500
+ fs.rmSync(sessionDir, { recursive: true, force: true });
501
+ emitSessionManagerTrace(sessionId, `evicted (committed at v${committedMarker.version}); local cache reclaimed`);
502
+ }
503
+ else if (this.sessionStore) {
504
+ // Unmarked dir. If the store already holds a VERSIONED
505
+ // chain for this session, the local files are a stale
506
+ // cache at best — reclaim without writing (a legacy
507
+ // dehydrate would destroy the CAS metadata and could
508
+ // roll the session back).
509
+ const versioned = supportsVersionedSnapshots(this.sessionStore)
510
+ ? await this.sessionStore.probeSnapshot(sessionId).catch(() => null)
511
+ : null;
512
+ if (versioned?.exists && !versioned.legacy) {
513
+ fs.rmSync(sessionDir, { recursive: true, force: true });
514
+ emitSessionManagerTrace(sessionId, `evicted (store versioned at v${versioned.version}); stale unmarked cache reclaimed`);
515
+ }
516
+ else {
517
+ // Legacy session: its local files may be the only copy.
518
+ await this._dehydrateUnlocked(sessionId, "eviction");
519
+ emitSessionManagerTrace(sessionId, "evicted via legacy dehydrate (no committed marker)");
520
+ }
521
+ }
522
+ else {
523
+ return; // no store — leave local state alone
524
+ }
525
+ this.sessionLastTouchedAt.delete(sessionId);
526
+ this.sessionClientKeys.delete(sessionId);
527
+ reclaimed++;
528
+ });
529
+ }
530
+ catch (error) {
531
+ const message = error instanceof Error ? error.message : String(error);
532
+ console.warn(`[SessionManager] eviction sweep skipped ${sessionId}: ${message}`);
533
+ }
534
+ }
535
+ // Reap crash-orphaned hydrate temp roots (finally blocks don't run
536
+ // on SIGKILL). They are dot-prefixed and never legitimate sessions.
537
+ try {
538
+ for (const entry of fs.readdirSync(this.sessionStateDir, { withFileTypes: true })) {
539
+ if (!entry.isDirectory() || !entry.name.startsWith(".ps-hydrate-"))
540
+ continue;
541
+ const orphan = path.join(this.sessionStateDir, entry.name);
542
+ try {
543
+ if (now - fs.statSync(orphan).mtimeMs > 3_600_000) {
544
+ fs.rmSync(orphan, { recursive: true, force: true });
545
+ }
546
+ }
547
+ catch { }
548
+ }
549
+ }
550
+ catch { }
551
+ return reclaimed;
437
552
  }
438
553
  /**
439
554
  * Stop-turn interrupt primitive: abort the warm session's in-flight turn.
@@ -508,6 +623,7 @@ export class SessionManager {
508
623
  return this._getOrCreateUnlocked(sessionId, serializableConfig, options);
509
624
  }
510
625
  async _getOrCreateUnlocked(sessionId, serializableConfig, options) {
626
+ this.sessionLastTouchedAt.set(sessionId, Date.now());
511
627
  const turnIndex = options?.turnIndex;
512
628
  const trace = options?.trace;
513
629
  const inheritedToolNames = Array.from(new Set([
@@ -838,6 +954,26 @@ export class SessionManager {
838
954
  get(sessionId) {
839
955
  return this.sessions.get(sessionId) ?? null;
840
956
  }
957
+ /** Root directory holding per-session state dirs. */
958
+ getSessionStateDir() {
959
+ return this.sessionStateDir;
960
+ }
961
+ /**
962
+ * Destroy the in-memory ManagedSession only — disk state untouched.
963
+ * Used by the lifecycle preamble before overwriting local files with a
964
+ * hydrated snapshot (a warm session bound to the old files must not
965
+ * survive the swap). Caller holds the per-session run-turn lock.
966
+ */
967
+ async dropWarmSession(sessionId) {
968
+ const existing = this.sessions.get(sessionId);
969
+ if (!existing)
970
+ return;
971
+ try {
972
+ await existing.destroy();
973
+ }
974
+ catch { }
975
+ this.sessions.delete(sessionId);
976
+ }
841
977
  /**
842
978
  * Dehydrate a session: snapshot to the session store, release in-memory state.
843
979
  *