@elie-laloum/outpost 7.0.0 → 8.0.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 (230) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +2 -0
  3. package/dist/adapters/agents/antigravity-adapter.js +3 -0
  4. package/dist/adapters/agents/antigravity-adapter.js.map +1 -1
  5. package/dist/adapters/agents/claude-adapter.js +3 -0
  6. package/dist/adapters/agents/claude-adapter.js.map +1 -1
  7. package/dist/adapters/agents/claude-events.js +25 -1
  8. package/dist/adapters/agents/claude-events.js.map +1 -1
  9. package/dist/adapters/agents/codex-adapter.js +3 -0
  10. package/dist/adapters/agents/codex-adapter.js.map +1 -1
  11. package/dist/adapters/agents/copilot-adapter.js +3 -0
  12. package/dist/adapters/agents/copilot-adapter.js.map +1 -1
  13. package/dist/adapters/agents/copilot-events.js +7 -3
  14. package/dist/adapters/agents/copilot-events.js.map +1 -1
  15. package/dist/adapters/agents/kimi-adapter.js +3 -0
  16. package/dist/adapters/agents/kimi-adapter.js.map +1 -1
  17. package/dist/adapters/agents/quota.constants.d.ts +7 -0
  18. package/dist/adapters/agents/quota.constants.js +31 -0
  19. package/dist/adapters/agents/quota.constants.js.map +1 -0
  20. package/dist/adapters/agents/quota.d.ts +1 -0
  21. package/dist/adapters/agents/quota.js +4 -0
  22. package/dist/adapters/agents/quota.js.map +1 -0
  23. package/dist/adapters/models/http-provider.js +8 -2
  24. package/dist/adapters/models/http-provider.js.map +1 -1
  25. package/dist/adapters/models/model.constants.d.ts +2 -0
  26. package/dist/adapters/models/model.constants.js +6 -0
  27. package/dist/adapters/models/model.constants.js.map +1 -1
  28. package/dist/adapters/models/stream-events.js +11 -7
  29. package/dist/adapters/models/stream-events.js.map +1 -1
  30. package/dist/application/agent-output.js +10 -1
  31. package/dist/application/agent-output.js.map +1 -1
  32. package/dist/application/agent-quota.d.ts +7 -0
  33. package/dist/application/agent-quota.js +33 -0
  34. package/dist/application/agent-quota.js.map +1 -0
  35. package/dist/application/agent-turn.js +25 -9
  36. package/dist/application/agent-turn.js.map +1 -1
  37. package/dist/application/dispatch-observation.js +10 -1
  38. package/dist/application/dispatch-observation.js.map +1 -1
  39. package/dist/application/execution.types.d.ts +2 -0
  40. package/dist/application/execution.types.js.map +1 -1
  41. package/dist/application/interactive-task-execution.d.ts +3 -0
  42. package/dist/application/interactive-task-execution.js +76 -0
  43. package/dist/application/interactive-task-execution.js.map +1 -0
  44. package/dist/application/interactive-task-protocol.d.ts +4 -0
  45. package/dist/application/interactive-task-protocol.js +56 -0
  46. package/dist/application/interactive-task-protocol.js.map +1 -0
  47. package/dist/application/interactive-task.constants.d.ts +4 -0
  48. package/dist/application/interactive-task.constants.js +7 -0
  49. package/dist/application/interactive-task.constants.js.map +1 -0
  50. package/dist/application/interactive-task.d.ts +3 -0
  51. package/dist/application/interactive-task.js +84 -0
  52. package/dist/application/interactive-task.js.map +1 -0
  53. package/dist/application/interactive-task.types.d.ts +44 -0
  54. package/dist/application/interactive-task.types.js +2 -0
  55. package/dist/application/interactive-task.types.js.map +1 -0
  56. package/dist/application/queue-worker.js +19 -1
  57. package/dist/application/queue-worker.js.map +1 -1
  58. package/dist/application/queued-task.js +17 -2
  59. package/dist/application/queued-task.js.map +1 -1
  60. package/dist/application/quota-resume.constants.d.ts +1 -0
  61. package/dist/application/quota-resume.constants.js +2 -0
  62. package/dist/application/quota-resume.constants.js.map +1 -0
  63. package/dist/application/quota-resume.d.ts +9 -0
  64. package/dist/application/quota-resume.js +34 -0
  65. package/dist/application/quota-resume.js.map +1 -0
  66. package/dist/application/quota-resume.types.d.ts +1 -0
  67. package/dist/application/quota-resume.types.js +2 -0
  68. package/dist/application/quota-resume.types.js.map +1 -0
  69. package/dist/application/recovery-retention-policy.js +1 -1
  70. package/dist/application/recovery-retention-policy.js.map +1 -1
  71. package/dist/application/recovery-retention.js +3 -3
  72. package/dist/application/recovery-retention.js.map +1 -1
  73. package/dist/application/recovery-retention.types.d.ts +1 -1
  74. package/dist/application/recovery-retention.types.js.map +1 -1
  75. package/dist/application/remote-workspace.js +2 -1
  76. package/dist/application/remote-workspace.js.map +1 -1
  77. package/dist/application/replay-recording.d.ts +2 -0
  78. package/dist/application/replay-recording.js +22 -0
  79. package/dist/application/replay-recording.js.map +1 -0
  80. package/dist/application/replay-turn.d.ts +4 -0
  81. package/dist/application/replay-turn.js +47 -0
  82. package/dist/application/replay-turn.js.map +1 -0
  83. package/dist/application/replay-workspace.d.ts +4 -0
  84. package/dist/application/replay-workspace.js +71 -0
  85. package/dist/application/replay-workspace.js.map +1 -0
  86. package/dist/application/replay-workspace.types.d.ts +6 -0
  87. package/dist/application/replay-workspace.types.js +2 -0
  88. package/dist/application/replay-workspace.types.js.map +1 -0
  89. package/dist/application/repository-fingerprint.d.ts +1 -0
  90. package/dist/application/repository-fingerprint.js +18 -0
  91. package/dist/application/repository-fingerprint.js.map +1 -0
  92. package/dist/application/sandbox-dispatch.js +2 -0
  93. package/dist/application/sandbox-dispatch.js.map +1 -1
  94. package/dist/application/speculation-checkpoint.js +19 -2
  95. package/dist/application/speculation-checkpoint.js.map +1 -1
  96. package/dist/application/speculation-checkpoint.types.d.ts +1 -1
  97. package/dist/application/speculation-checkpoint.types.js.map +1 -1
  98. package/dist/application/speculation-resume.js +21 -0
  99. package/dist/application/speculation-resume.js.map +1 -1
  100. package/dist/application/speculation-run.js +18 -1
  101. package/dist/application/speculation-run.js.map +1 -1
  102. package/dist/application/speculation.types.d.ts +7 -2
  103. package/dist/application/speculation.types.js.map +1 -1
  104. package/dist/application/tasks.d.ts +2 -2
  105. package/dist/application/tasks.js +11 -4
  106. package/dist/application/tasks.js.map +1 -1
  107. package/dist/application/tasks.types.d.ts +5 -0
  108. package/dist/application/tasks.types.js.map +1 -1
  109. package/dist/application/transport-inspection.constants.js +1 -0
  110. package/dist/application/transport-inspection.constants.js.map +1 -1
  111. package/dist/application/transport-retention.js +33 -1
  112. package/dist/application/transport-retention.js.map +1 -1
  113. package/dist/domain/agent-observation.js +1 -0
  114. package/dist/domain/agent-observation.js.map +1 -1
  115. package/dist/domain/agent.types.d.ts +8 -1
  116. package/dist/domain/agent.types.js.map +1 -1
  117. package/dist/domain/errors.types.d.ts +1 -1
  118. package/dist/domain/errors.types.js.map +1 -1
  119. package/dist/domain/observation.types.d.ts +6 -1
  120. package/dist/domain/observation.types.js.map +1 -1
  121. package/dist/domain/quota.constants.d.ts +1 -0
  122. package/dist/domain/quota.constants.js +2 -0
  123. package/dist/domain/quota.constants.js.map +1 -0
  124. package/dist/domain/quota.d.ts +4 -0
  125. package/dist/domain/quota.js +28 -0
  126. package/dist/domain/quota.js.map +1 -0
  127. package/dist/domain/quota.types.d.ts +7 -0
  128. package/dist/domain/quota.types.js +2 -0
  129. package/dist/domain/quota.types.js.map +1 -0
  130. package/dist/domain/replay.constants.d.ts +11 -0
  131. package/dist/domain/replay.constants.js +42 -0
  132. package/dist/domain/replay.constants.js.map +1 -0
  133. package/dist/domain/replay.d.ts +11 -0
  134. package/dist/domain/replay.js +231 -0
  135. package/dist/domain/replay.js.map +1 -0
  136. package/dist/domain/replay.types.d.ts +84 -0
  137. package/dist/domain/replay.types.js +2 -0
  138. package/dist/domain/replay.types.js.map +1 -0
  139. package/dist/domain/task-queue.js +18 -0
  140. package/dist/domain/task-queue.js.map +1 -1
  141. package/dist/domain/task-queue.types.d.ts +9 -0
  142. package/dist/domain/task-queue.types.js.map +1 -1
  143. package/dist/domain/workflow/checkpoint-validation.js +14 -1
  144. package/dist/domain/workflow/checkpoint-validation.js.map +1 -1
  145. package/dist/domain/workflow/checkpoint.js +13 -2
  146. package/dist/domain/workflow/checkpoint.js.map +1 -1
  147. package/dist/domain/workflow/gate-validation.js +2 -1
  148. package/dist/domain/workflow/gate-validation.js.map +1 -1
  149. package/dist/domain/workflow/input-validation.d.ts +7 -0
  150. package/dist/domain/workflow/input-validation.js +82 -0
  151. package/dist/domain/workflow/input-validation.js.map +1 -0
  152. package/dist/domain/workflow/input.d.ts +10 -0
  153. package/dist/domain/workflow/input.js +93 -0
  154. package/dist/domain/workflow/input.js.map +1 -0
  155. package/dist/domain/workflow/input.types.d.ts +37 -0
  156. package/dist/domain/workflow/input.types.js +2 -0
  157. package/dist/domain/workflow/input.types.js.map +1 -0
  158. package/dist/domain/workflow/loop-runner.d.ts +3 -0
  159. package/dist/domain/workflow/loop-runner.js +91 -0
  160. package/dist/domain/workflow/loop-runner.js.map +1 -0
  161. package/dist/domain/workflow/loop-task.d.ts +10 -0
  162. package/dist/domain/workflow/loop-task.js +39 -0
  163. package/dist/domain/workflow/loop-task.js.map +1 -0
  164. package/dist/domain/workflow/loop-task.types.d.ts +28 -0
  165. package/dist/domain/workflow/loop-task.types.js +2 -0
  166. package/dist/domain/workflow/loop-task.types.js.map +1 -0
  167. package/dist/domain/workflow/loop-validation.d.ts +4 -0
  168. package/dist/domain/workflow/loop-validation.js +62 -0
  169. package/dist/domain/workflow/loop-validation.js.map +1 -0
  170. package/dist/domain/workflow/quota-pause.d.ts +7 -0
  171. package/dist/domain/workflow/quota-pause.js +83 -0
  172. package/dist/domain/workflow/quota-pause.js.map +1 -0
  173. package/dist/domain/workflow/quota-pause.types.d.ts +14 -0
  174. package/dist/domain/workflow/quota-pause.types.js +2 -0
  175. package/dist/domain/workflow/quota-pause.types.js.map +1 -0
  176. package/dist/domain/workflow/quota-validation.d.ts +2 -0
  177. package/dist/domain/workflow/quota-validation.js +30 -0
  178. package/dist/domain/workflow/quota-validation.js.map +1 -0
  179. package/dist/domain/workflow/task-cache-entry.d.ts +3 -0
  180. package/dist/domain/workflow/task-cache-entry.js +57 -0
  181. package/dist/domain/workflow/task-cache-entry.js.map +1 -0
  182. package/dist/domain/workflow/task-cache.constants.d.ts +3 -0
  183. package/dist/domain/workflow/task-cache.constants.js +3 -0
  184. package/dist/domain/workflow/task-cache.constants.js.map +1 -0
  185. package/dist/domain/workflow/task-cache.d.ts +6 -0
  186. package/dist/domain/workflow/task-cache.js +92 -0
  187. package/dist/domain/workflow/task-cache.js.map +1 -0
  188. package/dist/domain/workflow/task-cache.types.d.ts +35 -0
  189. package/dist/domain/workflow/task-cache.types.js +2 -0
  190. package/dist/domain/workflow/task-cache.types.js.map +1 -0
  191. package/dist/domain/workflow/task-runner.js +100 -34
  192. package/dist/domain/workflow/task-runner.js.map +1 -1
  193. package/dist/domain/workflow/task.js +21 -0
  194. package/dist/domain/workflow/task.js.map +1 -1
  195. package/dist/domain/workflow/validation.js +13 -2
  196. package/dist/domain/workflow/validation.js.map +1 -1
  197. package/dist/domain/workflow/workflow-scheduler.js +27 -2
  198. package/dist/domain/workflow/workflow-scheduler.js.map +1 -1
  199. package/dist/domain/workflow/workflow-state.js +20 -3
  200. package/dist/domain/workflow/workflow-state.js.map +1 -1
  201. package/dist/domain/workflow.types.d.ts +26 -3
  202. package/dist/domain/workflow.types.js.map +1 -1
  203. package/dist/index.d.ts +16 -1
  204. package/dist/index.js +6 -0
  205. package/dist/index.js.map +1 -1
  206. package/dist/infrastructure/git/command.d.ts +2 -1
  207. package/dist/infrastructure/git/command.js +2 -2
  208. package/dist/infrastructure/git/command.js.map +1 -1
  209. package/dist/infrastructure/git/replay-commits.constants.d.ts +5 -0
  210. package/dist/infrastructure/git/replay-commits.constants.js +6 -0
  211. package/dist/infrastructure/git/replay-commits.constants.js.map +1 -0
  212. package/dist/infrastructure/git/replay-commits.d.ts +2 -0
  213. package/dist/infrastructure/git/replay-commits.js +92 -0
  214. package/dist/infrastructure/git/replay-commits.js.map +1 -0
  215. package/dist/infrastructure/journal.types.d.ts +1 -0
  216. package/dist/infrastructure/journal.types.js.map +1 -1
  217. package/dist/infrastructure/reporter.js +3 -0
  218. package/dist/infrastructure/reporter.js.map +1 -1
  219. package/dist/infrastructure/storage-inventory.types.d.ts +1 -1
  220. package/dist/infrastructure/storage-inventory.types.js.map +1 -1
  221. package/dist/infrastructure/transport-task-cache.constants.d.ts +2 -0
  222. package/dist/infrastructure/transport-task-cache.constants.js +3 -0
  223. package/dist/infrastructure/transport-task-cache.constants.js.map +1 -0
  224. package/dist/infrastructure/transport-task-cache.d.ts +3 -0
  225. package/dist/infrastructure/transport-task-cache.js +56 -0
  226. package/dist/infrastructure/transport-task-cache.js.map +1 -0
  227. package/dist/infrastructure/transport-task-cache.types.d.ts +4 -0
  228. package/dist/infrastructure/transport-task-cache.types.js +2 -0
  229. package/dist/infrastructure/transport-task-cache.types.js.map +1 -0
  230. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"remote-workspace.js","sourceRoot":"","sources":["../../src/application/remote-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { observedOperation } from \"../domain/observed-operation.ts\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rm } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport type { WorkspaceRecord } from \"../domain/workspace.types.ts\";\nimport { seedHistory } from \"./remote-history.ts\";\nimport { uploadFiles } from \"./remote-upload.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { gitDefaults } from \"../infrastructure/git/git.constants.ts\";\nimport { applyChanges } from \"./remote-apply.ts\";\nimport { backupHost } from \"./remote-backup.ts\";\nimport { downloadChanges } from \"./remote-download.ts\";\nimport { validateChanges } from \"./remote-validation.ts\";\nimport type {\n RemoteSync,\n RemoteSyncOptions,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { digest, extras } from \"./workspace-fingerprint.ts\";\n\nexport type {\n RemoteSync,\n RemoteSyncOptions,\n} from \"./remote-workspace.types.ts\";\n\nexport async function seedRemote(\n workspace: WorkspaceRecord,\n lease: SandboxLease,\n options: RemoteSyncOptions = {},\n): Promise<RemoteSync> {\n options.signal?.throwIfAborted();\n const recovery = join(\n workspace.repository,\n \".outpost\",\n \"recovery\",\n randomUUID(),\n );\n await mkdir(recovery, { recursive: true });\n const remoteBundle = posix.join(\n lease.root.replaceAll(\"\\\\\", \"/\"),\n \"..\",\n `outpost-${randomUUID()}.bundle`,\n );\n let initializing = true,\n failed = false;\n const run = async (args: readonly string[]) => {\n let result;\n for (let attempt = 0; attempt < 3; attempt++) {\n result = await lease.invoke({\n executable: \"git\",\n arguments: args,\n directory: lease.root,\n retain: gitDefaults.retainBytes,\n ...(initializing && options.signal ? { signal: options.signal } : {}),\n ...(options.limits?.gitMs ? { deadlineMs: options.limits.gitMs } : {}),\n });\n if (!initializing || result.status !== 126) break;\n }\n if (!result)\n throw new OutpostError(\"workspace\", \"Remote Git did not start\");\n if (result.status !== 0)\n throw new OutpostError(\"workspace\", \"Remote Git operation failed\", {\n ...result,\n recovery,\n });\n if (result.stdout.length >= gitDefaults.retainBytes)\n throw new OutpostError(\n \"workspace\",\n \"Remote Git output exceeds synchronization limit\",\n { recovery },\n );\n return result.stdout;\n };\n await run([\"init\"]);\n await run([\"config\", \"core.autocrlf\", \"false\"]);\n await seedHistory(workspace, lease, options, recovery, remoteBundle, run);\n for (const key of [\"user.name\", \"user.email\"] as const) {\n const fallback = key === \"user.name\" ? \"Outpost\" : \"outpost@localhost\";\n const value =\n (\n await git(workspace.repository, [\"config\", \"--get\", key]).catch(\n () => fallback,\n )\n ).trim() || fallback;\n await run([\"config\", key, value]);\n }\n const initialPatch = join(recovery, \"initial.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--binary\",\n \"HEAD\",\n `--output=${initialPatch}`,\n ]);\n const initialIndex = join(recovery, \"initial-index.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--cached\",\n \"--binary\",\n `--output=${initialIndex}`,\n ]);\n const protectedFiles = options.includeUncommitted\n ? []\n : [\n ...(\n await git(workspace.directory, [\"diff\", \"--name-only\", \"-z\", \"HEAD\"])\n )\n .split(\"\\0\")\n .filter(Boolean),\n ...(await extras(workspace.directory)),\n ];\n const originalHead = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n if (options.includeUncommitted && (await readFile(initialPatch)).length) {\n await lease.upload(initialPatch, `${remoteBundle}.patch`);\n await run([\"apply\", \"--binary\", `${remoteBundle}.patch`]);\n }\n await uploadFiles(\n lease,\n workspace.directory,\n options.includeUncommitted ? await extras(workspace.directory) : [],\n { ...(options.signal ? { signal: options.signal } : {}) },\n );\n let synchronized = (await run([\"rev-parse\", \"HEAD\"])).trim();\n initializing = false;\n let expected = await digest(workspace.directory, recovery);\n const context: RemoteWorkspaceContext = {\n transferred: new Map(),\n workspace,\n lease,\n options,\n recovery,\n remoteBundle,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n };\n return {\n async close() {\n if (!failed) await rm(recovery, { recursive: true, force: true });\n },\n async pull() {\n const transfer = join(recovery, randomUUID());\n await mkdir(transfer, { recursive: true });\n try {\n const changes = await observedOperation(\n options.observation,\n \"transfer\",\n \"repository.download\",\n async () => downloadChanges(context, synchronized, transfer),\n );\n await observedOperation(\n options.observation,\n \"recovery\",\n \"changes.validate\",\n async () =>\n validateChanges(context, changes, synchronized, expected, transfer),\n );\n const backup = await observedOperation(\n options.observation,\n \"recovery\",\n \"host.backup\",\n async () => backupHost(context, changes, synchronized, transfer),\n );\n await observedOperation(\n options.observation,\n \"git\",\n \"changes.apply\",\n async () =>\n applyChanges(\n context,\n changes,\n backup,\n synchronized,\n expected,\n transfer,\n ),\n );\n context.transferred?.clear();\n for (const entry of changes.manifest ?? [])\n context.transferred?.set(entry.path, entry);\n const { head } = changes;\n synchronized = head;\n expected = await digest(workspace.directory, recovery);\n await rm(transfer, { recursive: true, force: true });\n } catch (cause) {\n failed = true;\n throw new OutpostError(\n \"workspace\",\n \"Remote changes could not be fully synchronized; recovery files retained\",\n { recovery: transfer, directory: workspace.directory },\n cause,\n );\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"remote-workspace.js","sourceRoot":"","sources":["../../src/application/remote-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import { observedOperation } from \"../domain/observed-operation.ts\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rm } from \"node:fs/promises\";\nimport { join, posix } from \"node:path\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport type { WorkspaceRecord } from \"../domain/workspace.types.ts\";\nimport { seedHistory } from \"./remote-history.ts\";\nimport { uploadFiles } from \"./remote-upload.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { gitDefaults } from \"../infrastructure/git/git.constants.ts\";\nimport { applyChanges } from \"./remote-apply.ts\";\nimport { backupHost } from \"./remote-backup.ts\";\nimport { downloadChanges } from \"./remote-download.ts\";\nimport { validateChanges } from \"./remote-validation.ts\";\nimport type {\n RemoteSync,\n RemoteSyncOptions,\n RemoteWorkspaceContext,\n} from \"./remote-workspace.types.ts\";\nimport { digest, extras } from \"./workspace-fingerprint.ts\";\n\nexport type {\n RemoteSync,\n RemoteSyncOptions,\n} from \"./remote-workspace.types.ts\";\n\nexport async function seedRemote(\n workspace: WorkspaceRecord,\n lease: SandboxLease,\n options: RemoteSyncOptions = {},\n): Promise<RemoteSync> {\n options.signal?.throwIfAborted();\n const recovery = join(\n workspace.repository,\n \".outpost\",\n \"recovery\",\n randomUUID(),\n );\n await mkdir(recovery, { recursive: true });\n // The root's parent may be read-only for the sandbox user; its own .git is writable.\n const remoteBundle = posix.join(\n lease.root.replaceAll(\"\\\\\", \"/\"),\n \".git\",\n `outpost-${randomUUID()}.bundle`,\n );\n let initializing = true,\n failed = false;\n const run = async (args: readonly string[]) => {\n let result;\n for (let attempt = 0; attempt < 3; attempt++) {\n result = await lease.invoke({\n executable: \"git\",\n arguments: args,\n directory: lease.root,\n retain: gitDefaults.retainBytes,\n ...(initializing && options.signal ? { signal: options.signal } : {}),\n ...(options.limits?.gitMs ? { deadlineMs: options.limits.gitMs } : {}),\n });\n if (!initializing || result.status !== 126) break;\n }\n if (!result)\n throw new OutpostError(\"workspace\", \"Remote Git did not start\");\n if (result.status !== 0)\n throw new OutpostError(\"workspace\", \"Remote Git operation failed\", {\n ...result,\n recovery,\n });\n if (result.stdout.length >= gitDefaults.retainBytes)\n throw new OutpostError(\n \"workspace\",\n \"Remote Git output exceeds synchronization limit\",\n { recovery },\n );\n return result.stdout;\n };\n await run([\"init\"]);\n await run([\"config\", \"core.autocrlf\", \"false\"]);\n await seedHistory(workspace, lease, options, recovery, remoteBundle, run);\n for (const key of [\"user.name\", \"user.email\"] as const) {\n const fallback = key === \"user.name\" ? \"Outpost\" : \"outpost@localhost\";\n const value =\n (\n await git(workspace.repository, [\"config\", \"--get\", key]).catch(\n () => fallback,\n )\n ).trim() || fallback;\n await run([\"config\", key, value]);\n }\n const initialPatch = join(recovery, \"initial.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--binary\",\n \"HEAD\",\n `--output=${initialPatch}`,\n ]);\n const initialIndex = join(recovery, \"initial-index.patch\");\n await git(workspace.directory, [\n \"diff\",\n \"--cached\",\n \"--binary\",\n `--output=${initialIndex}`,\n ]);\n const protectedFiles = options.includeUncommitted\n ? []\n : [\n ...(\n await git(workspace.directory, [\"diff\", \"--name-only\", \"-z\", \"HEAD\"])\n )\n .split(\"\\0\")\n .filter(Boolean),\n ...(await extras(workspace.directory)),\n ];\n const originalHead = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n if (options.includeUncommitted && (await readFile(initialPatch)).length) {\n await lease.upload(initialPatch, `${remoteBundle}.patch`);\n await run([\"apply\", \"--binary\", `${remoteBundle}.patch`]);\n }\n await uploadFiles(\n lease,\n workspace.directory,\n options.includeUncommitted ? await extras(workspace.directory) : [],\n { ...(options.signal ? { signal: options.signal } : {}) },\n );\n let synchronized = (await run([\"rev-parse\", \"HEAD\"])).trim();\n initializing = false;\n let expected = await digest(workspace.directory, recovery);\n const context: RemoteWorkspaceContext = {\n transferred: new Map(),\n workspace,\n lease,\n options,\n recovery,\n remoteBundle,\n run,\n protectedFiles,\n originalHead,\n initialPatch,\n initialIndex,\n };\n return {\n async close() {\n if (!failed) await rm(recovery, { recursive: true, force: true });\n },\n async pull() {\n const transfer = join(recovery, randomUUID());\n await mkdir(transfer, { recursive: true });\n try {\n const changes = await observedOperation(\n options.observation,\n \"transfer\",\n \"repository.download\",\n async () => downloadChanges(context, synchronized, transfer),\n );\n await observedOperation(\n options.observation,\n \"recovery\",\n \"changes.validate\",\n async () =>\n validateChanges(context, changes, synchronized, expected, transfer),\n );\n const backup = await observedOperation(\n options.observation,\n \"recovery\",\n \"host.backup\",\n async () => backupHost(context, changes, synchronized, transfer),\n );\n await observedOperation(\n options.observation,\n \"git\",\n \"changes.apply\",\n async () =>\n applyChanges(\n context,\n changes,\n backup,\n synchronized,\n expected,\n transfer,\n ),\n );\n context.transferred?.clear();\n for (const entry of changes.manifest ?? [])\n context.transferred?.set(entry.path, entry);\n const { head } = changes;\n synchronized = head;\n expected = await digest(workspace.directory, recovery);\n await rm(transfer, { recursive: true, force: true });\n } catch (cause) {\n failed = true;\n throw new OutpostError(\n \"workspace\",\n \"Remote changes could not be fully synchronized; recovery files retained\",\n { recovery: transfer, directory: workspace.directory },\n cause,\n );\n }\n },\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { DispatchOptions } from "./execution.types.ts";
2
+ export declare function recordReplayChanges(dispatch: DispatchOptions<unknown>, directory: string, baseline: string, deadlineMs?: number): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { observedOperation } from "../domain/observed-operation.js";
2
+ import { recordWorkspaceCommits } from "../infrastructure/git/replay-commits.js";
3
+ import { notify } from "./observation.js";
4
+ export async function recordReplayChanges(dispatch, directory, baseline, deadlineMs) {
5
+ const logging = dispatch.logging;
6
+ if (!logging || logging === "stdout" || !logging.replayable)
7
+ return;
8
+ let event;
9
+ try {
10
+ event = await observedOperation(dispatch.observation, "git", "commits.record", () => recordWorkspaceCommits(directory, baseline, deadlineMs));
11
+ }
12
+ catch (error) {
13
+ event = {
14
+ kind: "workspace-commits",
15
+ unavailable: `Workspace commits could not be recorded: ${error instanceof Error ? error.message : String(error)}`,
16
+ };
17
+ }
18
+ if ("unavailable" in event)
19
+ notify(dispatch.warn, `Replay recording is incomplete: ${event.unavailable}`);
20
+ dispatch.observation?.emit("git", event);
21
+ }
22
+ //# sourceMappingURL=replay-recording.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-recording.js","sourceRoot":"","sources":["../../src/application/replay-recording.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAEjF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAkC,EAClC,SAAiB,EACjB,QAAgB,EAChB,UAAmB;IAEnB,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IACjC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO;IACpE,IAAI,KAA4B,CAAC;IACjC,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,iBAAiB,CAC7B,QAAQ,CAAC,WAAW,EACpB,KAAK,EACL,gBAAgB,EAChB,GAAG,EAAE,CAAC,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAC9D,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,KAAK,GAAG;YACN,IAAI,EAAE,mBAAmB;YACzB,WAAW,EAAE,4CAA4C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;SAClH,CAAC;IACJ,CAAC;IACD,IAAI,aAAa,IAAI,KAAK;QACxB,MAAM,CACJ,QAAQ,CAAC,IAAI,EACb,mCAAmC,KAAK,CAAC,WAAW,EAAE,CACvD,CAAC;IACJ,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["import { observedOperation } from \"../domain/observed-operation.ts\";\nimport type { WorkspaceCommitsEvent } from \"../domain/replay.types.ts\";\nimport { recordWorkspaceCommits } from \"../infrastructure/git/replay-commits.ts\";\nimport type { DispatchOptions } from \"./execution.types.ts\";\nimport { notify } from \"./observation.ts\";\n\nexport async function recordReplayChanges(\n dispatch: DispatchOptions<unknown>,\n directory: string,\n baseline: string,\n deadlineMs?: number,\n): Promise<void> {\n const logging = dispatch.logging;\n if (!logging || logging === \"stdout\" || !logging.replayable) return;\n let event: WorkspaceCommitsEvent;\n try {\n event = await observedOperation(\n dispatch.observation,\n \"git\",\n \"commits.record\",\n () => recordWorkspaceCommits(directory, baseline, deadlineMs),\n );\n } catch (error) {\n event = {\n kind: \"workspace-commits\",\n unavailable: `Workspace commits could not be recorded: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n if (\"unavailable\" in event)\n notify(\n dispatch.warn,\n `Replay recording is incomplete: ${event.unavailable}`,\n );\n dispatch.observation?.emit(\"git\", event);\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ReplayAgent } from "../domain/replay.types.ts";
2
+ import type { SandboxLease } from "../domain/sandbox.types.ts";
3
+ import type { DispatchOptions, Turn } from "./execution.types.ts";
4
+ export declare function replayTurn(lease: SandboxLease, agent: ReplayAgent, prompt: string, options: DispatchOptions<unknown>, pass: number): Promise<Turn>;
@@ -0,0 +1,47 @@
1
+ import { OutpostError } from "../domain/errors.js";
2
+ import { ReplayDivergence } from "../domain/replay.js";
3
+ import { executionDefaults } from "./execution.constants.js";
4
+ import { notify } from "./observation.js";
5
+ import { replayWorkspace } from "./replay-workspace.js";
6
+ export async function replayTurn(lease, agent, prompt, options, pass) {
7
+ const start = Date.now();
8
+ options.signal?.throwIfAborted();
9
+ const recorded = agent.nextTurn();
10
+ if (!recorded)
11
+ throw new ReplayDivergence({
12
+ kind: "exhausted",
13
+ turn: agent.turns.length + 1,
14
+ });
15
+ const number = agent.turns.length - agent.remainingTurns;
16
+ const diverge = (details, fatal = false) => {
17
+ const divergence = new ReplayDivergence({ ...details, turn: number });
18
+ if (fatal || agent.divergence === "fail")
19
+ throw divergence;
20
+ notify(options.warn, divergence.message);
21
+ };
22
+ if (recorded.prompt !== prompt)
23
+ diverge({ kind: "prompt", expected: recorded.prompt, actual: prompt });
24
+ for (const event of recorded.events) {
25
+ options.signal?.throwIfAborted();
26
+ notify(options.observe, { ...event, pass, at: new Date().toISOString() });
27
+ }
28
+ if (recorded.changes)
29
+ await replayWorkspace(lease, recorded.changes, {
30
+ ...(options.signal ? { signal: options.signal } : {}),
31
+ deadlineMs: options.deadlineMs ?? executionDefaults.deadlineMs,
32
+ diverge,
33
+ });
34
+ options.signal?.throwIfAborted();
35
+ if (recorded.failure)
36
+ throw new OutpostError(recorded.failure.code, recorded.failure.message, {
37
+ replayed: true,
38
+ });
39
+ return {
40
+ text: recorded.text,
41
+ usage: recorded.usage,
42
+ status: 0,
43
+ durationMs: Date.now() - start,
44
+ ...(recorded.conversation ? { conversation: recorded.conversation } : {}),
45
+ };
46
+ }
47
+ //# sourceMappingURL=replay-turn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-turn.js","sourceRoot":"","sources":["../../src/application/replay-turn.ts"],"names":[],"mappings":"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","sourcesContent":["import { OutpostError } from \"../domain/errors.ts\";\nimport { ReplayDivergence } from \"../domain/replay.ts\";\nimport type {\n ReplayAgent,\n ReplayDivergenceDetails,\n} from \"../domain/replay.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport { executionDefaults } from \"./execution.constants.ts\";\nimport type { DispatchOptions, Turn } from \"./execution.types.ts\";\nimport { notify } from \"./observation.ts\";\nimport { replayWorkspace } from \"./replay-workspace.ts\";\n\nexport async function replayTurn(\n lease: SandboxLease,\n agent: ReplayAgent,\n prompt: string,\n options: DispatchOptions<unknown>,\n pass: number,\n): Promise<Turn> {\n const start = Date.now();\n options.signal?.throwIfAborted();\n const recorded = agent.nextTurn();\n if (!recorded)\n throw new ReplayDivergence({\n kind: \"exhausted\",\n turn: agent.turns.length + 1,\n });\n const number = agent.turns.length - agent.remainingTurns;\n const diverge = (\n details: Omit<ReplayDivergenceDetails, \"turn\">,\n fatal = false,\n ): void => {\n const divergence = new ReplayDivergence({ ...details, turn: number });\n if (fatal || agent.divergence === \"fail\") throw divergence;\n notify(options.warn, divergence.message);\n };\n if (recorded.prompt !== prompt)\n diverge({ kind: \"prompt\", expected: recorded.prompt, actual: prompt });\n for (const event of recorded.events) {\n options.signal?.throwIfAborted();\n notify(options.observe, { ...event, pass, at: new Date().toISOString() });\n }\n if (recorded.changes)\n await replayWorkspace(lease, recorded.changes, {\n ...(options.signal ? { signal: options.signal } : {}),\n deadlineMs: options.deadlineMs ?? executionDefaults.deadlineMs,\n diverge,\n });\n options.signal?.throwIfAborted();\n if (recorded.failure)\n throw new OutpostError(recorded.failure.code, recorded.failure.message, {\n replayed: true,\n });\n return {\n text: recorded.text,\n usage: recorded.usage,\n status: 0,\n durationMs: Date.now() - start,\n ...(recorded.conversation ? { conversation: recorded.conversation } : {}),\n };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { WorkspaceCommitsEvent } from "../domain/replay.types.ts";
2
+ import type { SandboxLease } from "../domain/sandbox.types.ts";
3
+ import type { ReplayWorkspaceContext } from "./replay-workspace.types.ts";
4
+ export declare function replayWorkspace(lease: SandboxLease, changes: WorkspaceCommitsEvent, context: ReplayWorkspaceContext): Promise<void>;
@@ -0,0 +1,71 @@
1
+ import { OutpostError } from "../domain/errors.js";
2
+ export async function replayWorkspace(lease, changes, context) {
3
+ if ("unavailable" in changes)
4
+ return context.diverge({
5
+ kind: "unrecorded",
6
+ expected: changes.unavailable,
7
+ });
8
+ const invoke = (args, stdin, variables = {}) => lease.invoke({
9
+ executable: "git",
10
+ arguments: args,
11
+ directory: lease.root,
12
+ ...(stdin === undefined ? {} : { stdin }),
13
+ variables: { ...variables, LC_ALL: "C", GIT_TERMINAL_PROMPT: "0" },
14
+ deadlineMs: context.deadlineMs,
15
+ ...(context.signal ? { signal: context.signal } : {}),
16
+ });
17
+ const output = async (args, stdin, variables) => {
18
+ const result = await invoke(args, stdin, variables);
19
+ if (result.status !== 0)
20
+ throw new OutpostError("process", `git ${args[0]} failed while replaying workspace commits`, { ...result });
21
+ return result.stdout.trim();
22
+ };
23
+ const tree = await output(["rev-parse", "HEAD^{tree}"]);
24
+ if (tree !== changes.baseline.tree)
25
+ context.diverge({
26
+ kind: "baseline",
27
+ expected: changes.baseline.tree,
28
+ actual: tree,
29
+ });
30
+ for (const commit of changes.commits) {
31
+ if (commit.patch) {
32
+ const applied = await invoke(["apply", "--index", "--binary", "--whitespace=nowarn", "-"], commit.patch);
33
+ if (applied.status !== 0)
34
+ context.diverge({
35
+ kind: "tree",
36
+ commit: commit.oid,
37
+ expected: commit.tree,
38
+ actual: applied.stderr.trim(),
39
+ }, true);
40
+ }
41
+ const actual = await output(["write-tree"]);
42
+ if (actual !== commit.tree)
43
+ context.diverge({
44
+ kind: "tree",
45
+ commit: commit.oid,
46
+ expected: commit.tree,
47
+ actual,
48
+ });
49
+ const parent = await output(["rev-parse", "HEAD"]);
50
+ const created = await output(["commit-tree", actual, "-p", parent], commit.message, identity(commit));
51
+ await output([
52
+ "update-ref",
53
+ "-m",
54
+ "outpost replay",
55
+ "HEAD",
56
+ created,
57
+ parent,
58
+ ]);
59
+ }
60
+ }
61
+ function identity(commit) {
62
+ return {
63
+ GIT_AUTHOR_NAME: commit.author.name,
64
+ GIT_AUTHOR_EMAIL: commit.author.email,
65
+ GIT_AUTHOR_DATE: commit.author.date,
66
+ GIT_COMMITTER_NAME: commit.committer.name,
67
+ GIT_COMMITTER_EMAIL: commit.committer.email,
68
+ GIT_COMMITTER_DATE: commit.committer.date,
69
+ };
70
+ }
71
+ //# sourceMappingURL=replay-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-workspace.js","sourceRoot":"","sources":["../../src/application/replay-workspace.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommandResult, Variables } from \"../domain/command.types.ts\";\nimport { OutpostError } from \"../domain/errors.ts\";\nimport type {\n RecordedCommit,\n WorkspaceCommitsEvent,\n} from \"../domain/replay.types.ts\";\nimport type { SandboxLease } from \"../domain/sandbox.types.ts\";\nimport type { ReplayWorkspaceContext } from \"./replay-workspace.types.ts\";\n\nexport async function replayWorkspace(\n lease: SandboxLease,\n changes: WorkspaceCommitsEvent,\n context: ReplayWorkspaceContext,\n): Promise<void> {\n if (\"unavailable\" in changes)\n return context.diverge({\n kind: \"unrecorded\",\n expected: changes.unavailable,\n });\n const invoke = (\n args: readonly string[],\n stdin?: string,\n variables: Variables = {},\n ): Promise<CommandResult> =>\n lease.invoke({\n executable: \"git\",\n arguments: args,\n directory: lease.root,\n ...(stdin === undefined ? {} : { stdin }),\n variables: { ...variables, LC_ALL: \"C\", GIT_TERMINAL_PROMPT: \"0\" },\n deadlineMs: context.deadlineMs,\n ...(context.signal ? { signal: context.signal } : {}),\n });\n const output = async (\n args: readonly string[],\n stdin?: string,\n variables?: Variables,\n ): Promise<string> => {\n const result = await invoke(args, stdin, variables);\n if (result.status !== 0)\n throw new OutpostError(\n \"process\",\n `git ${args[0]} failed while replaying workspace commits`,\n { ...result },\n );\n return result.stdout.trim();\n };\n const tree = await output([\"rev-parse\", \"HEAD^{tree}\"]);\n if (tree !== changes.baseline.tree)\n context.diverge({\n kind: \"baseline\",\n expected: changes.baseline.tree,\n actual: tree,\n });\n for (const commit of changes.commits) {\n if (commit.patch) {\n const applied = await invoke(\n [\"apply\", \"--index\", \"--binary\", \"--whitespace=nowarn\", \"-\"],\n commit.patch,\n );\n if (applied.status !== 0)\n context.diverge(\n {\n kind: \"tree\",\n commit: commit.oid,\n expected: commit.tree,\n actual: applied.stderr.trim(),\n },\n true,\n );\n }\n const actual = await output([\"write-tree\"]);\n if (actual !== commit.tree)\n context.diverge({\n kind: \"tree\",\n commit: commit.oid,\n expected: commit.tree,\n actual,\n });\n const parent = await output([\"rev-parse\", \"HEAD\"]);\n const created = await output(\n [\"commit-tree\", actual, \"-p\", parent],\n commit.message,\n identity(commit),\n );\n await output([\n \"update-ref\",\n \"-m\",\n \"outpost replay\",\n \"HEAD\",\n created,\n parent,\n ]);\n }\n}\n\nfunction identity(commit: RecordedCommit): Variables {\n return {\n GIT_AUTHOR_NAME: commit.author.name,\n GIT_AUTHOR_EMAIL: commit.author.email,\n GIT_AUTHOR_DATE: commit.author.date,\n GIT_COMMITTER_NAME: commit.committer.name,\n GIT_COMMITTER_EMAIL: commit.committer.email,\n GIT_COMMITTER_DATE: commit.committer.date,\n };\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { ReplayDivergenceDetails } from "../domain/replay.types.ts";
2
+ export interface ReplayWorkspaceContext {
3
+ readonly signal?: AbortSignal;
4
+ readonly deadlineMs: number;
5
+ diverge(details: Omit<ReplayDivergenceDetails, "turn">, fatal?: boolean): void;
6
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=replay-workspace.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-workspace.types.js","sourceRoot":"","sources":["../../src/application/replay-workspace.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ReplayDivergenceDetails } from \"../domain/replay.types.ts\";\n\nexport interface ReplayWorkspaceContext {\n readonly signal?: AbortSignal;\n readonly deadlineMs: number;\n diverge(\n details: Omit<ReplayDivergenceDetails, \"turn\">,\n fatal?: boolean,\n ): void;\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function repositoryFingerprint(repository: string): Promise<string>;
@@ -0,0 +1,18 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { directory } from "../infrastructure/files.js";
5
+ import { git } from "../infrastructure/git/command.js";
6
+ import { digest } from "./workspace-fingerprint.js";
7
+ export async function repositoryFingerprint(repository) {
8
+ const requested = await directory(repository);
9
+ const root = await directory((await git(requested, ["rev-parse", "--show-toplevel"])).trim());
10
+ const scratch = await mkdtemp(join(tmpdir(), "outpost-fingerprint-"));
11
+ try {
12
+ return await digest(root, scratch);
13
+ }
14
+ finally {
15
+ await rm(scratch, { recursive: true, force: true });
16
+ }
17
+ }
18
+ //# sourceMappingURL=repository-fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-fingerprint.js","sourceRoot":"","sources":["../../src/application/repository-fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAkB;IAElB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,SAAS,CAC1B,CAAC,MAAM,GAAG,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAChE,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC","sourcesContent":["import { mkdtemp, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { directory } from \"../infrastructure/files.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { digest } from \"./workspace-fingerprint.ts\";\n\nexport async function repositoryFingerprint(\n repository: string,\n): Promise<string> {\n const requested = await directory(repository);\n const root = await directory(\n (await git(requested, [\"rev-parse\", \"--show-toplevel\"])).trim(),\n );\n const scratch = await mkdtemp(join(tmpdir(), \"outpost-fingerprint-\"));\n try {\n return await digest(root, scratch);\n } finally {\n await rm(scratch, { recursive: true, force: true });\n }\n}\n"]}
@@ -6,6 +6,7 @@ import { commits } from "../infrastructure/git/history.js";
6
6
  import { storageFor } from "./agent-storage.js";
7
7
  import { warmContinuation } from "./continuation.js";
8
8
  import { preflightDispatch } from "./dispatch-validation.js";
9
+ import { recordReplayChanges } from "./replay-recording.js";
9
10
  import { execute } from "./execution.js";
10
11
  import { notify } from "./observation.js";
11
12
  export async function dispatchInSandbox(context, agents, result, dispatch) {
@@ -84,6 +85,7 @@ export async function dispatchInSandbox(context, agents, result, dispatch) {
84
85
  : cause;
85
86
  }
86
87
  const changes = await observedOperation(dispatch.observation, "git", "commits.collect", async () => commits(workspace.directory, baseline, options.limits?.collectMs));
88
+ await recordReplayChanges(dispatch, workspace.directory, baseline, options.limits?.collectMs);
87
89
  if (failure) {
88
90
  recordRecovery(failure, {
89
91
  branch: workspace.branch,
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox-dispatch.js","sourceRoot":"","sources":["../../src/application/sandbox-dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import { observedOperation } from \"../domain/observed-operation.ts\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ConversationRecord } from \"../domain/conversation.types.ts\";\nimport { invariant, recordRecovery } from \"../domain/errors.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { commits } from \"../infrastructure/git/history.ts\";\nimport { storageFor } from \"./agent-storage.ts\";\nimport { warmContinuation } from \"./continuation.ts\";\nimport { preflightDispatch } from \"./dispatch-validation.ts\";\nimport { execute } from \"./execution.ts\";\nimport type { DispatchOptions, Execution } from \"./execution.types.ts\";\nimport { notify } from \"./observation.ts\";\nimport type { Sandbox, WarmDispatchResult } from \"./outpost.types.ts\";\nimport type {\n ProvisionedSandbox,\n SandboxAgents,\n} from \"./sandbox-session.types.ts\";\n\nexport async function dispatchInSandbox<T>(\n context: ProvisionedSandbox,\n agents: SandboxAgents,\n result: Sandbox,\n dispatch: DispatchOptions<T>,\n): Promise<WarmDispatchResult<T>> {\n const { options, sandboxProvider, workspace, sync, stop, staging } = context;\n const { selectAgent, restore } = agents;\n await preflightDispatch(\n dispatch,\n workspace.repository,\n dispatch.agent ?? options.agent,\n );\n const signal = dispatch.signal\n ? AbortSignal.any([dispatch.signal, stop.signal])\n : stop.signal;\n const { selected, adapter, executionLease } = await observedOperation(\n dispatch.observation,\n \"sandbox\",\n \"agent.prepare\",\n async () =>\n selectAgent(\n dispatch.agent ?? options.agent,\n signal,\n dispatch.observation,\n ),\n );\n if (dispatch.continuation)\n await observedOperation(\n dispatch.observation,\n \"conversation\",\n \"conversation.restore\",\n async () => restore(dispatch.continuation!.id, selected, executionLease),\n );\n const baseline = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n let execution: Execution<T> | undefined,\n transcript: ConversationRecord | undefined;\n const captured = new Map<string, ConversationRecord>();\n const storage = storageFor(selected);\n let failure: unknown;\n let conversation = dispatch.continuation?.id;\n const conversations = new Set<string>(\n conversation && !dispatch.continuation?.fork ? [conversation] : [],\n );\n const save = async (id: string) => {\n invariant(storage, \"Conversation storage is unavailable\");\n const location = await observedOperation(\n dispatch.observation,\n \"conversation\",\n \"conversation.capture\",\n async () =>\n storage.capture(id, {\n repository: workspace.repository,\n sandbox: executionLease,\n staging,\n ...(options.conversationHome\n ? { home: options.conversationHome }\n : {}),\n ...(dispatch.warn ? { warn: dispatch.warn } : {}),\n local: sandboxProvider.placement === \"host\",\n }),\n );\n captured.set(id, location);\n transcript = location;\n return location;\n };\n try {\n execution = await execute(\n workspace,\n executionLease,\n adapter,\n sandboxProvider.placement === \"host\",\n {\n ...dispatch,\n signal,\n observe(event) {\n if (event.kind === \"conversation\") {\n conversation = event.id;\n conversations.add(event.id);\n agents.remember(selected, event.id);\n }\n notify(dispatch.observe, event);\n },\n },\n async (turn) => {\n if (!turn.conversation || !storage || selected.capture === false)\n return turn;\n const location = await save(turn.conversation);\n const usage = selected.transcriptUsage?.(\n await readFile(location.file, \"utf8\"),\n );\n return {\n ...turn,\n transcript: location.file,\n ...(location.reference\n ? { transcriptReference: location.reference }\n : {}),\n ...(usage ? { usage } : {}),\n };\n },\n );\n } catch (cause) {\n failure = cause;\n }\n try {\n await observedOperation(\n dispatch.observation,\n \"transfer\",\n \"repository.refresh\",\n async () => sync?.pull(),\n );\n if (storage && selected.capture !== false)\n for (const id of conversations)\n if (failure || !captured.has(id)) await save(id);\n } catch (cause) {\n failure = failure\n ? new AggregateError(\n [failure, cause],\n \"Execution and recovery both failed\",\n )\n : cause;\n }\n const changes = await observedOperation(\n dispatch.observation,\n \"git\",\n \"commits.collect\",\n async () =>\n commits(workspace.directory, baseline, options.limits?.collectMs),\n );\n if (failure) {\n recordRecovery(failure, {\n branch: workspace.branch,\n directory: workspace.directory,\n commits: changes,\n transcript: transcript?.file,\n transcriptReference: transcript?.reference,\n });\n throw failure;\n }\n invariant(execution, \"Execution did not produce a result\");\n return {\n ...execution,\n branch: workspace.branch,\n directory: workspace.directory,\n commits: changes,\n ...(transcript ? { transcript: transcript.file } : {}),\n ...(transcript?.reference\n ? { transcriptReference: transcript.reference }\n : {}),\n resume<U>(next: DispatchOptions<U>) {\n warmContinuation(next);\n invariant(conversation, \"No conversation was emitted\");\n return result.resume(conversation, { agent: selected, ...next });\n },\n fork<U>(next: DispatchOptions<U>) {\n warmContinuation(next);\n invariant(conversation, \"No conversation was emitted\");\n return result.fork(conversation, { agent: selected, ...next });\n },\n };\n}\n"]}
1
+ {"version":3,"file":"sandbox-dispatch.js","sourceRoot":"","sources":["../../src/application/sandbox-dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import { observedOperation } from \"../domain/observed-operation.ts\";\nimport { readFile } from \"node:fs/promises\";\nimport type { ConversationRecord } from \"../domain/conversation.types.ts\";\nimport { invariant, recordRecovery } from \"../domain/errors.ts\";\nimport { git } from \"../infrastructure/git/command.ts\";\nimport { commits } from \"../infrastructure/git/history.ts\";\nimport { storageFor } from \"./agent-storage.ts\";\nimport { warmContinuation } from \"./continuation.ts\";\nimport { preflightDispatch } from \"./dispatch-validation.ts\";\nimport { recordReplayChanges } from \"./replay-recording.ts\";\nimport { execute } from \"./execution.ts\";\nimport type { DispatchOptions, Execution } from \"./execution.types.ts\";\nimport { notify } from \"./observation.ts\";\nimport type { Sandbox, WarmDispatchResult } from \"./outpost.types.ts\";\nimport type {\n ProvisionedSandbox,\n SandboxAgents,\n} from \"./sandbox-session.types.ts\";\n\nexport async function dispatchInSandbox<T>(\n context: ProvisionedSandbox,\n agents: SandboxAgents,\n result: Sandbox,\n dispatch: DispatchOptions<T>,\n): Promise<WarmDispatchResult<T>> {\n const { options, sandboxProvider, workspace, sync, stop, staging } = context;\n const { selectAgent, restore } = agents;\n await preflightDispatch(\n dispatch,\n workspace.repository,\n dispatch.agent ?? options.agent,\n );\n const signal = dispatch.signal\n ? AbortSignal.any([dispatch.signal, stop.signal])\n : stop.signal;\n const { selected, adapter, executionLease } = await observedOperation(\n dispatch.observation,\n \"sandbox\",\n \"agent.prepare\",\n async () =>\n selectAgent(\n dispatch.agent ?? options.agent,\n signal,\n dispatch.observation,\n ),\n );\n if (dispatch.continuation)\n await observedOperation(\n dispatch.observation,\n \"conversation\",\n \"conversation.restore\",\n async () => restore(dispatch.continuation!.id, selected, executionLease),\n );\n const baseline = (\n await git(workspace.directory, [\"rev-parse\", \"HEAD\"])\n ).trim();\n let execution: Execution<T> | undefined,\n transcript: ConversationRecord | undefined;\n const captured = new Map<string, ConversationRecord>();\n const storage = storageFor(selected);\n let failure: unknown;\n let conversation = dispatch.continuation?.id;\n const conversations = new Set<string>(\n conversation && !dispatch.continuation?.fork ? [conversation] : [],\n );\n const save = async (id: string) => {\n invariant(storage, \"Conversation storage is unavailable\");\n const location = await observedOperation(\n dispatch.observation,\n \"conversation\",\n \"conversation.capture\",\n async () =>\n storage.capture(id, {\n repository: workspace.repository,\n sandbox: executionLease,\n staging,\n ...(options.conversationHome\n ? { home: options.conversationHome }\n : {}),\n ...(dispatch.warn ? { warn: dispatch.warn } : {}),\n local: sandboxProvider.placement === \"host\",\n }),\n );\n captured.set(id, location);\n transcript = location;\n return location;\n };\n try {\n execution = await execute(\n workspace,\n executionLease,\n adapter,\n sandboxProvider.placement === \"host\",\n {\n ...dispatch,\n signal,\n observe(event) {\n if (event.kind === \"conversation\") {\n conversation = event.id;\n conversations.add(event.id);\n agents.remember(selected, event.id);\n }\n notify(dispatch.observe, event);\n },\n },\n async (turn) => {\n if (!turn.conversation || !storage || selected.capture === false)\n return turn;\n const location = await save(turn.conversation);\n const usage = selected.transcriptUsage?.(\n await readFile(location.file, \"utf8\"),\n );\n return {\n ...turn,\n transcript: location.file,\n ...(location.reference\n ? { transcriptReference: location.reference }\n : {}),\n ...(usage ? { usage } : {}),\n };\n },\n );\n } catch (cause) {\n failure = cause;\n }\n try {\n await observedOperation(\n dispatch.observation,\n \"transfer\",\n \"repository.refresh\",\n async () => sync?.pull(),\n );\n if (storage && selected.capture !== false)\n for (const id of conversations)\n if (failure || !captured.has(id)) await save(id);\n } catch (cause) {\n failure = failure\n ? new AggregateError(\n [failure, cause],\n \"Execution and recovery both failed\",\n )\n : cause;\n }\n const changes = await observedOperation(\n dispatch.observation,\n \"git\",\n \"commits.collect\",\n async () =>\n commits(workspace.directory, baseline, options.limits?.collectMs),\n );\n await recordReplayChanges(\n dispatch,\n workspace.directory,\n baseline,\n options.limits?.collectMs,\n );\n if (failure) {\n recordRecovery(failure, {\n branch: workspace.branch,\n directory: workspace.directory,\n commits: changes,\n transcript: transcript?.file,\n transcriptReference: transcript?.reference,\n });\n throw failure;\n }\n invariant(execution, \"Execution did not produce a result\");\n return {\n ...execution,\n branch: workspace.branch,\n directory: workspace.directory,\n commits: changes,\n ...(transcript ? { transcript: transcript.file } : {}),\n ...(transcript?.reference\n ? { transcriptReference: transcript.reference }\n : {}),\n resume<U>(next: DispatchOptions<U>) {\n warmContinuation(next);\n invariant(conversation, \"No conversation was emitted\");\n return result.resume(conversation, { agent: selected, ...next });\n },\n fork<U>(next: DispatchOptions<U>) {\n warmContinuation(next);\n invariant(conversation, \"No conversation was emitted\");\n return result.fork(conversation, { agent: selected, ...next });\n },\n };\n}\n"]}
@@ -131,7 +131,7 @@ export function validateSpeculation(value, identity, keys) {
131
131
  !usage(value.usage.tokens))
132
132
  throw invalid();
133
133
  if (value.status !== undefined &&
134
- !["winner", "no-winner", "aborted", "budget-exhausted"].includes(String(value.status)))
134
+ !["winner", "no-winner", "quota", "aborted", "budget-exhausted"].includes(String(value.status)))
135
135
  throw invalid();
136
136
  if (value.error !== undefined && typeof value.error !== "string")
137
137
  throw invalid();
@@ -163,8 +163,25 @@ export function validateSpeculation(value, identity, keys) {
163
163
  if (!object(record) ||
164
164
  record.key !== item.key ||
165
165
  record.branch !== item.branch ||
166
- !["winner", "rejected", "failed", "cancelled", "skipped"].includes(String(record.status)))
166
+ ![
167
+ "winner",
168
+ "rejected",
169
+ "failed",
170
+ "quota",
171
+ "cancelled",
172
+ "skipped",
173
+ ].includes(String(record.status)) ||
174
+ (record.status === "quota") !== (record.quota !== undefined))
167
175
  throw invalid();
176
+ if (record.quota !== undefined) {
177
+ const quota = record.quota;
178
+ if (!object(quota) ||
179
+ typeof quota.message !== "string" ||
180
+ (quota.resetAt !== undefined &&
181
+ (typeof quota.resetAt !== "string" ||
182
+ !Number.isFinite(Date.parse(quota.resetAt)))))
183
+ throw invalid();
184
+ }
168
185
  if (record.status === "winner") {
169
186
  if (item.phase !== "settled" ||
170
187
  item.accepted !== true ||
@@ -1 +1 @@
1
- {"version":3,"file":"speculation-checkpoint.js","sourceRoot":"","sources":["../../src/application/speculation-checkpoint.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { realpath } from \"node:fs/promises\";\nimport { checkpointValue } from \"../domain/workflow/checkpoint-value.ts\";\nimport type { WorkflowJson } from \"../domain/workflow/checkpoint.types.ts\";\nimport type { SpeculationOptions } from \"./speculation.types.ts\";\nimport type { SpeculationCheckpoint } from \"./speculation-checkpoint.types.ts\";\n\nexport async function speculationIdentity<T>(\n options: SpeculationOptions<T>,\n): Promise<string> {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n repository: await realpath(options.repository),\n version: options.durability?.version,\n provider: options.sandboxProvider.name,\n placement: options.sandboxProvider.placement,\n candidates: options.candidates.map(({ key, request }) => ({\n key,\n brief: request.brief,\n response: request.response?.tag,\n })),\n budget: options.budget,\n }),\n )\n .digest(\"hex\");\n}\nfunction diagnostic(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"Unprintable speculation error\";\n }\n}\n\nexport function encodeSpeculation<T>(\n state: SpeculationCheckpoint<T>,\n): WorkflowJson {\n for (const attempt of state.attempts)\n if (attempt.record?.result) checkpointValue(attempt.record.result.value);\n const encoded: unknown = JSON.parse(\n JSON.stringify({\n ...state,\n attempts: state.attempts.map((attempt) => ({\n ...attempt,\n ...(attempt.record\n ? {\n record: {\n ...attempt.record,\n ...(attempt.record.error !== undefined\n ? { error: diagnostic(attempt.record.error) }\n : {}),\n ...(attempt.record.result\n ? {\n result: {\n ...attempt.record.result,\n ...(attempt.record.result.observerErrors\n ? {\n observerErrors:\n attempt.record.result.observerErrors.map(\n diagnostic,\n ),\n }\n : {}),\n },\n }\n : {}),\n },\n }\n : {}),\n })),\n }),\n );\n const value = checkpointValue(encoded);\n if (value.kind !== \"json\") throw new Error(\"Missing speculation state\");\n return value.value;\n}\nfunction object(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction integer(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\nfunction usage(value: unknown): boolean {\n return (\n object(value) &&\n [\"input\", \"cached\", \"output\"].every((key) => integer(value[key])) &&\n Object.entries(value).every(([key, count]) =>\n key === \"complete\" ? typeof count === \"boolean\" : integer(count),\n )\n );\n}\nfunction output(value: unknown): boolean {\n if (\n !object(value) ||\n typeof value.text !== \"string\" ||\n typeof value.completed !== \"boolean\" ||\n !usage(value.usage) ||\n typeof value.branch !== \"string\" ||\n typeof value.directory !== \"string\" ||\n !Array.isArray(value.turns) ||\n !Array.isArray(value.commits)\n )\n return false;\n if (\n !value.turns.every(\n (turn) =>\n object(turn) &&\n typeof turn.text === \"string\" &&\n integer(turn.status) &&\n typeof turn.durationMs === \"number\" &&\n usage(turn.usage),\n )\n )\n return false;\n if (\n !value.commits.every(\n (commit) =>\n object(commit) &&\n typeof commit.oid === \"string\" &&\n typeof commit.subject === \"string\",\n )\n )\n return false;\n for (const key of [\n \"conversation\",\n \"completion\",\n \"transcript\",\n \"retainedDirectory\",\n ])\n if (value[key] !== undefined && typeof value[key] !== \"string\")\n return false;\n for (const key of [\"logReference\", \"transcriptReference\"]) {\n const reference = value[key];\n if (\n reference !== undefined &&\n (!object(reference) ||\n typeof reference.key !== \"string\" ||\n typeof reference.revision !== \"string\")\n )\n return false;\n }\n return true;\n}\nexport function validateSpeculation<T>(\n value: unknown,\n identity: string,\n keys: readonly string[],\n): asserts value is SpeculationCheckpoint<T> {\n const invalid = () =>\n new Error(\"Invalid or incompatible speculation checkpoint\");\n if (\n !object(value) ||\n value.format !== 1 ||\n value.identity !== identity ||\n typeof value.id !== \"string\" ||\n !/^[a-f0-9-]{36}$/.test(value.id) ||\n typeof value.finished !== \"boolean\" ||\n !object(value.before) ||\n typeof value.before.head !== \"string\" ||\n !/^[a-f0-9]{40,64}$/.test(value.before.head) ||\n typeof value.before.branch !== \"string\" ||\n typeof value.before.fingerprint !== \"string\" ||\n typeof value.before.dirty !== \"boolean\" ||\n !Array.isArray(value.attempts) ||\n !object(value.usage) ||\n !integer(value.usage.attempts) ||\n !usage(value.usage.tokens)\n )\n throw invalid();\n if (\n value.status !== undefined &&\n ![\"winner\", \"no-winner\", \"aborted\", \"budget-exhausted\"].includes(\n String(value.status),\n )\n )\n throw invalid();\n if (value.error !== undefined && typeof value.error !== \"string\")\n throw invalid();\n const attempts = new Map<string, number>();\n let admitted = 0,\n winners = 0;\n for (const item of value.attempts) {\n if (\n !object(item) ||\n typeof item.key !== \"string\" ||\n !keys.includes(item.key) ||\n !integer(item.attempt) ||\n item.attempt !== (attempts.get(item.key) ?? 0) + 1 ||\n item.branch !==\n `outpost/speculation/${value.id}/${item.key}/${item.attempt}` ||\n ![\"waiting\", \"running\", \"validated\", \"settled\"].includes(\n String(item.phase),\n ) ||\n ![\"pending\", \"done\"].includes(String(item.cleanup))\n )\n throw invalid();\n attempts.set(item.key, item.attempt);\n if (item.phase !== \"waiting\") admitted++;\n for (const key of [\"resourceId\", \"directory\"])\n if (item[key] !== undefined && typeof item[key] !== \"string\")\n throw invalid();\n if (item.accepted !== undefined && typeof item.accepted !== \"boolean\")\n throw invalid();\n if (item.phase === \"validated\" && typeof item.accepted !== \"boolean\")\n throw invalid();\n const record = item.record;\n if (record !== undefined) {\n if (\n !object(record) ||\n record.key !== item.key ||\n record.branch !== item.branch ||\n ![\"winner\", \"rejected\", \"failed\", \"cancelled\", \"skipped\"].includes(\n String(record.status),\n )\n )\n throw invalid();\n if (record.status === \"winner\") {\n if (\n item.phase !== \"settled\" ||\n item.accepted !== true ||\n !record.result ||\n typeof record.commit !== \"string\" ||\n !/^[a-f0-9]{40,64}$/.test(record.commit)\n )\n throw invalid();\n winners++;\n }\n if (record.attempt !== undefined && record.attempt !== item.attempt)\n throw invalid();\n if (record.cleanup !== undefined && record.cleanup !== item.cleanup)\n throw invalid();\n if (record.result !== undefined && !output(record.result))\n throw invalid();\n for (const key of [\n \"directory\",\n \"retainedDirectory\",\n \"error\",\n \"resourceId\",\n \"commit\",\n ])\n if (record[key] !== undefined && typeof record[key] !== \"string\")\n throw invalid();\n if (\n record.result &&\n object(record.result) &&\n !Object.hasOwn(record.result, \"value\")\n )\n record.result.value = undefined;\n }\n if (item.phase === \"settled\" && !record) throw invalid();\n }\n if (\n attempts.size !== keys.length ||\n admitted !== value.usage.attempts ||\n winners > 1 ||\n (value.finished && (value.status === \"winner\") !== (winners === 1))\n )\n throw invalid();\n}\n"]}
1
+ {"version":3,"file":"speculation-checkpoint.js","sourceRoot":"","sources":["../../src/application/speculation-checkpoint.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { realpath } from \"node:fs/promises\";\nimport { checkpointValue } from \"../domain/workflow/checkpoint-value.ts\";\nimport type { WorkflowJson } from \"../domain/workflow/checkpoint.types.ts\";\nimport type { SpeculationOptions } from \"./speculation.types.ts\";\nimport type { SpeculationCheckpoint } from \"./speculation-checkpoint.types.ts\";\n\nexport async function speculationIdentity<T>(\n options: SpeculationOptions<T>,\n): Promise<string> {\n return createHash(\"sha256\")\n .update(\n JSON.stringify({\n repository: await realpath(options.repository),\n version: options.durability?.version,\n provider: options.sandboxProvider.name,\n placement: options.sandboxProvider.placement,\n candidates: options.candidates.map(({ key, request }) => ({\n key,\n brief: request.brief,\n response: request.response?.tag,\n })),\n budget: options.budget,\n }),\n )\n .digest(\"hex\");\n}\nfunction diagnostic(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"Unprintable speculation error\";\n }\n}\n\nexport function encodeSpeculation<T>(\n state: SpeculationCheckpoint<T>,\n): WorkflowJson {\n for (const attempt of state.attempts)\n if (attempt.record?.result) checkpointValue(attempt.record.result.value);\n const encoded: unknown = JSON.parse(\n JSON.stringify({\n ...state,\n attempts: state.attempts.map((attempt) => ({\n ...attempt,\n ...(attempt.record\n ? {\n record: {\n ...attempt.record,\n ...(attempt.record.error !== undefined\n ? { error: diagnostic(attempt.record.error) }\n : {}),\n ...(attempt.record.result\n ? {\n result: {\n ...attempt.record.result,\n ...(attempt.record.result.observerErrors\n ? {\n observerErrors:\n attempt.record.result.observerErrors.map(\n diagnostic,\n ),\n }\n : {}),\n },\n }\n : {}),\n },\n }\n : {}),\n })),\n }),\n );\n const value = checkpointValue(encoded);\n if (value.kind !== \"json\") throw new Error(\"Missing speculation state\");\n return value.value;\n}\nfunction object(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\nfunction integer(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\nfunction usage(value: unknown): boolean {\n return (\n object(value) &&\n [\"input\", \"cached\", \"output\"].every((key) => integer(value[key])) &&\n Object.entries(value).every(([key, count]) =>\n key === \"complete\" ? typeof count === \"boolean\" : integer(count),\n )\n );\n}\nfunction output(value: unknown): boolean {\n if (\n !object(value) ||\n typeof value.text !== \"string\" ||\n typeof value.completed !== \"boolean\" ||\n !usage(value.usage) ||\n typeof value.branch !== \"string\" ||\n typeof value.directory !== \"string\" ||\n !Array.isArray(value.turns) ||\n !Array.isArray(value.commits)\n )\n return false;\n if (\n !value.turns.every(\n (turn) =>\n object(turn) &&\n typeof turn.text === \"string\" &&\n integer(turn.status) &&\n typeof turn.durationMs === \"number\" &&\n usage(turn.usage),\n )\n )\n return false;\n if (\n !value.commits.every(\n (commit) =>\n object(commit) &&\n typeof commit.oid === \"string\" &&\n typeof commit.subject === \"string\",\n )\n )\n return false;\n for (const key of [\n \"conversation\",\n \"completion\",\n \"transcript\",\n \"retainedDirectory\",\n ])\n if (value[key] !== undefined && typeof value[key] !== \"string\")\n return false;\n for (const key of [\"logReference\", \"transcriptReference\"]) {\n const reference = value[key];\n if (\n reference !== undefined &&\n (!object(reference) ||\n typeof reference.key !== \"string\" ||\n typeof reference.revision !== \"string\")\n )\n return false;\n }\n return true;\n}\nexport function validateSpeculation<T>(\n value: unknown,\n identity: string,\n keys: readonly string[],\n): asserts value is SpeculationCheckpoint<T> {\n const invalid = () =>\n new Error(\"Invalid or incompatible speculation checkpoint\");\n if (\n !object(value) ||\n value.format !== 1 ||\n value.identity !== identity ||\n typeof value.id !== \"string\" ||\n !/^[a-f0-9-]{36}$/.test(value.id) ||\n typeof value.finished !== \"boolean\" ||\n !object(value.before) ||\n typeof value.before.head !== \"string\" ||\n !/^[a-f0-9]{40,64}$/.test(value.before.head) ||\n typeof value.before.branch !== \"string\" ||\n typeof value.before.fingerprint !== \"string\" ||\n typeof value.before.dirty !== \"boolean\" ||\n !Array.isArray(value.attempts) ||\n !object(value.usage) ||\n !integer(value.usage.attempts) ||\n !usage(value.usage.tokens)\n )\n throw invalid();\n if (\n value.status !== undefined &&\n ![\"winner\", \"no-winner\", \"quota\", \"aborted\", \"budget-exhausted\"].includes(\n String(value.status),\n )\n )\n throw invalid();\n if (value.error !== undefined && typeof value.error !== \"string\")\n throw invalid();\n const attempts = new Map<string, number>();\n let admitted = 0,\n winners = 0;\n for (const item of value.attempts) {\n if (\n !object(item) ||\n typeof item.key !== \"string\" ||\n !keys.includes(item.key) ||\n !integer(item.attempt) ||\n item.attempt !== (attempts.get(item.key) ?? 0) + 1 ||\n item.branch !==\n `outpost/speculation/${value.id}/${item.key}/${item.attempt}` ||\n ![\"waiting\", \"running\", \"validated\", \"settled\"].includes(\n String(item.phase),\n ) ||\n ![\"pending\", \"done\"].includes(String(item.cleanup))\n )\n throw invalid();\n attempts.set(item.key, item.attempt);\n if (item.phase !== \"waiting\") admitted++;\n for (const key of [\"resourceId\", \"directory\"])\n if (item[key] !== undefined && typeof item[key] !== \"string\")\n throw invalid();\n if (item.accepted !== undefined && typeof item.accepted !== \"boolean\")\n throw invalid();\n if (item.phase === \"validated\" && typeof item.accepted !== \"boolean\")\n throw invalid();\n const record = item.record;\n if (record !== undefined) {\n if (\n !object(record) ||\n record.key !== item.key ||\n record.branch !== item.branch ||\n ![\n \"winner\",\n \"rejected\",\n \"failed\",\n \"quota\",\n \"cancelled\",\n \"skipped\",\n ].includes(String(record.status)) ||\n (record.status === \"quota\") !== (record.quota !== undefined)\n )\n throw invalid();\n if (record.quota !== undefined) {\n const quota = record.quota;\n if (\n !object(quota) ||\n typeof quota.message !== \"string\" ||\n (quota.resetAt !== undefined &&\n (typeof quota.resetAt !== \"string\" ||\n !Number.isFinite(Date.parse(quota.resetAt))))\n )\n throw invalid();\n }\n if (record.status === \"winner\") {\n if (\n item.phase !== \"settled\" ||\n item.accepted !== true ||\n !record.result ||\n typeof record.commit !== \"string\" ||\n !/^[a-f0-9]{40,64}$/.test(record.commit)\n )\n throw invalid();\n winners++;\n }\n if (record.attempt !== undefined && record.attempt !== item.attempt)\n throw invalid();\n if (record.cleanup !== undefined && record.cleanup !== item.cleanup)\n throw invalid();\n if (record.result !== undefined && !output(record.result))\n throw invalid();\n for (const key of [\n \"directory\",\n \"retainedDirectory\",\n \"error\",\n \"resourceId\",\n \"commit\",\n ])\n if (record[key] !== undefined && typeof record[key] !== \"string\")\n throw invalid();\n if (\n record.result &&\n object(record.result) &&\n !Object.hasOwn(record.result, \"value\")\n )\n record.result.value = undefined;\n }\n if (item.phase === \"settled\" && !record) throw invalid();\n }\n if (\n attempts.size !== keys.length ||\n admitted !== value.usage.attempts ||\n winners > 1 ||\n (value.finished && (value.status === \"winner\") !== (winners === 1))\n )\n throw invalid();\n}\n"]}
@@ -19,6 +19,6 @@ export interface SpeculationCheckpoint<T> {
19
19
  attempts: SpeculationAttempt<T>[];
20
20
  usage: WorkflowUsage;
21
21
  finished: boolean;
22
- status?: "winner" | "no-winner" | "aborted" | "budget-exhausted";
22
+ status?: "winner" | "no-winner" | "quota" | "aborted" | "budget-exhausted";
23
23
  error?: string;
24
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"speculation-checkpoint.types.js","sourceRoot":"","sources":["../../src/application/speculation-checkpoint.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { WorkflowUsage } from \"../domain/workflow/budget.types.ts\";\nimport type {\n SpeculativeCandidateResult,\n SpeculativeHostSnapshot,\n} from \"./speculation.types.ts\";\n\nexport interface SpeculationAttempt<T> {\n key: string;\n attempt: number;\n branch: string;\n phase: \"waiting\" | \"running\" | \"validated\" | \"settled\";\n resourceId?: string;\n directory?: string;\n cleanup: \"pending\" | \"done\";\n accepted?: boolean;\n record?: SpeculativeCandidateResult<T>;\n}\nexport interface SpeculationCheckpoint<T> {\n format: 1;\n identity: string;\n id: string;\n before: SpeculativeHostSnapshot;\n attempts: SpeculationAttempt<T>[];\n usage: WorkflowUsage;\n finished: boolean;\n status?: \"winner\" | \"no-winner\" | \"aborted\" | \"budget-exhausted\";\n error?: string;\n}\n"]}
1
+ {"version":3,"file":"speculation-checkpoint.types.js","sourceRoot":"","sources":["../../src/application/speculation-checkpoint.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { WorkflowUsage } from \"../domain/workflow/budget.types.ts\";\nimport type {\n SpeculativeCandidateResult,\n SpeculativeHostSnapshot,\n} from \"./speculation.types.ts\";\n\nexport interface SpeculationAttempt<T> {\n key: string;\n attempt: number;\n branch: string;\n phase: \"waiting\" | \"running\" | \"validated\" | \"settled\";\n resourceId?: string;\n directory?: string;\n cleanup: \"pending\" | \"done\";\n accepted?: boolean;\n record?: SpeculativeCandidateResult<T>;\n}\nexport interface SpeculationCheckpoint<T> {\n format: 1;\n identity: string;\n id: string;\n before: SpeculativeHostSnapshot;\n attempts: SpeculationAttempt<T>[];\n usage: WorkflowUsage;\n finished: boolean;\n status?: \"winner\" | \"no-winner\" | \"quota\" | \"aborted\" | \"budget-exhausted\";\n error?: string;\n}\n"]}
@@ -2,6 +2,8 @@ import { invariant } from "../domain/errors.js";
2
2
  import { speculationTimeout } from "./speculation-timeout.js";
3
3
  export async function resumeSpeculation(options, state, accounting, save, cleanupMs) {
4
4
  const latest = new Map(state.attempts.map((attempt) => [attempt.key, attempt]));
5
+ if (state.finished && state.status === "quota")
6
+ reopenQuota(state, latest);
5
7
  if (state.attempts.some((attempt) => attempt.phase !== "waiting" && attempt.phase !== "settled") &&
6
8
  options.durability?.resume !== "retry-incomplete")
7
9
  throw new Error("Interrupted speculation requires resume retry-incomplete to authorize replay");
@@ -68,4 +70,23 @@ export async function resumeSpeculation(options, state, accounting, save, cleanu
68
70
  }
69
71
  return latest;
70
72
  }
73
+ /** Queues a new attempt for every candidate a usage or rate limit stopped. */
74
+ function reopenQuota(state, latest) {
75
+ for (const previous of [...latest.values()]) {
76
+ if (previous.record?.status !== "quota")
77
+ continue;
78
+ const attempt = {
79
+ key: previous.key,
80
+ attempt: previous.attempt + 1,
81
+ branch: `outpost/speculation/${state.id}/${previous.key}/${previous.attempt + 1}`,
82
+ phase: "waiting",
83
+ cleanup: "done",
84
+ };
85
+ state.attempts.push(attempt);
86
+ latest.set(attempt.key, attempt);
87
+ }
88
+ state.finished = false;
89
+ delete state.status;
90
+ delete state.error;
91
+ }
71
92
  //# sourceMappingURL=speculation-resume.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"speculation-resume.js","sourceRoot":"","sources":["../../src/application/speculation-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type { WorkflowAccounting } from \"../domain/workflow/budget.types.ts\";\nimport type {\n SpeculationCheckpoint,\n SpeculationAttempt,\n} from \"./speculation-checkpoint.types.ts\";\nimport type { SpeculationOptions } from \"./speculation.types.ts\";\nimport { speculationTimeout } from \"./speculation-timeout.ts\";\n\nexport async function resumeSpeculation<T>(\n options: SpeculationOptions<T>,\n state: SpeculationCheckpoint<T>,\n accounting: WorkflowAccounting,\n save: () => Promise<void>,\n cleanupMs: number,\n): Promise<Map<string, SpeculationAttempt<T>>> {\n const latest = new Map(\n state.attempts.map((attempt) => [attempt.key, attempt]),\n );\n if (\n state.attempts.some(\n (attempt) => attempt.phase !== \"waiting\" && attempt.phase !== \"settled\",\n ) &&\n options.durability?.resume !== \"retry-incomplete\"\n )\n throw new Error(\n \"Interrupted speculation requires resume retry-incomplete to authorize replay\",\n );\n await save();\n for (const attempt of state.attempts) {\n if (attempt.cleanup === \"done\") continue;\n if (attempt.resourceId) {\n invariant(\n options.sandboxProvider.recover,\n \"Sandbox provider cannot recover this resource\",\n );\n await save();\n await speculationTimeout(\n options.sandboxProvider.recover(attempt.resourceId, {\n signal: AbortSignal.timeout(cleanupMs),\n deadlineMs: cleanupMs,\n }),\n cleanupMs,\n );\n }\n attempt.cleanup = \"done\";\n if (attempt.record) attempt.record = { ...attempt.record, cleanup: \"done\" };\n await save();\n }\n let recoveredWinner = state.attempts.some(\n (attempt) => attempt.record?.status === \"winner\",\n );\n for (const previous of latest.values()) {\n if (previous.phase === \"waiting\" || previous.phase === \"settled\") continue;\n if (previous.phase === \"validated\" && previous.record) {\n previous.phase = \"settled\";\n const accepted = previous.accepted && !recoveredWinner;\n previous.record = {\n ...previous.record,\n status: accepted ? \"winner\" : \"rejected\",\n cleanup: \"done\",\n ...(previous.directory\n ? { retainedDirectory: previous.directory }\n : {}),\n };\n recoveredWinner ||= !!accepted;\n continue;\n }\n accounting.report({ input: 0, cached: 0, output: 0, complete: false });\n previous.phase = \"settled\";\n previous.record = {\n key: previous.key,\n branch: previous.branch,\n attempt: previous.attempt,\n status: \"cancelled\",\n cleanup: \"done\",\n ...(previous.directory\n ? {\n directory: previous.directory,\n retainedDirectory: previous.directory,\n }\n : {}),\n error: \"Interrupted by coordinator failure\",\n };\n const attempt: SpeculationAttempt<T> = {\n key: previous.key,\n attempt: previous.attempt + 1,\n branch: `outpost/speculation/${state.id}/${previous.key}/${previous.attempt + 1}`,\n phase: \"waiting\",\n cleanup: \"done\",\n };\n state.attempts.push(attempt);\n latest.set(attempt.key, attempt);\n }\n return latest;\n}\n"]}
1
+ {"version":3,"file":"speculation-resume.js","sourceRoot":"","sources":["../../src/application/speculation-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from \"../domain/errors.ts\";\nimport type { WorkflowAccounting } from \"../domain/workflow/budget.types.ts\";\nimport type {\n SpeculationCheckpoint,\n SpeculationAttempt,\n} from \"./speculation-checkpoint.types.ts\";\nimport type { SpeculationOptions } from \"./speculation.types.ts\";\nimport { speculationTimeout } from \"./speculation-timeout.ts\";\n\nexport async function resumeSpeculation<T>(\n options: SpeculationOptions<T>,\n state: SpeculationCheckpoint<T>,\n accounting: WorkflowAccounting,\n save: () => Promise<void>,\n cleanupMs: number,\n): Promise<Map<string, SpeculationAttempt<T>>> {\n const latest = new Map(\n state.attempts.map((attempt) => [attempt.key, attempt]),\n );\n if (state.finished && state.status === \"quota\") reopenQuota(state, latest);\n if (\n state.attempts.some(\n (attempt) => attempt.phase !== \"waiting\" && attempt.phase !== \"settled\",\n ) &&\n options.durability?.resume !== \"retry-incomplete\"\n )\n throw new Error(\n \"Interrupted speculation requires resume retry-incomplete to authorize replay\",\n );\n await save();\n for (const attempt of state.attempts) {\n if (attempt.cleanup === \"done\") continue;\n if (attempt.resourceId) {\n invariant(\n options.sandboxProvider.recover,\n \"Sandbox provider cannot recover this resource\",\n );\n await save();\n await speculationTimeout(\n options.sandboxProvider.recover(attempt.resourceId, {\n signal: AbortSignal.timeout(cleanupMs),\n deadlineMs: cleanupMs,\n }),\n cleanupMs,\n );\n }\n attempt.cleanup = \"done\";\n if (attempt.record) attempt.record = { ...attempt.record, cleanup: \"done\" };\n await save();\n }\n let recoveredWinner = state.attempts.some(\n (attempt) => attempt.record?.status === \"winner\",\n );\n for (const previous of latest.values()) {\n if (previous.phase === \"waiting\" || previous.phase === \"settled\") continue;\n if (previous.phase === \"validated\" && previous.record) {\n previous.phase = \"settled\";\n const accepted = previous.accepted && !recoveredWinner;\n previous.record = {\n ...previous.record,\n status: accepted ? \"winner\" : \"rejected\",\n cleanup: \"done\",\n ...(previous.directory\n ? { retainedDirectory: previous.directory }\n : {}),\n };\n recoveredWinner ||= !!accepted;\n continue;\n }\n accounting.report({ input: 0, cached: 0, output: 0, complete: false });\n previous.phase = \"settled\";\n previous.record = {\n key: previous.key,\n branch: previous.branch,\n attempt: previous.attempt,\n status: \"cancelled\",\n cleanup: \"done\",\n ...(previous.directory\n ? {\n directory: previous.directory,\n retainedDirectory: previous.directory,\n }\n : {}),\n error: \"Interrupted by coordinator failure\",\n };\n const attempt: SpeculationAttempt<T> = {\n key: previous.key,\n attempt: previous.attempt + 1,\n branch: `outpost/speculation/${state.id}/${previous.key}/${previous.attempt + 1}`,\n phase: \"waiting\",\n cleanup: \"done\",\n };\n state.attempts.push(attempt);\n latest.set(attempt.key, attempt);\n }\n return latest;\n}\n\n/** Queues a new attempt for every candidate a usage or rate limit stopped. */\nfunction reopenQuota<T>(\n state: SpeculationCheckpoint<T>,\n latest: Map<string, SpeculationAttempt<T>>,\n): void {\n for (const previous of [...latest.values()]) {\n if (previous.record?.status !== \"quota\") continue;\n const attempt: SpeculationAttempt<T> = {\n key: previous.key,\n attempt: previous.attempt + 1,\n branch: `outpost/speculation/${state.id}/${previous.key}/${previous.attempt + 1}`,\n phase: \"waiting\",\n cleanup: \"done\",\n };\n state.attempts.push(attempt);\n latest.set(attempt.key, attempt);\n }\n state.finished = false;\n delete state.status;\n delete state.error;\n}\n"]}