poly-weaver 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/ansi.d.ts +7 -0
  2. package/dist/ansi.d.ts.map +1 -1
  3. package/dist/ansi.js +30 -0
  4. package/dist/ansi.js.map +1 -1
  5. package/dist/app-header.d.ts +16 -0
  6. package/dist/app-header.d.ts.map +1 -0
  7. package/dist/app-header.js +74 -0
  8. package/dist/app-header.js.map +1 -0
  9. package/dist/cli.js +81 -59
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts +1 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +6 -0
  14. package/dist/config.js.map +1 -1
  15. package/dist/dump/collector.d.ts +2 -1
  16. package/dist/dump/collector.d.ts.map +1 -1
  17. package/dist/dump/collector.js +1 -0
  18. package/dist/dump/collector.js.map +1 -1
  19. package/dist/dump/hotkey.d.ts +9 -30
  20. package/dist/dump/hotkey.d.ts.map +1 -1
  21. package/dist/dump/hotkey.js +15 -158
  22. package/dist/dump/hotkey.js.map +1 -1
  23. package/dist/dump/service.d.ts +3 -2
  24. package/dist/dump/service.d.ts.map +1 -1
  25. package/dist/dump/service.js +4 -1
  26. package/dist/dump/service.js.map +1 -1
  27. package/dist/dump/types.d.ts +5 -0
  28. package/dist/dump/types.d.ts.map +1 -1
  29. package/dist/flow/built-in/default-factory.d.ts +21 -0
  30. package/dist/flow/built-in/default-factory.d.ts.map +1 -0
  31. package/dist/flow/built-in/default-factory.js +155 -0
  32. package/dist/flow/built-in/default-factory.js.map +1 -0
  33. package/dist/flow/built-in/default.d.ts +4 -4
  34. package/dist/flow/built-in/default.d.ts.map +1 -1
  35. package/dist/flow/built-in/default.js +3 -141
  36. package/dist/flow/built-in/default.js.map +1 -1
  37. package/dist/flow/built-in/default.ts +5 -169
  38. package/dist/flow/built-in/why-so-serious-factory.d.ts +16 -0
  39. package/dist/flow/built-in/why-so-serious-factory.d.ts.map +1 -0
  40. package/dist/flow/built-in/why-so-serious-factory.js +187 -0
  41. package/dist/flow/built-in/why-so-serious-factory.js.map +1 -0
  42. package/dist/flow/built-in/why-so-serious.d.ts +3 -2
  43. package/dist/flow/built-in/why-so-serious.d.ts.map +1 -1
  44. package/dist/flow/built-in/why-so-serious.js +3 -164
  45. package/dist/flow/built-in/why-so-serious.js.map +1 -1
  46. package/dist/flow/custom/AUTHORING.md +13 -0
  47. package/dist/flow/custom/load.d.ts +16 -1
  48. package/dist/flow/custom/load.d.ts.map +1 -1
  49. package/dist/flow/custom/load.js +69 -18
  50. package/dist/flow/custom/load.js.map +1 -1
  51. package/dist/flow/dsl.js +1 -1
  52. package/dist/flow/dsl.js.map +1 -1
  53. package/dist/flow/executor.d.ts.map +1 -1
  54. package/dist/flow/executor.js +44 -8
  55. package/dist/flow/executor.js.map +1 -1
  56. package/dist/flow/types.d.ts +20 -2
  57. package/dist/flow/types.d.ts.map +1 -1
  58. package/dist/flow/types.js.map +1 -1
  59. package/dist/flow-editor/tui.d.ts.map +1 -1
  60. package/dist/flow-editor/tui.js +9 -18
  61. package/dist/flow-editor/tui.js.map +1 -1
  62. package/dist/orchestrator.d.ts +43 -0
  63. package/dist/orchestrator.d.ts.map +1 -1
  64. package/dist/orchestrator.js +230 -92
  65. package/dist/orchestrator.js.map +1 -1
  66. package/dist/providers/claude/completion-plan-mode.d.ts.map +1 -1
  67. package/dist/providers/claude/completion-plan-mode.js +5 -2
  68. package/dist/providers/claude/completion-plan-mode.js.map +1 -1
  69. package/dist/providers/claude/tool-tracking.d.ts +1 -0
  70. package/dist/providers/claude/tool-tracking.d.ts.map +1 -1
  71. package/dist/providers/claude/tool-tracking.js +3 -0
  72. package/dist/providers/claude/tool-tracking.js.map +1 -1
  73. package/dist/pty/spawn.d.ts.map +1 -1
  74. package/dist/pty/spawn.js +6 -1
  75. package/dist/pty/spawn.js.map +1 -1
  76. package/dist/pty/types.d.ts +7 -2
  77. package/dist/pty/types.d.ts.map +1 -1
  78. package/dist/resume-tui.d.ts +52 -0
  79. package/dist/resume-tui.d.ts.map +1 -0
  80. package/dist/resume-tui.js +344 -0
  81. package/dist/resume-tui.js.map +1 -0
  82. package/dist/session/flow-snapshot.d.ts +14 -0
  83. package/dist/session/flow-snapshot.d.ts.map +1 -0
  84. package/dist/session/flow-snapshot.js +28 -0
  85. package/dist/session/flow-snapshot.js.map +1 -0
  86. package/dist/session/list.d.ts +16 -0
  87. package/dist/session/list.d.ts.map +1 -0
  88. package/dist/session/list.js +89 -0
  89. package/dist/session/list.js.map +1 -0
  90. package/dist/session/lock.d.ts +26 -0
  91. package/dist/session/lock.d.ts.map +1 -0
  92. package/dist/session/lock.js +95 -0
  93. package/dist/session/lock.js.map +1 -0
  94. package/dist/session/manifest.d.ts +80 -0
  95. package/dist/session/manifest.d.ts.map +1 -0
  96. package/dist/session/manifest.js +2 -0
  97. package/dist/session/manifest.js.map +1 -0
  98. package/dist/session/persistence.d.ts +19 -0
  99. package/dist/session/persistence.d.ts.map +1 -0
  100. package/dist/session/persistence.js +68 -0
  101. package/dist/session/persistence.js.map +1 -0
  102. package/dist/session/process-start-time.d.ts +17 -0
  103. package/dist/session/process-start-time.d.ts.map +1 -0
  104. package/dist/session/process-start-time.js +99 -0
  105. package/dist/session/process-start-time.js.map +1 -0
  106. package/dist/session/restore.d.ts +31 -0
  107. package/dist/session/restore.d.ts.map +1 -0
  108. package/dist/session/restore.js +111 -0
  109. package/dist/session/restore.js.map +1 -0
  110. package/dist/session/resume.d.ts +5 -0
  111. package/dist/session/resume.d.ts.map +1 -0
  112. package/dist/session/resume.js +109 -0
  113. package/dist/session/resume.js.map +1 -0
  114. package/dist/session/session-store.d.ts +43 -0
  115. package/dist/session/session-store.d.ts.map +1 -0
  116. package/dist/session/session-store.js +134 -0
  117. package/dist/session/session-store.js.map +1 -0
  118. package/dist/startup-tui.d.ts +10 -11
  119. package/dist/startup-tui.d.ts.map +1 -1
  120. package/dist/startup-tui.js +25 -108
  121. package/dist/startup-tui.js.map +1 -1
  122. package/dist/status-bar.js +1 -26
  123. package/dist/status-bar.js.map +1 -1
  124. package/dist/terminal/host.d.ts +22 -9
  125. package/dist/terminal/host.d.ts.map +1 -1
  126. package/dist/terminal/host.js +67 -78
  127. package/dist/terminal/host.js.map +1 -1
  128. package/dist/terminal/input-router.d.ts +24 -103
  129. package/dist/terminal/input-router.d.ts.map +1 -1
  130. package/dist/terminal/input-router.js +63 -364
  131. package/dist/terminal/input-router.js.map +1 -1
  132. package/dist/terminal/key-event-source.d.ts +52 -0
  133. package/dist/terminal/key-event-source.d.ts.map +1 -0
  134. package/dist/terminal/key-event-source.js +31 -0
  135. package/dist/terminal/key-event-source.js.map +1 -0
  136. package/dist/terminal/key-to-bytes.d.ts +11 -0
  137. package/dist/terminal/key-to-bytes.d.ts.map +1 -0
  138. package/dist/terminal/key-to-bytes.js +81 -0
  139. package/dist/terminal/key-to-bytes.js.map +1 -0
  140. package/dist/terminal/koffi-loader.d.ts +14 -0
  141. package/dist/terminal/koffi-loader.d.ts.map +1 -1
  142. package/dist/terminal/koffi-loader.js.map +1 -1
  143. package/dist/terminal/render.d.ts +1 -2
  144. package/dist/terminal/render.d.ts.map +1 -1
  145. package/dist/terminal/render.js +1 -30
  146. package/dist/terminal/render.js.map +1 -1
  147. package/dist/terminal/stdin-byte-source.d.ts +27 -0
  148. package/dist/terminal/stdin-byte-source.d.ts.map +1 -0
  149. package/dist/terminal/stdin-byte-source.js +92 -0
  150. package/dist/terminal/stdin-byte-source.js.map +1 -0
  151. package/dist/terminal/win32-console-mode.d.ts.map +1 -1
  152. package/dist/terminal/win32-console-mode.js.map +1 -1
  153. package/dist/terminal/win32-console-source.d.ts +26 -0
  154. package/dist/terminal/win32-console-source.d.ts.map +1 -0
  155. package/dist/terminal/win32-console-source.js +275 -0
  156. package/dist/terminal/win32-console-source.js.map +1 -0
  157. package/dist/terminal/win32-key-translator.d.ts +26 -0
  158. package/dist/terminal/win32-key-translator.d.ts.map +1 -0
  159. package/dist/terminal/win32-key-translator.js +87 -0
  160. package/dist/terminal/win32-key-translator.js.map +1 -0
  161. package/dist/terminal-input.d.ts +10 -0
  162. package/dist/terminal-input.d.ts.map +1 -1
  163. package/dist/terminal-input.js +132 -113
  164. package/dist/terminal-input.js.map +1 -1
  165. package/dist/user/host-curate-prompt.d.ts +2 -1
  166. package/dist/user/host-curate-prompt.d.ts.map +1 -1
  167. package/dist/user/host-curate-prompt.js +13 -11
  168. package/dist/user/host-curate-prompt.js.map +1 -1
  169. package/dist/user/host-prompt.d.ts +4 -1
  170. package/dist/user/host-prompt.d.ts.map +1 -1
  171. package/dist/user/host-prompt.js +24 -6
  172. package/dist/user/host-prompt.js.map +1 -1
  173. package/dist/user/prompt.d.ts +7 -9
  174. package/dist/user/prompt.d.ts.map +1 -1
  175. package/dist/user/prompt.js +14 -23
  176. package/dist/user/prompt.js.map +1 -1
  177. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ import { hostname } from "node:os";
2
+ import { join } from "node:path";
3
+ import { unlink } from "node:fs/promises";
4
+ import { atomicWriteJson, readJsonIfExists } from "./persistence.js";
5
+ import { getProcessStartTime } from "./process-start-time.js";
6
+ const LOCK_FILE = "lock.json";
7
+ export async function acquireLock(persistentDir, sessionCreatedAt) {
8
+ const startTime = await getProcessStartTime(process.pid);
9
+ const lock = {
10
+ pid: process.pid,
11
+ hostname: hostname(),
12
+ startedAt: new Date().toISOString(),
13
+ sessionCreatedAt,
14
+ };
15
+ if (startTime !== undefined)
16
+ lock.processStartTime = startTime;
17
+ await atomicWriteJson(join(persistentDir, LOCK_FILE), lock);
18
+ }
19
+ export async function releaseLock(persistentDir) {
20
+ try {
21
+ await unlink(join(persistentDir, LOCK_FILE));
22
+ }
23
+ catch (err) {
24
+ if (err.code !== "ENOENT")
25
+ throw err;
26
+ }
27
+ try {
28
+ await unlink(join(persistentDir, `${LOCK_FILE}.new`));
29
+ }
30
+ catch (err) {
31
+ if (err.code !== "ENOENT")
32
+ throw err;
33
+ }
34
+ }
35
+ /**
36
+ * Decide whether `lock` (already-loaded LockFile) represents a live holder for
37
+ * a session created at `manifestCreatedAt`. See {@link isLockAlive} for the
38
+ * full rule list.
39
+ */
40
+ async function isLockEntryAlive(lock, manifestCreatedAt) {
41
+ if (lock.sessionCreatedAt !== manifestCreatedAt)
42
+ return false;
43
+ if (lock.hostname !== hostname())
44
+ return true;
45
+ try {
46
+ process.kill(lock.pid, 0);
47
+ }
48
+ catch (err) {
49
+ const code = err.code;
50
+ if (code === "ESRCH")
51
+ return false;
52
+ if (code === "EPERM")
53
+ return true;
54
+ return false;
55
+ }
56
+ if (lock.processStartTime) {
57
+ const currentStartTime = await getProcessStartTime(lock.pid);
58
+ if (currentStartTime === undefined)
59
+ return true;
60
+ if (currentStartTime !== lock.processStartTime)
61
+ return false;
62
+ }
63
+ return true;
64
+ }
65
+ /**
66
+ * Returns true when another live process likely holds the lock.
67
+ * - Missing lock → false.
68
+ * - Different host → true (defensive cross-host default).
69
+ * - Same host: `process.kill(pid, 0)` — ESRCH ⇒ false, EPERM ⇒ true, success ⇒ true.
70
+ * - Extra guard: if `lock.sessionCreatedAt !== manifestCreatedAt`, the lock is
71
+ * stale (inherited by an unrelated PID via PID reuse) → false.
72
+ * - PID-reuse guard: when the lock has a `processStartTime` and the current
73
+ * start time for `pid` is known and disagrees, the PID has been reused by
74
+ * an unrelated process → false.
75
+ */
76
+ export async function isLockAlive(persistentDir, manifestCreatedAt) {
77
+ const lock = await readJsonIfExists(join(persistentDir, LOCK_FILE));
78
+ if (!lock)
79
+ return false;
80
+ return isLockEntryAlive(lock, manifestCreatedAt);
81
+ }
82
+ /**
83
+ * Single-read combined liveness check + summary for the picker. Returns
84
+ * `undefined` when the lock is absent or stale; returns the holder summary
85
+ * when alive.
86
+ */
87
+ export async function readLiveLockSummary(persistentDir, manifestCreatedAt) {
88
+ const lock = await readJsonIfExists(join(persistentDir, LOCK_FILE));
89
+ if (!lock)
90
+ return undefined;
91
+ if (!(await isLockEntryAlive(lock, manifestCreatedAt)))
92
+ return undefined;
93
+ return { pid: lock.pid, hostname: lock.hostname, startedAt: lock.startedAt };
94
+ }
95
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/session/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAkB9D,MAAM,SAAS,GAAG,WAAW,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,aAAqB,EACrB,gBAAwB;IAExB,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzD,MAAM,IAAI,GAAa;QACrB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,QAAQ,EAAE;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,gBAAgB;KACjB,CAAC;IACF,IAAI,SAAS,KAAK,SAAS;QAAE,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAC/D,MAAM,eAAe,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,aAAqB;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,SAAS,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;AACH,CAAC;AAQD;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAC7B,IAAc,EACd,iBAAyB;IAEzB,IAAI,IAAI,CAAC,gBAAgB,KAAK,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC;QACnC,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,MAAM,gBAAgB,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7D,IAAI,gBAAgB,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QAChD,IAAI,gBAAgB,KAAK,IAAI,CAAC,gBAAgB;YAAE,OAAO,KAAK,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,aAAqB,EACrB,iBAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAW,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAC9E,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,aAAqB,EACrB,iBAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAW,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAC9E,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,80 @@
1
+ import type { FlowParamValues } from "../flow/types.js";
2
+ export interface ArtifactRef {
3
+ storeKey: string;
4
+ /** When set, the file at this absolute path holds the artifact bytes. */
5
+ persistentPath?: string;
6
+ text?: string;
7
+ /** JSON.stringify(artifact.data) — undefined when artifact.data is undefined. */
8
+ dataJson?: string;
9
+ meta?: Record<string, unknown>;
10
+ }
11
+ export interface SessionStepEntry {
12
+ seq: number;
13
+ stepId: string;
14
+ role: string;
15
+ variant?: string;
16
+ driver?: string;
17
+ phaseLabel: string;
18
+ /** Read-side declared inputs — informational (recoverable from artifactStore). */
19
+ inputs: Record<string, ArtifactRef>;
20
+ /** Write-side declared outputs — informational. */
21
+ outputs: Record<string, ArtifactRef>;
22
+ /**
23
+ * Full artifact store at the moment this step completed, keyed by storeKey.
24
+ * Source of truth for resume restoration — covers artifacts mutated outside
25
+ * step.writes (notably TASK after incorporateFeedback).
26
+ */
27
+ artifactStore: Record<string, ArtifactRef>;
28
+ loopIterations: Record<string, number>;
29
+ loopCycle: Record<string, number>;
30
+ totalIterations: number;
31
+ nextClarificationNumber: number;
32
+ /**
33
+ * Provider session IDs that were live at the moment this step completed.
34
+ * Informational-only: recorded for diagnostics/dumps. NOT read back at
35
+ * resume time — a resumed poly-weaver process always launches a fresh
36
+ * provider session on its first non-fast-forwarded step, because
37
+ * cross-process provider session continuity is not supported.
38
+ */
39
+ sessionIds: Record<string, string>;
40
+ completedAt: string;
41
+ }
42
+ export interface SessionManifest {
43
+ version: 1;
44
+ sessionId: string;
45
+ createdAt: string;
46
+ lastActivityAt: string;
47
+ status: "active" | "complete";
48
+ workdir: string;
49
+ taskText: string;
50
+ flow: {
51
+ name: string;
52
+ kind: "built-in" | "custom";
53
+ params: FlowParamValues;
54
+ };
55
+ /** Resolved orthogonal options — must round-trip on resume. */
56
+ options: {
57
+ opinionless: boolean;
58
+ yolo: boolean;
59
+ };
60
+ /** -1 before the first checkpoint. */
61
+ lastCompletedSeq: number;
62
+ polyWeaverVersion: string;
63
+ }
64
+ /** Picker/list view of a session — lightweight summary built from the manifest. */
65
+ export interface SessionDescriptor {
66
+ sessionId: string;
67
+ persistentDir: string;
68
+ manifest: SessionManifest;
69
+ /** Whether `manifest.workdir` currently exists on disk. */
70
+ workdirExists: boolean;
71
+ /** Short label of the last completed step (for picker rendering). */
72
+ lastStepLabel?: string;
73
+ /** Present iff another live poly-weaver process holds the lock. */
74
+ lock?: {
75
+ pid: number;
76
+ hostname: string;
77
+ startedAt: string;
78
+ };
79
+ }
80
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/session/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IAEnB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAErC;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAE3C,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;QAC5B,MAAM,EAAE,eAAe,CAAC;KACzB,CAAC;IACF,+DAA+D;IAC/D,OAAO,EAAE;QACP,WAAW,EAAE,OAAO,CAAC;QACrB,IAAI,EAAE,OAAO,CAAC;KACf,CAAC;IACF,sCAAsC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,eAAe,CAAC;IAC1B,2DAA2D;IAC3D,aAAa,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,IAAI,CAAC,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/session/manifest.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ export declare function ensureDir(path: string): Promise<void>;
2
+ /** Standard per-step checkpoint filename, e.g. `step-0042.json`. */
3
+ export declare function stepFilename(seq: number): string;
4
+ /**
5
+ * Atomically write JSON to `path`. Writes to `${path}.new`, then on Windows
6
+ * unlinks any existing target (rename fails on existing target), then renames.
7
+ * Crash-recovery: if a process is killed between unlink and rename, the next
8
+ * `readJsonIfExists(path)` finds `${path}.new` and promotes it.
9
+ */
10
+ export declare function atomicWriteJson(path: string, obj: unknown): Promise<void>;
11
+ /** True when `path` exists and is a directory. */
12
+ export declare function dirExists(path: string): Promise<boolean>;
13
+ /**
14
+ * Read JSON from `path`. If `path` is missing but `${path}.new` exists
15
+ * (mid-rename crash), promote `.new` to `path` and read it.
16
+ * Returns `undefined` if neither file exists.
17
+ */
18
+ export declare function readJsonIfExists<T>(path: string): Promise<T | undefined>;
19
+ //# sourceMappingURL=persistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../src/session/persistence.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3D;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/E;AAED,kDAAkD;AAClD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAOD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAgB9E"}
@@ -0,0 +1,68 @@
1
+ import { mkdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
2
+ export async function ensureDir(path) {
3
+ await mkdir(path, { recursive: true });
4
+ }
5
+ /** Standard per-step checkpoint filename, e.g. `step-0042.json`. */
6
+ export function stepFilename(seq) {
7
+ return `step-${String(seq).padStart(4, "0")}.json`;
8
+ }
9
+ /**
10
+ * Atomically write JSON to `path`. Writes to `${path}.new`, then on Windows
11
+ * unlinks any existing target (rename fails on existing target), then renames.
12
+ * Crash-recovery: if a process is killed between unlink and rename, the next
13
+ * `readJsonIfExists(path)` finds `${path}.new` and promotes it.
14
+ */
15
+ export async function atomicWriteJson(path, obj) {
16
+ const tmp = `${path}.new`;
17
+ const body = JSON.stringify(obj, null, 2);
18
+ await writeFile(tmp, body, "utf-8");
19
+ try {
20
+ await unlink(path);
21
+ }
22
+ catch (err) {
23
+ if (err.code !== "ENOENT")
24
+ throw err;
25
+ }
26
+ await rename(tmp, path);
27
+ }
28
+ /** True when `path` exists and is a directory. */
29
+ export async function dirExists(path) {
30
+ try {
31
+ const s = await stat(path);
32
+ return s.isDirectory();
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ function isNotFound(err) {
39
+ const code = err.code;
40
+ return code === "ENOENT" || code === "ENOTDIR";
41
+ }
42
+ /**
43
+ * Read JSON from `path`. If `path` is missing but `${path}.new` exists
44
+ * (mid-rename crash), promote `.new` to `path` and read it.
45
+ * Returns `undefined` if neither file exists.
46
+ */
47
+ export async function readJsonIfExists(path) {
48
+ let raw;
49
+ try {
50
+ raw = await readFile(path, "utf-8");
51
+ }
52
+ catch (err) {
53
+ if (!isNotFound(err))
54
+ throw err;
55
+ const tmp = `${path}.new`;
56
+ try {
57
+ await rename(tmp, path);
58
+ }
59
+ catch (renameErr) {
60
+ if (isNotFound(renameErr))
61
+ return undefined;
62
+ throw renameErr;
63
+ }
64
+ raw = await readFile(path, "utf-8");
65
+ }
66
+ return JSON.parse(raw);
67
+ }
68
+ //# sourceMappingURL=persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../src/session/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,QAAQ,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,GAAY;IAC9D,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;IACD,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,GAAY;IAC9B,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;IACjD,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAI,IAAY;IACpD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,SAAkB,EAAE,CAAC;YAC5B,IAAI,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;YAC5C,MAAM,SAAS,CAAC;QAClB,CAAC;QACD,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns an opaque, platform-stable string identifying the OS-reported
3
+ * creation time of the process at `pid`, or `undefined` when:
4
+ * - the platform implementation is unavailable,
5
+ * - the process at `pid` no longer exists,
6
+ * - any underlying syscall / FFI / parse error.
7
+ *
8
+ * Comparison semantics: equality on the string. The format need not be
9
+ * human-readable — only that two reads on the same machine for the same
10
+ * live process yield equal strings.
11
+ *
12
+ * Used by lock liveness to detect PID reuse: a PID may be reused by an
13
+ * unrelated process within seconds on Windows; comparing the start-time
14
+ * captured at lock acquire against the current value catches that.
15
+ */
16
+ export declare function getProcessStartTime(pid: number): Promise<string | undefined>;
17
+ //# sourceMappingURL=process-start-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-start-time.d.ts","sourceRoot":"","sources":["../../src/session/process-start-time.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAIlF"}
@@ -0,0 +1,99 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { loadKoffi } from "../terminal/koffi-loader.js";
3
+ const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
4
+ /**
5
+ * Returns an opaque, platform-stable string identifying the OS-reported
6
+ * creation time of the process at `pid`, or `undefined` when:
7
+ * - the platform implementation is unavailable,
8
+ * - the process at `pid` no longer exists,
9
+ * - any underlying syscall / FFI / parse error.
10
+ *
11
+ * Comparison semantics: equality on the string. The format need not be
12
+ * human-readable — only that two reads on the same machine for the same
13
+ * live process yield equal strings.
14
+ *
15
+ * Used by lock liveness to detect PID reuse: a PID may be reused by an
16
+ * unrelated process within seconds on Windows; comparing the start-time
17
+ * captured at lock acquire against the current value catches that.
18
+ */
19
+ export async function getProcessStartTime(pid) {
20
+ if (process.platform === "win32")
21
+ return getWindowsStartTime(pid);
22
+ if (process.platform === "linux")
23
+ return getLinuxStartTime(pid);
24
+ return undefined;
25
+ }
26
+ async function getWindowsStartTime(pid) {
27
+ const koffi = loadKoffi();
28
+ if (!koffi)
29
+ return undefined;
30
+ try {
31
+ const kernel32 = koffi.load("kernel32.dll");
32
+ // Win32 FILETIME is a struct of two DWORDs (low, high) representing
33
+ // 100-ns intervals since 1601-01-01 UTC. GetProcessTimes takes four
34
+ // FILETIME* pointers — creation, exit, kernel, user — so the koffi
35
+ // binding must match that 4-pointer signature, not eight uint32* slots.
36
+ const FILETIME = koffi.struct("FILETIME", {
37
+ dwLowDateTime: "uint32",
38
+ dwHighDateTime: "uint32",
39
+ });
40
+ const FILETIME_PTR_OUT = koffi.out(koffi.pointer(FILETIME));
41
+ const openProcess = kernel32.func("OpenProcess", "void *", ["uint32", "int", "uint32"]);
42
+ const getProcessTimes = kernel32.func("GetProcessTimes", "int", [
43
+ "void *",
44
+ FILETIME_PTR_OUT,
45
+ FILETIME_PTR_OUT,
46
+ FILETIME_PTR_OUT,
47
+ FILETIME_PTR_OUT,
48
+ ]);
49
+ const closeHandle = kernel32.func("CloseHandle", "int", ["void *"]);
50
+ const handle = openProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
51
+ if (!handle)
52
+ return undefined;
53
+ try {
54
+ const creation = { dwLowDateTime: 0, dwHighDateTime: 0 };
55
+ const exit = { dwLowDateTime: 0, dwHighDateTime: 0 };
56
+ const kernel = { dwLowDateTime: 0, dwHighDateTime: 0 };
57
+ const user = { dwLowDateTime: 0, dwHighDateTime: 0 };
58
+ const ok = getProcessTimes(handle, creation, exit, kernel, user);
59
+ if (!ok)
60
+ return undefined;
61
+ const high = (creation.dwHighDateTime ?? 0) >>> 0;
62
+ const low = (creation.dwLowDateTime ?? 0) >>> 0;
63
+ return `${high.toString(16)}:${low.toString(16)}`;
64
+ }
65
+ finally {
66
+ try {
67
+ closeHandle(handle);
68
+ }
69
+ catch {
70
+ /* best-effort */
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ }
78
+ async function getLinuxStartTime(pid) {
79
+ try {
80
+ const stat = await readFile(`/proc/${pid}/stat`, "utf8");
81
+ // Field 22 (1-indexed) is starttime. Field 2 (comm) is in parens and may
82
+ // contain spaces — parse by finding the trailing `)`.
83
+ const closeParen = stat.lastIndexOf(")");
84
+ if (closeParen < 0)
85
+ return undefined;
86
+ const after = stat.slice(closeParen + 2);
87
+ const parts = after.split(/\s+/);
88
+ // After `comm`, field 3 (state) is parts[0]. starttime is field 22, so
89
+ // index 22 - 3 = 19 within `parts`.
90
+ const starttime = parts[19];
91
+ if (!starttime)
92
+ return undefined;
93
+ return starttime;
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ //# sourceMappingURL=process-start-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-start-time.js","sourceRoot":"","sources":["../../src/session/process-start-time.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { StepRecord } from "../dump/types.js";
2
+ import type { AgentStep, FlowStep, StepContext, StepSignal, UserStep } from "../flow/types.js";
3
+ import type { SessionStepEntry } from "./manifest.js";
4
+ /**
5
+ * Restore one step's snapshot into the live context.
6
+ * The single place that copies persistent artifact files into the
7
+ * ephemeral session dir during fast-forward.
8
+ *
9
+ * Idempotent — calling twice with the same snapshot is safe (overwrites).
10
+ */
11
+ export declare function applyRestoredSnapshot(snap: SessionStepEntry, ctx: StepContext): Promise<void>;
12
+ /**
13
+ * Synthesize a StepRecord for a fast-forwarded step so listeners
14
+ * (DumpService, SessionStore) see the same shape as a freshly executed step.
15
+ */
16
+ export declare function buildSkippedRecord(seq: number, step: FlowStep, snap: SessionStepEntry | undefined): StepRecord;
17
+ /**
18
+ * Reconstruct the StepSignal the original run produced for a fast-forwarded
19
+ * step, so resume preserves the original loop control flow.
20
+ *
21
+ * Agent handlers always return `continue`. For user steps we infer from the
22
+ * snapshot:
23
+ * - verdict.meta.source === "user" → restart-loop (user rejected)
24
+ * - no user-sourced verdict + breakOnAccept → break-loop (accept-and-exit)
25
+ * - otherwise → continue
26
+ *
27
+ * Degraded inputs (missing snapshot, no `writes.verdict`) fall back to
28
+ * `continue` so a corrupted/partial session does not get stuck on resume.
29
+ */
30
+ export declare function inferRestoredStepSignal(step: AgentStep | UserStep, snap: SessionStepEntry | undefined): StepSignal;
31
+ //# sourceMappingURL=restore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/session/restore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EACV,SAAS,EAET,QAAQ,EACR,WAAW,EACX,UAAU,EACV,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAe,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,gBAAgB,GAAG,SAAS,GACjC,UAAU,CA0BZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,GAAG,QAAQ,EAC1B,IAAI,EAAE,gBAAgB,GAAG,SAAS,GACjC,UAAU,CAYZ"}
@@ -0,0 +1,111 @@
1
+ import { basename, join } from "node:path";
2
+ import { copyFile } from "node:fs/promises";
3
+ /**
4
+ * Restore one step's snapshot into the live context.
5
+ * The single place that copies persistent artifact files into the
6
+ * ephemeral session dir during fast-forward.
7
+ *
8
+ * Idempotent — calling twice with the same snapshot is safe (overwrites).
9
+ */
10
+ export async function applyRestoredSnapshot(snap, ctx) {
11
+ for (const [storeKey, ref] of Object.entries(snap.artifactStore)) {
12
+ let file;
13
+ if (ref.persistentPath && ctx.resumeEphemeralDir) {
14
+ const dest = join(ctx.resumeEphemeralDir, basename(ref.persistentPath));
15
+ try {
16
+ await copyFile(ref.persistentPath, dest);
17
+ file = dest;
18
+ }
19
+ catch {
20
+ // Source missing — fall through with text/data only.
21
+ }
22
+ }
23
+ const artifact = {};
24
+ if (file !== undefined)
25
+ artifact.file = file;
26
+ if (ref.text !== undefined)
27
+ artifact.text = ref.text;
28
+ if (ref.dataJson !== undefined)
29
+ artifact.data = JSON.parse(ref.dataJson);
30
+ if (ref.meta !== undefined)
31
+ artifact.meta = ref.meta;
32
+ ctx.artifacts.set(storeKey, artifact);
33
+ }
34
+ ctx.loopIterations = new Map(Object.entries(snap.loopIterations));
35
+ ctx.loopCycle = new Map(Object.entries(snap.loopCycle));
36
+ ctx.totalIterations = snap.totalIterations;
37
+ ctx.nextClarificationNumber = snap.nextClarificationNumber;
38
+ // Intentionally NOT rehydrating ctx.sessionIds — cross-process provider
39
+ // session continuity is not supported. A resumed poly-weaver process
40
+ // always launches a fresh provider session on its first non-fast-forwarded
41
+ // step. Same-process subsequent steps still resume normally because the
42
+ // role handler populates ctx.sessionIds on each live execution.
43
+ }
44
+ /**
45
+ * Synthesize a StepRecord for a fast-forwarded step so listeners
46
+ * (DumpService, SessionStore) see the same shape as a freshly executed step.
47
+ */
48
+ export function buildSkippedRecord(seq, step, snap) {
49
+ if (step.type === "loop") {
50
+ throw new Error(`buildSkippedRecord called with a loop step (seq=${seq})`);
51
+ }
52
+ const inputs = {};
53
+ const outputs = {};
54
+ if (snap) {
55
+ for (const [alias, ref] of Object.entries(snap.inputs)) {
56
+ inputs[alias] = refToSnapshot(ref);
57
+ }
58
+ for (const [alias, ref] of Object.entries(snap.outputs)) {
59
+ outputs[alias] = refToSnapshot(ref);
60
+ }
61
+ }
62
+ const phaseLabel = snap?.phaseLabel
63
+ ?? (typeof step.phaseLabel === "string" ? step.phaseLabel : "");
64
+ return {
65
+ seq,
66
+ stepId: step.id,
67
+ role: step.role,
68
+ driver: step.type === "agent" ? step.driver : undefined,
69
+ phaseLabel,
70
+ inputs,
71
+ outputs,
72
+ sessionFilePaths: [],
73
+ };
74
+ }
75
+ /**
76
+ * Reconstruct the StepSignal the original run produced for a fast-forwarded
77
+ * step, so resume preserves the original loop control flow.
78
+ *
79
+ * Agent handlers always return `continue`. For user steps we infer from the
80
+ * snapshot:
81
+ * - verdict.meta.source === "user" → restart-loop (user rejected)
82
+ * - no user-sourced verdict + breakOnAccept → break-loop (accept-and-exit)
83
+ * - otherwise → continue
84
+ *
85
+ * Degraded inputs (missing snapshot, no `writes.verdict`) fall back to
86
+ * `continue` so a corrupted/partial session does not get stuck on resume.
87
+ */
88
+ export function inferRestoredStepSignal(step, snap) {
89
+ if (step.type !== "user")
90
+ return { kind: "continue" };
91
+ if (!snap)
92
+ return { kind: "continue" };
93
+ const verdictKey = step.writes.verdict;
94
+ if (!verdictKey)
95
+ return { kind: "continue" };
96
+ const verdict = snap.artifactStore[verdictKey];
97
+ if (verdict?.meta?.source === "user")
98
+ return { kind: "restart-loop" };
99
+ if (step.breakOnAccept)
100
+ return { kind: "break-loop" };
101
+ return { kind: "continue" };
102
+ }
103
+ function refToSnapshot(ref) {
104
+ return {
105
+ artifactName: ref.storeKey,
106
+ file: ref.persistentPath,
107
+ text: ref.text,
108
+ dataJson: ref.dataJson,
109
+ };
110
+ }
111
+ //# sourceMappingURL=restore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/session/restore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type ExitOutcome } from "../orchestrator.js";
2
+ import type { SessionDescriptor } from "./manifest.js";
3
+ export type ResumedRunResult = ExitOutcome;
4
+ export declare function runResumedSession(session: SessionDescriptor, pkgVersion: string): Promise<ResumedRunResult>;
5
+ //# sourceMappingURL=resume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../src/session/resume.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,WAAW,EAEjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,eAAe,CAAC;AAEzE,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE3C,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAyG3B"}