@playcraft/cli 0.1.50-beta → 0.1.52

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 (406) hide show
  1. package/README.md +3 -62
  2. package/dist/agent/local-backend.js +1 -1
  3. package/dist/cli-root-help.js +0 -2
  4. package/dist/commands/build-all.js +19 -5
  5. package/dist/commands/build.js +28 -15
  6. package/dist/commands/create.js +33 -26
  7. package/dist/commands/image.js +6 -2
  8. package/dist/commands/login.js +42 -84
  9. package/dist/commands/skills.js +0 -4
  10. package/dist/commands/tools-3d.js +0 -11
  11. package/dist/commands/tools-generation.js +137 -69
  12. package/dist/commands/tools-image-context.js +29 -0
  13. package/dist/commands/tools-utils.js +12 -0
  14. package/dist/commands/tools.js +2 -2
  15. package/dist/config.js +4 -59
  16. package/dist/index.js +3 -40
  17. package/dist/remix/clone.js +0 -2
  18. package/dist/remix/init-template.js +0 -2
  19. package/dist/remix/pull.js +0 -2
  20. package/dist/remix/push.js +1 -21
  21. package/dist/utils/agent-api-client.js +30 -210
  22. package/dist/utils/image-asset-context.js +228 -0
  23. package/dist/utils/playcraft-http-client.js +4 -6
  24. package/package.json +5 -15
  25. package/project-template/.claude/agents/refs/ta-image-generation-detail.md +67 -123
  26. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  27. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  28. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  29. package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  30. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  31. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  32. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  33. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  34. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  35. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  36. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  37. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  38. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  39. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  40. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  41. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  42. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  43. package/SKILLS_REGISTRY.md +0 -149
  44. package/dist/auth/account-activation.js +0 -157
  45. package/dist/auth/messages.js +0 -30
  46. package/dist/commands/build-temp.js +0 -26
  47. package/dist/commands/platform-skills.generated.js +0 -19
  48. package/dist/commands/playable.js +0 -53
  49. package/dist/commands/tools-batch.js +0 -113
  50. package/dist/commands/video.js +0 -122
  51. package/dist/commands/workspace-runtime.js +0 -54
  52. package/dist/commands/workspace.js +0 -40
  53. package/dist/project-skills/builtin-import.js +0 -225
  54. package/dist/project-skills/commands.js +0 -77
  55. package/dist/project-skills/lifecycle.js +0 -28
  56. package/dist/project-skills/local-run.js +0 -112
  57. package/dist/project-skills/local-store.js +0 -221
  58. package/dist/project-skills/messages.js +0 -80
  59. package/dist/project-skills/reconcile.js +0 -62
  60. package/dist/project-skills/remote-cache.js +0 -51
  61. package/dist/project-skills/validation.js +0 -19
  62. package/dist/recording/messages.js +0 -40
  63. package/dist/recording/project-digest.js +0 -85
  64. package/dist/recording/recorder.js +0 -293
  65. package/dist/recording/scenario.js +0 -50
  66. package/dist/skill-registry/client.js +0 -345
  67. package/dist/skill-registry/commands.js +0 -180
  68. package/dist/skill-registry/evaluation-client.js +0 -156
  69. package/dist/skill-registry/evaluation-commands.js +0 -174
  70. package/dist/skill-registry/messages.js +0 -177
  71. package/dist/skill-registry/package.js +0 -275
  72. package/dist/skill-registry/platform-package.js +0 -100
  73. package/dist/utils/environment-endpoints.js +0 -17
  74. package/dist/utils/error-diagnostics.js +0 -20
  75. package/dist/utils/tool-operation-journal.js +0 -124
  76. package/dist/utils/video-evidence-git.js +0 -81
  77. package/dist/video-replication/client.js +0 -234
  78. package/dist/video-replication/comparison-evidence.js +0 -149
  79. package/dist/video-replication/comparison-files.js +0 -57
  80. package/dist/video-replication/comparison-log.js +0 -72
  81. package/dist/video-replication/comparison-messages.js +0 -104
  82. package/dist/video-replication/comparison-output.js +0 -63
  83. package/dist/video-replication/comparison-preflight.js +0 -66
  84. package/dist/video-replication/comparison.js +0 -230
  85. package/dist/video-replication/diagnostics.js +0 -41
  86. package/dist/video-replication/evidence.js +0 -165
  87. package/dist/video-replication/media.js +0 -112
  88. package/dist/video-replication/messages.js +0 -97
  89. package/dist/workspace/messages.js +0 -67
  90. package/dist/workspace/transfer-client.js +0 -233
  91. package/dist/workspace/transfer-errors.js +0 -48
  92. package/dist/workspace-runtime/batch/content-changes.js +0 -30
  93. package/dist/workspace-runtime/batch/control.js +0 -86
  94. package/dist/workspace-runtime/batch/deliver.js +0 -173
  95. package/dist/workspace-runtime/batch/freeze.js +0 -101
  96. package/dist/workspace-runtime/batch/generate.js +0 -249
  97. package/dist/workspace-runtime/batch/git.js +0 -40
  98. package/dist/workspace-runtime/batch/occupancy.js +0 -8
  99. package/dist/workspace-runtime/batch/policy.js +0 -70
  100. package/dist/workspace-runtime/batch/prepare.js +0 -317
  101. package/dist/workspace-runtime/batch/processes.js +0 -72
  102. package/dist/workspace-runtime/batch/records.js +0 -113
  103. package/dist/workspace-runtime/batch/routes.js +0 -61
  104. package/dist/workspace-runtime/batch/source-candidate.js +0 -86
  105. package/dist/workspace-runtime/batch/source-capture.js +0 -146
  106. package/dist/workspace-runtime/batch/source-failure.js +0 -43
  107. package/dist/workspace-runtime/batch/source-publish.js +0 -130
  108. package/dist/workspace-runtime/codex/app-server.js +0 -1065
  109. package/dist/workspace-runtime/codex/compact.js +0 -46
  110. package/dist/workspace-runtime/codex/config-toml.js +0 -107
  111. package/dist/workspace-runtime/codex/delivery-tools.js +0 -144
  112. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
  113. package/dist/workspace-runtime/codex/launcher-client.js +0 -177
  114. package/dist/workspace-runtime/codex/loopback.js +0 -146
  115. package/dist/workspace-runtime/codex/mcp-config.js +0 -39
  116. package/dist/workspace-runtime/codex/model-broker.js +0 -263
  117. package/dist/workspace-runtime/codex/native-adapter.js +0 -9
  118. package/dist/workspace-runtime/codex/permissions.js +0 -79
  119. package/dist/workspace-runtime/codex/thread-image.js +0 -38
  120. package/dist/workspace-runtime/codex/tool-response.js +0 -10
  121. package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
  122. package/dist/workspace-runtime/codex/ui-tools.js +0 -203
  123. package/dist/workspace-runtime/codex/work-mode.js +0 -15
  124. package/dist/workspace-runtime/main.js +0 -10
  125. package/dist/workspace-runtime/persistence/journal.js +0 -365
  126. package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
  127. package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
  128. package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
  129. package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
  130. package/dist/workspace-runtime/previews/diagnostics.js +0 -44
  131. package/dist/workspace-runtime/previews/output.js +0 -36
  132. package/dist/workspace-runtime/previews/publish.js +0 -283
  133. package/dist/workspace-runtime/previews/runtime-preview.js +0 -177
  134. package/dist/workspace-runtime/previews/single-file.js +0 -43
  135. package/dist/workspace-runtime/processes/managed-writes.js +0 -69
  136. package/dist/workspace-runtime/serve.js +0 -441
  137. package/dist/workspace-runtime/server/accept-operation.js +0 -61
  138. package/dist/workspace-runtime/server/asset-transfers.js +0 -63
  139. package/dist/workspace-runtime/server/auth.js +0 -21
  140. package/dist/workspace-runtime/server/connectivity.js +0 -209
  141. package/dist/workspace-runtime/server/desktop-host.js +0 -429
  142. package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
  143. package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
  144. package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
  145. package/dist/workspace-runtime/server/desktop-preview.js +0 -189
  146. package/dist/workspace-runtime/server/dispatch.js +0 -1632
  147. package/dist/workspace-runtime/server/endpoints.js +0 -21
  148. package/dist/workspace-runtime/server/execution-group.js +0 -90
  149. package/dist/workspace-runtime/server/http.js +0 -1340
  150. package/dist/workspace-runtime/server/lock.js +0 -53
  151. package/dist/workspace-runtime/server/operation-record.js +0 -33
  152. package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
  153. package/dist/workspace-runtime/server/session-restore.js +0 -84
  154. package/dist/workspace-runtime/server/source-uploader.js +0 -138
  155. package/dist/workspace-runtime/server/submit-operation.js +0 -41
  156. package/dist/workspace-runtime/server/terminal-host.js +0 -366
  157. package/dist/workspace-runtime/server/types.js +0 -1
  158. package/dist/workspace-runtime/server/workspace-transfer.js +0 -168
  159. package/dist/workspace-runtime/verify-artifact.js +0 -59
  160. package/dist/workspace-runtime/workspaces/append-only-log.js +0 -62
  161. package/dist/workspace-runtime/workspaces/archive.js +0 -207
  162. package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
  163. package/dist/workspace-runtime/workspaces/artifact-files.js +0 -199
  164. package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
  165. package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
  166. package/dist/workspace-runtime/workspaces/asset-library-import.js +0 -366
  167. package/dist/workspace-runtime/workspaces/asset-transfer-dependencies.js +0 -81
  168. package/dist/workspace-runtime/workspaces/asset-transfer-publish.js +0 -434
  169. package/dist/workspace-runtime/workspaces/asset-transfer-storage.js +0 -389
  170. package/dist/workspace-runtime/workspaces/attachment-content.js +0 -73
  171. package/dist/workspace-runtime/workspaces/attachments.js +0 -185
  172. package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
  173. package/dist/workspace-runtime/workspaces/binary-content.js +0 -65
  174. package/dist/workspace-runtime/workspaces/binary-write.js +0 -112
  175. package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
  176. package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -115
  177. package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
  178. package/dist/workspace-runtime/workspaces/context-error.js +0 -2
  179. package/dist/workspace-runtime/workspaces/delivery-build.js +0 -331
  180. package/dist/workspace-runtime/workspaces/delivery-store.js +0 -168
  181. package/dist/workspace-runtime/workspaces/delivery.js +0 -64
  182. package/dist/workspace-runtime/workspaces/dependencies.js +0 -279
  183. package/dist/workspace-runtime/workspaces/developer-command.js +0 -98
  184. package/dist/workspace-runtime/workspaces/directories.js +0 -132
  185. package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
  186. package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
  187. package/dist/workspace-runtime/workspaces/directory-create.js +0 -57
  188. package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
  189. package/dist/workspace-runtime/workspaces/directory-mutations.js +0 -238
  190. package/dist/workspace-runtime/workspaces/directory-revisions.js +0 -56
  191. package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
  192. package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
  193. package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
  194. package/dist/workspace-runtime/workspaces/file-content.js +0 -48
  195. package/dist/workspace-runtime/workspaces/files-search.js +0 -116
  196. package/dist/workspace-runtime/workspaces/files.js +0 -150
  197. package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
  198. package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
  199. package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
  200. package/dist/workspace-runtime/workspaces/git.js +0 -670
  201. package/dist/workspace-runtime/workspaces/instructions.js +0 -56
  202. package/dist/workspace-runtime/workspaces/json5-edit.js +0 -313
  203. package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
  204. package/dist/workspace-runtime/workspaces/operation-log.js +0 -23
  205. package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
  206. package/dist/workspace-runtime/workspaces/parameter-agent-inputs.js +0 -87
  207. package/dist/workspace-runtime/workspaces/parameter-agent-protocol.js +0 -29
  208. package/dist/workspace-runtime/workspaces/parameter-agent-store.js +0 -58
  209. package/dist/workspace-runtime/workspaces/parameter-agent.js +0 -282
  210. package/dist/workspace-runtime/workspaces/parameters.js +0 -295
  211. package/dist/workspace-runtime/workspaces/prepare.js +0 -278
  212. package/dist/workspace-runtime/workspaces/registry.js +0 -251
  213. package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
  214. package/dist/workspace-runtime/workspaces/revisions.js +0 -31
  215. package/dist/workspace-runtime/workspaces/session-image.js +0 -104
  216. package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
  217. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
  218. package/dist/workspace-runtime/workspaces/skills.js +0 -191
  219. package/dist/workspace-runtime/workspaces/transfer-quota.js +0 -69
  220. package/dist/workspace-runtime/workspaces/transfer.js +0 -471
  221. package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
  222. package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
  223. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
  224. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
  225. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
  226. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
  227. package/project-template-v2/.claude/agents/artist.md +0 -82
  228. package/project-template-v2/.claude/agents/developer.md +0 -153
  229. package/project-template-v2/.claude/agents/game-designer.md +0 -264
  230. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
  231. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
  232. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
  233. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
  234. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
  235. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
  236. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
  237. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
  238. package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
  239. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
  240. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
  241. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
  242. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
  243. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
  244. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
  245. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
  246. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
  247. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
  248. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
  249. package/project-template-v2/.claude/agents/reviewer.md +0 -75
  250. package/project-template-v2/.claude/agents/technical-artist.md +0 -86
  251. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
  252. package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
  253. package/project-template-v2/.claude/settings.json +0 -33
  254. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
  255. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
  256. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
  257. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
  258. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
  259. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
  260. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
  261. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  262. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
  263. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
  264. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
  265. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
  266. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
  267. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
  268. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
  269. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
  270. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
  271. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
  272. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
  273. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
  274. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
  275. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
  276. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
  277. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
  278. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
  279. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
  280. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
  281. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
  282. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
  283. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
  284. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
  285. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
  286. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
  287. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
  288. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
  289. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
  290. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
  291. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
  292. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
  293. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
  294. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
  295. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
  296. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
  297. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
  298. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
  299. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
  300. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
  301. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
  302. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
  303. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
  304. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
  305. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
  306. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
  307. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
  308. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
  309. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
  310. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
  311. package/project-template-v2/.claude/snapshot.mjs +0 -263
  312. package/project-template-v2/.playcraft/skills.lock.json +0 -152
  313. package/project-template-v2/CLAUDE.md +0 -146
  314. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  315. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  316. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  317. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  318. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  319. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  320. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  321. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  322. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  323. package/project-template-v2/assets/models/.gitkeep +0 -0
  324. package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
  325. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
  326. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
  327. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
  328. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
  329. package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
  330. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
  331. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
  332. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
  333. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
  334. package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
  335. package/project-template-v2/docs/project-state.json +0 -60
  336. package/project-template-v2/docs/project-state.md +0 -72
  337. package/project-template-v2/docs/standards/README.md +0 -225
  338. package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
  339. package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
  340. package/project-template-v2/docs/standards/artifacts/design.md +0 -22
  341. package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
  342. package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
  343. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
  344. package/project-template-v2/game/index.ts +0 -18
  345. package/project-template-v2/globals.d.ts +0 -51
  346. package/project-template-v2/index.css +0 -34
  347. package/project-template-v2/index.html +0 -18
  348. package/project-template-v2/main.ts +0 -9
  349. package/project-template-v2/package.json +0 -46
  350. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
  351. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
  352. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
  353. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
  354. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
  355. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
  356. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
  357. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
  358. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
  359. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
  360. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
  361. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
  362. package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
  363. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
  364. package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
  365. package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
  366. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
  367. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
  368. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
  369. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
  370. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
  371. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
  372. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
  373. package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
  374. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
  375. package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
  376. package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
  377. package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
  378. package/project-template-v2/skills/playable-record/SKILL.md +0 -140
  379. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
  380. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
  381. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
  382. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
  383. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
  384. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
  385. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
  386. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
  387. package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
  388. package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
  389. package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
  390. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
  391. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
  392. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
  393. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
  394. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
  395. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
  396. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
  397. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
  398. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
  399. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
  400. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  401. package/project-template-v2/tsconfig.json +0 -20
  402. package/project-template-v2/vite.config.ts +0 -27
  403. package/project-templates/batch-locks/phaser.yaml +0 -922
  404. package/project-templates/batch-locks/threejs.yaml +0 -978
  405. package/project-templates/empty/AGENTS.md +0 -41
  406. package/project-templates/empty/gitignore +0 -33
@@ -1,1161 +0,0 @@
1
- /**
2
- * Dedicated SQLite writer for the CLI platform state file.
3
- * The main thread never opens runtime-state.sqlite.
4
- *
5
- * Binding pin: better-sqlite3@13.0.3 vendors SQLite 3.53.4, which includes the
6
- * official WAL-reset fix (3.51.3). Node's built-in node:sqlite on Node 22.22.1
7
- * reports 3.51.2 and must not be used for this file.
8
- * Do not read Codex JSONL from this worker.
9
- */
10
- import { createHash, randomBytes, randomUUID } from 'node:crypto';
11
- import { chmodSync, mkdirSync } from 'node:fs';
12
- import { dirname } from 'node:path';
13
- import { parentPort } from 'node:worker_threads';
14
- import Database from 'better-sqlite3-pinned';
15
- import { clearPendingNativeFollowups, learnNativeSubagents, nativeEventOwner, nativeSubagentThreads } from './native-subagents.js';
16
- import { settleStoppedExecution, stoppedExecutionEpochs } from './stopped-execution.js';
17
- import { hashOperationPayload, operationRecordSchema, pendingSourceEventSchema, terminalRecordSchema, workspaceRegistrationSchema, transitionOperation, canTransitionTurn, } from '@playcraft/common/cloud-workspace/runtime';
18
- const SCHEMA_VERSION = 1;
19
- const MIN_SQLITE = [3, 51, 3];
20
- const BINDING = 'better-sqlite3@13.0.3';
21
- const MAX_PINS = 32;
22
- const MAX_PIN_MS = 120_000;
23
- let db;
24
- const port = parentPort;
25
- if (!port)
26
- throw new Error('sqlite_worker_without_parent');
27
- port.on('message', (message) => {
28
- try {
29
- const result = dispatch(message.type, message.payload ?? {});
30
- port.postMessage({ id: message.id, ok: true, result });
31
- }
32
- catch (error) {
33
- port.postMessage({
34
- id: message.id,
35
- ok: false,
36
- error: error instanceof Error ? error.message : 'sqlite_worker_failed',
37
- });
38
- }
39
- });
40
- function dispatch(type, payload) {
41
- if (type === 'open')
42
- return openDatabase(asRecord(payload));
43
- if (type === 'close') {
44
- db?.close();
45
- db = undefined;
46
- return { closed: true };
47
- }
48
- const database = requireDb();
49
- const body = asRecord(payload);
50
- const write = database.transaction(() => {
51
- switch (type) {
52
- case 'upsertRecord':
53
- upsertRecord(database, String(body.bucket), String(body.key), body.body);
54
- return;
55
- case 'deleteRecord':
56
- database.prepare('DELETE FROM records WHERE bucket = ? AND record_key = ?').run(body.bucket, body.key);
57
- return;
58
- case 'replaceBucket':
59
- replaceBucket(database, String(body.bucket), Array.isArray(body.entries) ? body.entries : []);
60
- return;
61
- case 'forgetWorkspaces':
62
- database.prepare(`DELETE FROM records WHERE bucket = 'workspaces'`).run();
63
- return;
64
- case 'putOperation':
65
- putOperation(database, String(body.scopeKey), body.operation);
66
- return;
67
- case 'setAcceptedFence':
68
- setMeta(database, 'accepted_lease_fence', String(Math.max(metaNumber(database, 'accepted_lease_fence'), Number(body.value) || 0)));
69
- return;
70
- case 'setDestroyed':
71
- setMeta(database, 'destroyed', '1');
72
- return;
73
- case 'noteExecutionGroup':
74
- setMeta(database, 'execution_group', JSON.stringify({ ...(executionGroup(database)), ...asRecord(body.group) }));
75
- return;
76
- case 'setStopConfirmed':
77
- return confirmExecutionStop(database, body.stopConfirmed === true);
78
- case 'writeExecutionEpoch':
79
- return writeExecutionEpoch(database, body);
80
- case 'bindRuntime':
81
- bindRuntime(database, body);
82
- return;
83
- case 'allocateEvent':
84
- return allocateEvent(database, body);
85
- case 'commitIdentity':
86
- return { eventChanges: commitIdentity(database, body.event).committedEvents };
87
- case 'ingest':
88
- return ingest(database, body);
89
- case 'bindTurn':
90
- return bindTurn(database, body);
91
- case 'ack':
92
- acknowledge(database, body);
93
- return;
94
- case 'createPin':
95
- return createPin(database, body);
96
- case 'deletePin':
97
- database.prepare('DELETE FROM replay_pins WHERE pin_id = ?').run(String(body.pinId));
98
- return { deleted: true };
99
- case 'reclaim':
100
- reclaim(database, Number(body.minRetainMs) || 0);
101
- return;
102
- case 'maintain':
103
- database.pragma('wal_checkpoint(PASSIVE)');
104
- return;
105
- default:
106
- throw new Error(`unknown_sqlite_command:${type}`);
107
- }
108
- });
109
- const extra = write();
110
- return { ...dump(database, type === 'reclaim'), ...(extra && typeof extra === 'object' ? extra : {}) };
111
- }
112
- function openDatabase(payload) {
113
- if (db)
114
- throw new Error('sqlite_already_open');
115
- const filePath = String(payload.filePath ?? '');
116
- if (!filePath)
117
- throw new Error('sqlite_path_missing');
118
- mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
119
- const database = new Database(filePath);
120
- db = database;
121
- const version = String(database.prepare('select sqlite_version() as v').get().v);
122
- if (!sqliteAtLeast(version, MIN_SQLITE)) {
123
- database.close();
124
- db = undefined;
125
- throw new Error(`sqlite_engine_too_old:${version}`);
126
- }
127
- const existing = readSchemaVersion(database);
128
- if (existing !== null && existing !== SCHEMA_VERSION) {
129
- database.close();
130
- db = undefined;
131
- throw new Error(`unsupported_runtime_state:${existing}`);
132
- }
133
- database.pragma('journal_mode = WAL');
134
- database.pragma('synchronous = FULL');
135
- database.pragma('wal_autocheckpoint = 0');
136
- database.pragma('busy_timeout = 2000');
137
- const journalMode = String(database.pragma('journal_mode', { simple: true }));
138
- const synchronous = Number(database.pragma('synchronous', { simple: true }));
139
- if (journalMode !== 'wal')
140
- throw new Error(`journal_mode_not_wal:${journalMode}`);
141
- if (synchronous !== 2)
142
- throw new Error(`synchronous_not_full:${synchronous}`);
143
- chmodSync(filePath, 0o600);
144
- let imported = false;
145
- if (existing === null) {
146
- createSchema(database);
147
- if (payload.legacy && typeof payload.legacy === 'object') {
148
- importLegacy(database, payload.legacy);
149
- imported = true;
150
- }
151
- quarantineReservedTurns(database);
152
- }
153
- else {
154
- quarantineReservedTurns(database);
155
- }
156
- database.exec(`
157
- CREATE INDEX IF NOT EXISTS native_inbox_pending_thread
158
- ON native_inbox(thread_id, ingress_sequence) WHERE converted = 0;
159
- CREATE INDEX IF NOT EXISTS records_native_session_thread
160
- ON records(json_extract(body_json, '$.threadId')) WHERE bucket = 'sessions';
161
- CREATE INDEX IF NOT EXISTS native_inbox_pending_parent
162
- ON native_inbox(COALESCE(json_extract(payload_json, '$.thread.parentThreadId'),
163
- json_extract(payload_json, '$.thread.source.subAgent.thread_spawn.parent_thread_id')))
164
- WHERE converted = 0 AND method = 'thread/started';
165
- `);
166
- return {
167
- ...dump(database),
168
- imported,
169
- engine: { version, journalMode, synchronous, binding: BINDING },
170
- };
171
- }
172
- function createSchema(database) {
173
- database.exec(`
174
- CREATE TABLE IF NOT EXISTS library_meta (
175
- key TEXT PRIMARY KEY,
176
- value TEXT NOT NULL
177
- );
178
- CREATE TABLE IF NOT EXISTS source_epochs (
179
- runtime_id TEXT NOT NULL,
180
- generation INTEGER NOT NULL,
181
- runtime_epoch INTEGER NOT NULL,
182
- next_sequence INTEGER NOT NULL,
183
- local_durable_through INTEGER NOT NULL,
184
- cloud_ack_through INTEGER NOT NULL,
185
- retained_from INTEGER NOT NULL,
186
- PRIMARY KEY (runtime_id, generation, runtime_epoch)
187
- );
188
- CREATE TABLE IF NOT EXISTS source_events (
189
- runtime_id TEXT NOT NULL,
190
- generation INTEGER NOT NULL,
191
- runtime_epoch INTEGER NOT NULL,
192
- source_sequence INTEGER NOT NULL,
193
- payload_hash TEXT NOT NULL,
194
- workspace_id TEXT,
195
- session_id TEXT,
196
- operation_id TEXT,
197
- thread_id TEXT,
198
- turn_id TEXT,
199
- item_id TEXT,
200
- type TEXT NOT NULL,
201
- payload_json TEXT NOT NULL,
202
- occurred_at TEXT NOT NULL,
203
- PRIMARY KEY (runtime_id, generation, runtime_epoch, source_sequence)
204
- );
205
- CREATE TABLE IF NOT EXISTS native_inbox (
206
- ingress_sequence INTEGER PRIMARY KEY,
207
- runtime_epoch INTEGER NOT NULL,
208
- kind TEXT NOT NULL,
209
- method TEXT,
210
- thread_id TEXT,
211
- turn_id TEXT,
212
- item_id TEXT,
213
- payload_json TEXT NOT NULL,
214
- converted INTEGER NOT NULL DEFAULT 0,
215
- source_sequence INTEGER,
216
- received_at TEXT NOT NULL
217
- );
218
- CREATE TABLE IF NOT EXISTS operation_intents (
219
- scope_key TEXT PRIMARY KEY,
220
- operation_json TEXT NOT NULL,
221
- gateway_operation_id TEXT,
222
- sandbox_operation_id TEXT NOT NULL,
223
- payload_hash TEXT NOT NULL,
224
- native_request_id TEXT,
225
- thread_id TEXT,
226
- turn_id TEXT,
227
- reconcile_status TEXT NOT NULL
228
- );
229
- CREATE TABLE IF NOT EXISTS replay_pins (
230
- pin_id TEXT PRIMARY KEY,
231
- epochs_json TEXT NOT NULL,
232
- created_at TEXT NOT NULL,
233
- expires_at TEXT NOT NULL
234
- );
235
- CREATE TABLE IF NOT EXISTS records (
236
- bucket TEXT NOT NULL,
237
- record_key TEXT NOT NULL,
238
- body_json TEXT NOT NULL,
239
- PRIMARY KEY (bucket, record_key)
240
- );
241
- `);
242
- setMeta(database, 'schema_version', String(SCHEMA_VERSION));
243
- setMeta(database, 'migration_status', 'complete');
244
- setMeta(database, 'source_read_token', randomBytes(32).toString('base64url'));
245
- setMeta(database, 'accepted_lease_fence', '0');
246
- setMeta(database, 'destroyed', '0');
247
- setMeta(database, 'runtime_epoch', '0');
248
- setMeta(database, 'ingress_counter', '0');
249
- setMeta(database, 'execution_group', JSON.stringify({ stopConfirmed: false, replaced: false }));
250
- }
251
- function readSchemaVersion(database) {
252
- const table = database
253
- .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'library_meta'`)
254
- .get();
255
- if (!table)
256
- return null;
257
- const row = database.prepare(`SELECT value FROM library_meta WHERE key = 'schema_version'`).get();
258
- if (!row?.value)
259
- return null;
260
- const parsed = Number(row.value);
261
- return Number.isInteger(parsed) ? parsed : null;
262
- }
263
- function importLegacy(database, legacy) {
264
- const snapshot = asRecord(legacy);
265
- const operations = asRecord(snapshot.operations);
266
- for (const [scopeKey, value] of Object.entries(operations)) {
267
- const parsed = operationRecordSchema.safeParse(value);
268
- if (!parsed.success)
269
- continue;
270
- putOperation(database, scopeKey, parsed.data);
271
- }
272
- const events = asRecord(snapshot.events);
273
- const sequences = [];
274
- for (const value of Object.values(events)) {
275
- const parsed = pendingSourceEventSchema.safeParse(value);
276
- if (!parsed.success)
277
- continue;
278
- const hash = hashOperationPayload(parsed.data.payload);
279
- if (hash !== parsed.data.payloadHash)
280
- continue;
281
- insertEventRow(database, parsed.data, {});
282
- sequences.push(parsed.data.source.sourceSequence);
283
- }
284
- const declared = Number(snapshot.lastSourceSequence) || 0;
285
- const maxImported = sequences.reduce((max, value) => Math.max(max, value), 0);
286
- const counter = Math.max(declared, maxImported, 0);
287
- const runtimeId = firstString(snapshot, 'runtimeId') || eventRuntime(events, 'runtimeId');
288
- const generation = Number.isInteger(snapshot.generation) ? Number(snapshot.generation) : eventRuntimeNumber(events, 'generation');
289
- const runtimeEpoch = Number.isInteger(snapshot.runtimeEpoch)
290
- ? Number(snapshot.runtimeEpoch)
291
- : eventRuntimeNumber(events, 'runtimeEpoch');
292
- if (runtimeId) {
293
- ensureEpoch(database, runtimeId, generation, runtimeEpoch);
294
- database
295
- .prepare(`UPDATE source_epochs SET next_sequence = ?, local_durable_through = ?
296
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
297
- .run(counter, counter, runtimeId, generation, runtimeEpoch);
298
- setMeta(database, 'runtime_id', runtimeId);
299
- setMeta(database, 'generation', String(generation));
300
- }
301
- importBucket(database, 'workspaces', asRecord(snapshot.workspaces), (value) => workspaceRegistrationSchema.safeParse(value).success);
302
- importBucket(database, 'sessions', asRecord(snapshot.sessions), (value) => {
303
- const session = asRecord(value);
304
- return typeof session.sessionId === 'string' && typeof session.threadId === 'string';
305
- });
306
- importBucket(database, 'terminals', asRecord(snapshot.terminals), (value) => terminalRecordSchema.safeParse(value).success);
307
- importBucket(database, 'approvals', asRecord(snapshot.approvals), (value) => isApproval(value));
308
- importBucket(database, 'managed_writes', asRecord(snapshot.managedWrites), () => true);
309
- importBucket(database, 'file_revisions', asRecord(snapshot.fileRevisions), () => true);
310
- importBucket(database, 'receipts', asRecord(snapshot.receipts), () => true);
311
- const workspaceFiles = asRecord(snapshot.workspaceFileRevisions);
312
- for (const [workspaceId, files] of Object.entries(workspaceFiles)) {
313
- importBucket(database, `workspace_files:${workspaceId}`, asRecord(files), () => true);
314
- }
315
- setMeta(database, 'accepted_lease_fence', String(Number(snapshot.acceptedLeaseFence) || 0));
316
- setMeta(database, 'destroyed', snapshot.destroyed === true ? '1' : '0');
317
- setMeta(database, 'runtime_epoch', String(runtimeEpoch));
318
- if (snapshot.executionGroup && typeof snapshot.executionGroup === 'object') {
319
- setMeta(database, 'execution_group', JSON.stringify(snapshot.executionGroup));
320
- }
321
- setMeta(database, 'migration_status', 'complete');
322
- }
323
- function quarantineReservedTurns(database) {
324
- const rows = database.prepare('SELECT scope_key, operation_json FROM operation_intents').all();
325
- for (const row of rows) {
326
- const parsed = operationRecordSchema.safeParse(JSON.parse(row.operation_json));
327
- if (!parsed.success)
328
- continue;
329
- const operation = parsed.data;
330
- if (!['session.turn', 'session.review', 'session.compact', 'session.steer'].includes(operation.kind) || operation.status !== 'dispatched')
331
- continue;
332
- if (!operation.nativeRequestId || (operation.kind !== 'session.steer' && operation.nativeTurnId))
333
- continue;
334
- const uncertain = transitionOperation(operation, 'uncertain', { error: 'native_receipt_lost' });
335
- putOperation(database, row.scope_key, uncertain);
336
- if (operation.sessionId && operation.kind !== 'session.steer') {
337
- const session = readSession(database, operation.sessionId);
338
- if (session) {
339
- upsertRecord(database, 'sessions', operation.sessionId, { ...session, turnBlock: 'uncertain', turnStatus: 'starting' });
340
- }
341
- }
342
- }
343
- }
344
- function allocateEvent(database, body) {
345
- const runtimeId = String(body.runtimeId);
346
- const generation = Number(body.generation);
347
- const runtimeEpoch = Number(body.runtimeEpoch);
348
- ensureEpoch(database, runtimeId, generation, runtimeEpoch);
349
- const allocated = database
350
- .prepare(`UPDATE source_epochs
351
- SET next_sequence = next_sequence + 1,
352
- local_durable_through = next_sequence + 1
353
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?
354
- RETURNING next_sequence`)
355
- .get(runtimeId, generation, runtimeEpoch);
356
- const payload = body.payload ?? {};
357
- const event = pendingSourceEventSchema.parse({
358
- source: { runtimeId, generation, runtimeEpoch, sourceSequence: allocated.next_sequence },
359
- payloadHash: hashOperationPayload(payload),
360
- type: String(body.type),
361
- payload,
362
- occurredAt: String(body.occurredAt),
363
- });
364
- insertEventRow(database, event, {
365
- workspaceId: optionalString(body.workspaceId),
366
- sessionId: optionalString(body.sessionId),
367
- operationId: optionalString(body.operationId),
368
- threadId: optionalString(body.threadId),
369
- turnId: optionalString(body.turnId),
370
- itemId: optionalString(body.itemId),
371
- });
372
- return { committedEvents: [event] };
373
- }
374
- function commitIdentity(database, value) {
375
- const event = pendingSourceEventSchema.parse(value);
376
- const existing = database
377
- .prepare(`SELECT payload_hash, payload_json, type, occurred_at FROM source_events
378
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence = ?`)
379
- .get(event.source.runtimeId, event.source.generation, event.source.runtimeEpoch, event.source.sourceSequence);
380
- if (existing) {
381
- if (existing.payload_hash !== event.payloadHash)
382
- throw new Error('source_payload_mismatch');
383
- return { committedEvents: [] };
384
- }
385
- ensureEpoch(database, event.source.runtimeId, event.source.generation, event.source.runtimeEpoch);
386
- insertEventRow(database, event, {});
387
- database
388
- .prepare(`UPDATE source_epochs
389
- SET next_sequence = CASE WHEN next_sequence < ? THEN ? ELSE next_sequence END,
390
- local_durable_through = CASE WHEN local_durable_through < ? THEN ? ELSE local_durable_through END
391
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
392
- .run(event.source.sourceSequence, event.source.sourceSequence, event.source.sourceSequence, event.source.sourceSequence, event.source.runtimeId, event.source.generation, event.source.runtimeEpoch);
393
- return { committedEvents: [event] };
394
- }
395
- function ingest(database, body) {
396
- const native = nativeIds(body.params);
397
- const threadId = optionalString(body.threadId) || native.threadId;
398
- const turnId = optionalString(body.turnId) || native.turnId;
399
- const itemId = optionalString(body.itemId) || native.itemId;
400
- const ingress = nextIngress(database);
401
- const kind = String(body.kind);
402
- const method = optionalString(body.method);
403
- const hintedTurn = turnId || nativeIds(body.params).turnId;
404
- database
405
- .prepare(`INSERT INTO native_inbox (
406
- ingress_sequence, runtime_epoch, kind, method, thread_id, turn_id, item_id, payload_json, converted, received_at
407
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`)
408
- .run(ingress, Number(body.runtimeEpoch) || 0, kind, method ?? null, threadId ?? null, hintedTurn ?? null, itemId ?? null, JSON.stringify(body.params ?? {}), String(body.occurredAt));
409
- return { committedEvents: releaseInbox(database, { ingress }, { runtimeId: String(body.runtimeId), generation: Number(body.generation) || 0 }) };
410
- }
411
- function bindTurn(database, body) {
412
- const operation = operationRecordSchema.parse(body.operation);
413
- putOperation(database, String(body.scopeKey), operation);
414
- if (operation.generation === metaNumber(database, 'generation') && operation.runtimeEpoch !== undefined &&
415
- stoppedExecutionEpochs(database, metaString(database, 'runtime_id'), operation.generation).includes(operation.runtimeEpoch)) {
416
- const released = releaseInbox(database, { rootThreadId: String(body.threadId), turnId: String(body.turnId) });
417
- applyRecordedTurn(database, String(body.threadId), String(body.turnId));
418
- return { committedEvents: [...released, ...settleStoppedExecution(database, {
419
- runtimeId: metaString(database, 'runtime_id'), generation: operation.generation, runtimeEpoch: operation.runtimeEpoch,
420
- }, Number(executionGroup(database).previousPid ?? executionGroup(database).pid), new Date().toISOString(), stopWriter(database))] };
421
- }
422
- const sessionId = String(body.sessionId);
423
- const session = readSession(database, sessionId);
424
- if (session) {
425
- upsertRecord(database, 'sessions', sessionId, {
426
- ...session,
427
- turnStatus: 'running',
428
- turnBlock: undefined,
429
- activeTurnId: String(body.turnId),
430
- threadId: String(body.threadId),
431
- recoveryContextPending: undefined,
432
- });
433
- }
434
- const threadId = String(body.threadId);
435
- const turnId = String(body.turnId);
436
- const released = releaseInbox(database, { rootThreadId: threadId, turnId });
437
- applyRecordedTurn(database, threadId, turnId);
438
- return { committedEvents: released };
439
- }
440
- function releaseInbox(database, selection, identity) {
441
- const events = [];
442
- const stoppedProcesses = [];
443
- const rows = [];
444
- const queued = new Set();
445
- const enqueue = (pending) => {
446
- for (const row of pending) {
447
- const ingress = Number(row.ingress_sequence);
448
- if (!queued.has(ingress)) {
449
- queued.add(ingress);
450
- rows.push(row);
451
- }
452
- }
453
- };
454
- const enqueueChildren = (parentId) => enqueue(database.prepare(`
455
- SELECT * FROM native_inbox WHERE converted = 0 AND method = 'thread/started'
456
- AND COALESCE(json_extract(payload_json, '$.thread.parentThreadId'),
457
- json_extract(payload_json, '$.thread.source.subAgent.thread_spawn.parent_thread_id')) = ?
458
- ORDER BY ingress_sequence
459
- `).all(parentId));
460
- const enqueueThread = (threadId) => {
461
- enqueue(database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND thread_id = ? ORDER BY ingress_sequence').all(threadId));
462
- enqueueChildren(threadId);
463
- };
464
- if ('ingress' in selection) {
465
- const current = database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND ingress_sequence = ?').get(selection.ingress);
466
- if (current) {
467
- const threadId = optionalString(current.thread_id), turnId = optionalString(current.turn_id);
468
- if (nativeEventOwner(database, threadId, turnId)) {
469
- // A receipt can bind between notifications. Drain that thread's earlier events first.
470
- enqueue(database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND thread_id = ? AND (turn_id = ? OR turn_id IS NULL) AND ingress_sequence <= ? ORDER BY ingress_sequence')
471
- .all(threadId, turnId ?? null, selection.ingress));
472
- }
473
- else
474
- enqueue([current]);
475
- }
476
- }
477
- else {
478
- enqueue(database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND thread_id = ? AND (turn_id = ? OR turn_id IS NULL) ORDER BY ingress_sequence')
479
- .all(selection.rootThreadId, selection.turnId));
480
- enqueueChildren(selection.rootThreadId);
481
- for (const threadId of nativeSubagentThreads(database, selection.rootThreadId))
482
- enqueueThread(threadId);
483
- }
484
- for (const stored of rows) {
485
- const threadId = optionalString(stored.thread_id);
486
- const turnId = optionalString(stored.turn_id);
487
- const method = optionalString(stored.method);
488
- const params = JSON.parse(String(stored.payload_json || '{}'));
489
- const stopped = stored.kind === 'process_exit' && Number(asRecord(params).pid) === executionGroup(database).pid &&
490
- Number(stored.runtime_epoch) === metaNumber(database, 'runtime_epoch') &&
491
- (!identity || (identity.runtimeId === metaString(database, 'runtime_id') && identity.generation === metaNumber(database, 'generation')));
492
- if (stopped)
493
- for (const child of clearPendingNativeFollowups(database))
494
- enqueueThread(child);
495
- for (const child of learnNativeSubagents(database, method ?? '', params, threadId, turnId))
496
- enqueueThread(child);
497
- const owner = nativeEventOwner(database, threadId, turnId);
498
- if (stored.kind !== 'process_exit' && !owner) {
499
- queued.delete(Number(stored.ingress_sequence));
500
- continue;
501
- }
502
- const event = convertInboxRow(database, Number(stored.ingress_sequence), {
503
- runtimeId: identity?.runtimeId ?? (metaString(database, 'runtime_id') || 'runtime'),
504
- generation: identity?.generation ?? metaNumber(database, 'generation'),
505
- runtimeEpoch: Number(stored.runtime_epoch),
506
- kind: String(stored.kind), method, threadId, turnId,
507
- itemId: optionalString(stored.item_id), occurredAt: String(stored.received_at),
508
- session: owner?.session, operation: owner?.operation, subAgent: owner?.subAgent, params,
509
- });
510
- if (event)
511
- events.push(event);
512
- if (stopped)
513
- stoppedProcesses.push({ runtimeEpoch: Number(stored.runtime_epoch), pid: Number(asRecord(params).pid), occurredAt: String(stored.received_at) });
514
- if (!owner?.subAgent && threadId && terminalTurn(method ?? '', asRecord(params)))
515
- for (const child of nativeSubagentThreads(database, threadId))
516
- enqueueThread(child);
517
- }
518
- // Release captured child events first so their real final messages precede synthetic interruption.
519
- for (const stopped of stoppedProcesses)
520
- events.push(...settleStoppedExecution(database, {
521
- runtimeId: metaString(database, 'runtime_id'), generation: metaNumber(database, 'generation'), runtimeEpoch: stopped.runtimeEpoch,
522
- }, stopped.pid, stopped.occurredAt, stopWriter(database)));
523
- return events;
524
- }
525
- function convertInboxRow(database, ingress, input) {
526
- const row = database.prepare('SELECT payload_json, converted FROM native_inbox WHERE ingress_sequence = ?').get(ingress);
527
- if (!row || row.converted)
528
- return undefined;
529
- const params = input.params ?? JSON.parse(row.payload_json || '{}');
530
- const payload = linkedPayload(params, input.session, input.subAgent);
531
- const nativeApprovalId = optionalString(payload.approvalId) || optionalString(payload.itemId) || input.itemId;
532
- const approvalRequest = input.method?.includes('requestApproval') || input.method === 'item/tool/requestUserInput';
533
- const stoppedEpoch = stoppedExecutionEpochs(database, input.runtimeId, input.generation).includes(input.runtimeEpoch);
534
- const ownerOperation = input.operation?.operation;
535
- const currentSource = input.runtimeId === metaString(database, 'runtime_id') && input.generation === metaNumber(database, 'generation') && input.runtimeEpoch === metaNumber(database, 'runtime_epoch');
536
- const ownerMatchesSource = ownerOperation && (ownerOperation.generation === undefined || ownerOperation.generation === input.generation) &&
537
- (ownerOperation.runtimeEpoch === undefined || ownerOperation.runtimeEpoch === input.runtimeEpoch) &&
538
- (currentSource || (ownerOperation.generation !== undefined && ownerOperation.runtimeEpoch !== undefined));
539
- const childApproval = input.subAgent && nativeApprovalId && approvalRequest && !stoppedEpoch && ownerMatchesSource;
540
- if (input.subAgent && nativeApprovalId && (approvalRequest || input.method === 'approval.resolved' || input.method === 'serverRequest/resolved')) {
541
- payload.approvalId = `subagent_${createHash('sha256').update(JSON.stringify([input.subAgent.threadId, nativeApprovalId])).digest('hex')}`;
542
- }
543
- const type = input.kind === 'process_exit' ? 'process.exited' : input.method || input.kind;
544
- const allocated = allocateEvent(database, {
545
- runtimeId: input.runtimeId || metaString(database, 'runtime_id'),
546
- generation: input.generation,
547
- runtimeEpoch: input.runtimeEpoch,
548
- type,
549
- payload,
550
- occurredAt: input.occurredAt,
551
- workspaceId: optionalString(input.session?.workspaceId),
552
- sessionId: optionalString(input.session?.sessionId),
553
- operationId: input.operation?.operation.operationId,
554
- threadId: input.threadId,
555
- turnId: input.turnId,
556
- itemId: input.itemId,
557
- });
558
- const event = allocated.committedEvents[0];
559
- if (!event)
560
- return undefined;
561
- database
562
- .prepare(`UPDATE native_inbox
563
- SET converted = 1, source_sequence = ?, payload_json = '{}'
564
- WHERE ingress_sequence = ?`)
565
- .run(event.source.sourceSequence, ingress);
566
- if (childApproval && input.operation?.operation.sessionId) {
567
- upsertRecord(database, 'approvals', `${input.operation.operation.sessionId}:${String(payload.approvalId)}`, {
568
- approvalId: payload.approvalId,
569
- sessionId: input.operation.operation.sessionId,
570
- runtimeId: input.runtimeId,
571
- generation: input.generation,
572
- runtimeEpoch: input.runtimeEpoch,
573
- nativeRequestId: optionalString(payload.requestId) ?? `approval:${nativeApprovalId}`,
574
- nativeApprovalId,
575
- nativeThreadId: input.subAgent.threadId,
576
- nativeTurnId: input.turnId,
577
- rootThreadId: input.subAgent.rootThreadId,
578
- rootTurnId: input.operation.operation.nativeTurnId,
579
- method: input.method,
580
- request: payload,
581
- status: 'pending',
582
- });
583
- }
584
- settleNativeApprovals(database, input, payload);
585
- if (!input.subAgent)
586
- applyNativeToState(database, { ...input, operation: resolveOperation(database, input, payload) }, payload);
587
- return event;
588
- }
589
- /** An ended native request must never reappear as a pending approval after reconnect. */
590
- function settleNativeApprovals(database, input, payload) {
591
- const resolved = input.method === 'approval.resolved' || input.method === 'serverRequest/resolved';
592
- const terminal = Boolean(terminalTurn(input.method ?? '', payload));
593
- if (!resolved && !terminal)
594
- return;
595
- const rows = database.prepare("SELECT record_key, body_json FROM records WHERE bucket = 'approvals'").all();
596
- for (const row of rows) {
597
- const approval = asRecord(JSON.parse(row.body_json));
598
- if (approval.status !== 'pending')
599
- continue;
600
- if (!input.runtimeId || input.generation === undefined || input.runtimeEpoch === undefined ||
601
- approval.runtimeId !== input.runtimeId || approval.generation !== input.generation || approval.runtimeEpoch !== input.runtimeEpoch)
602
- continue;
603
- const matchesThread = approval.nativeThreadId === input.threadId;
604
- const matchesRequest = (payload.approvalId && approval.approvalId === payload.approvalId)
605
- || (payload.requestId !== undefined && String(approval.nativeRequestId) === String(payload.requestId));
606
- const endedParent = terminal && approval.rootThreadId === input.threadId && approval.rootTurnId === input.turnId;
607
- if (!endedParent && (!matchesThread || (resolved ? !matchesRequest
608
- : input.turnId && approval.nativeTurnId && approval.nativeTurnId !== input.turnId)))
609
- continue;
610
- upsertRecord(database, 'approvals', row.record_key, { ...approval,
611
- status: input.method === 'approval.resolved' ? payload.accepted === false ? 'rejected' : 'approved' : 'expired' });
612
- }
613
- }
614
- function applyRecordedTurn(database, threadId, turnId) {
615
- const rows = database
616
- .prepare(`SELECT type, payload_json, thread_id, session_id, runtime_id, generation, runtime_epoch FROM source_events
617
- WHERE (thread_id = ? OR thread_id IS NULL) AND (turn_id = ? OR payload_json LIKE ?)
618
- ORDER BY source_sequence`)
619
- .all(threadId, turnId, `%"${turnId}"%`);
620
- for (const row of rows) {
621
- if (!row.type.includes('requestApproval') && !row.type.includes('requestUserInput') && !row.type.includes('completed') && !row.type.includes('interrupted') && row.type !== 'turn.failed' && row.type !== 'approval.resolved' && row.type !== 'serverRequest/resolved') {
622
- continue;
623
- }
624
- const payload = asRecord(JSON.parse(row.payload_json));
625
- if (payload.subAgent)
626
- continue;
627
- const session = sessionByThread(database, threadId) ?? (row.session_id ? readSession(database, row.session_id) : undefined);
628
- applyNativeToState(database, {
629
- method: row.type,
630
- kind: 'notification',
631
- runtimeId: row.runtime_id,
632
- generation: row.generation,
633
- runtimeEpoch: row.runtime_epoch,
634
- threadId: optionalString(row.thread_id) || threadId,
635
- turnId,
636
- session,
637
- operation: operationByTurn(database, turnId, threadId),
638
- }, payload);
639
- }
640
- }
641
- function resolveOperation(database, input, payload) {
642
- const turnId = input.turnId || nativeIds(payload).turnId;
643
- if (turnId)
644
- return input.operation ?? operationByTurn(database, turnId, input.threadId ?? nativeIds(payload).threadId);
645
- return input.operation;
646
- }
647
- function applyNativeToState(database, input, payload) {
648
- const method = input.method ?? '';
649
- settleNativeApprovals(database, input, payload);
650
- const currentSource = input.runtimeId === metaString(database, 'runtime_id') && input.generation === metaNumber(database, 'generation') &&
651
- input.runtimeEpoch === metaNumber(database, 'runtime_epoch');
652
- const operation = input.operation?.operation;
653
- const operationMatchesSource = !operation || ((operation.generation === undefined || operation.generation === input.generation) &&
654
- (operation.runtimeEpoch === undefined || operation.runtimeEpoch === input.runtimeEpoch) &&
655
- (currentSource || (operation.generation !== undefined && operation.runtimeEpoch !== undefined)));
656
- if (!operationMatchesSource)
657
- return;
658
- const session = input.session;
659
- // A late old-turn notification is history, not the state of a newer turn on this thread.
660
- if (currentSource && session?.sessionId && (!input.turnId || !session.activeTurnId || input.turnId === session.activeTurnId)) {
661
- const next = { ...session };
662
- const approval = method.includes('requestApproval') || method.includes('requestUserInput');
663
- if (approval)
664
- next.turnStatus = moveTurn(String(session.turnStatus ?? 'running'), 'waiting_for_user');
665
- else if (method === 'approval.resolved' || method === 'serverRequest/resolved') {
666
- const waiting = database.prepare("SELECT 1 FROM records WHERE bucket = 'approvals' AND json_extract(body_json, '$.sessionId') = ? AND json_extract(body_json, '$.status') = 'pending' AND json_extract(body_json, '$.nativeThreadId') = ? LIMIT 1")
667
- .get(session.sessionId, session.threadId);
668
- if (!waiting)
669
- next.turnStatus = moveTurn(String(session.turnStatus ?? 'waiting_for_user'), 'running');
670
- }
671
- else if (method.includes('delta') || method === 'turn/started' || method === 'turn.started') {
672
- const current = String(session.turnStatus ?? 'starting');
673
- if (current !== 'waiting_for_user')
674
- next.turnStatus = moveTurn(current, 'running');
675
- }
676
- const terminal = terminalTurn(method, payload);
677
- if (terminal) {
678
- let status = String(next.turnStatus ?? session.turnStatus ?? 'running');
679
- if (status === 'waiting_for_user')
680
- status = moveTurn(status, 'running');
681
- next.turnStatus = moveTurn(status, terminal);
682
- next.turnBlock = undefined;
683
- }
684
- upsertRecord(database, 'sessions', String(session.sessionId), next);
685
- }
686
- if (!input.operation)
687
- return;
688
- let ownedOperation = input.operation.operation;
689
- const approvalId = optionalString(payload.approvalId) || optionalString(payload.itemId) || input.itemId;
690
- if ((method.includes('requestApproval') || method.includes('requestUserInput')) && approvalId && ownedOperation.sessionId && currentSource &&
691
- !['completed', 'failed', 'interrupted'].includes(ownedOperation.status)) {
692
- upsertRecord(database, 'approvals', `${ownedOperation.sessionId}:${approvalId}`, {
693
- approvalId,
694
- sessionId: ownedOperation.sessionId,
695
- runtimeId: input.runtimeId,
696
- generation: input.generation,
697
- runtimeEpoch: input.runtimeEpoch,
698
- nativeRequestId: optionalString(payload.requestId) ?? `approval:${approvalId}`,
699
- nativeThreadId: input.threadId,
700
- nativeTurnId: input.turnId,
701
- nativeApprovalId: approvalId,
702
- method,
703
- request: payload,
704
- status: 'pending',
705
- });
706
- ownedOperation = operationRecordSchema.parse({
707
- ...ownedOperation,
708
- nativeApprovalId: approvalId,
709
- updatedAt: new Date().toISOString(),
710
- });
711
- putOperation(database, input.operation.scopeKey, ownedOperation);
712
- }
713
- const terminal = terminalTurn(method, payload);
714
- if (terminal && ownedOperation.status === 'accepted') {
715
- putOperation(database, input.operation.scopeKey, transitionOperation(ownedOperation, terminal, {
716
- nativeTurnId: input.turnId ?? ownedOperation.nativeTurnId,
717
- nativeThreadId: input.threadId ?? ownedOperation.nativeThreadId,
718
- }));
719
- }
720
- }
721
- function acknowledge(database, body) {
722
- const leaseFence = Number(body.leaseFence);
723
- const accepted = metaNumber(database, 'accepted_lease_fence');
724
- if (!Number.isInteger(leaseFence) || leaseFence < accepted)
725
- throw new Error('lease_fence_mismatch');
726
- const epochs = Array.isArray(body.epochs) ? body.epochs : [];
727
- for (const value of epochs) {
728
- const epoch = asRecord(value);
729
- if (epoch.runtimeId !== metaString(database, 'runtime_id') || Number(epoch.generation) !== metaNumber(database, 'generation'))
730
- throw new Error('source_identity_mismatch');
731
- const row = epochRow(database, String(epoch.runtimeId), Number(epoch.generation), Number(epoch.runtimeEpoch));
732
- if (!row)
733
- throw new Error('unknown_source_epoch');
734
- const next = Number(epoch.confirmedThrough);
735
- if (!Number.isInteger(next) || next < row.cloud_ack_through)
736
- throw new Error('ack_not_monotonic');
737
- if (next > row.local_durable_through)
738
- throw new Error('ack_ahead_of_local');
739
- database
740
- .prepare(`UPDATE source_epochs SET cloud_ack_through = ?
741
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
742
- .run(next, epoch.runtimeId, epoch.generation, epoch.runtimeEpoch);
743
- }
744
- // An archive acknowledgement is a control-authenticated write; adopting its fence creates no task.
745
- setMeta(database, 'accepted_lease_fence', String(leaseFence));
746
- }
747
- function createPin(database, body) {
748
- const now = Date.now();
749
- database.prepare('DELETE FROM replay_pins WHERE expires_at <= ?').run(new Date(now).toISOString());
750
- const count = database.prepare('SELECT count(*) AS n FROM replay_pins').get().n;
751
- if (count >= MAX_PINS)
752
- throw new Error('pin_quota');
753
- const ttl = Math.min(Number(body.ttlMs) || 60_000, MAX_PIN_MS);
754
- const identity = asRecord(body.identity);
755
- ensureEpoch(database, String(identity.runtimeId), Number(identity.generation), Number(identity.runtimeEpoch));
756
- const epochs = database.prepare('SELECT * FROM source_epochs').all().map((row) => ({
757
- runtimeId: row.runtime_id,
758
- generation: row.generation,
759
- runtimeEpoch: row.runtime_epoch,
760
- retainedFrom: row.retained_from,
761
- highWater: row.local_durable_through,
762
- }));
763
- const current = epochs.find((epoch) => epoch.runtimeId === identity.runtimeId &&
764
- epoch.generation === Number(identity.generation) &&
765
- epoch.runtimeEpoch === Number(identity.runtimeEpoch));
766
- const pinId = `pin_${randomUUID()}`;
767
- const createdAt = new Date(now).toISOString();
768
- const expiresAt = new Date(now + ttl).toISOString();
769
- database
770
- .prepare('INSERT INTO replay_pins (pin_id, epochs_json, created_at, expires_at) VALUES (?, ?, ?, ?)')
771
- .run(pinId, JSON.stringify(epochs), createdAt, expiresAt);
772
- const highWater = current?.highWater ?? 0;
773
- return {
774
- pin: {
775
- pinId,
776
- epochs,
777
- followBoundary: {
778
- runtimeId: String(identity.runtimeId),
779
- generation: Number(identity.generation),
780
- runtimeEpoch: Number(identity.runtimeEpoch),
781
- sourceSequence: Math.max(highWater, 1),
782
- },
783
- },
784
- };
785
- }
786
- function reclaim(database, minRetainMs) {
787
- const pins = activePins(database);
788
- const epochs = database.prepare('SELECT * FROM source_epochs').all();
789
- const now = Date.now();
790
- for (const epoch of epochs) {
791
- let retained = epoch.retained_from;
792
- while (true) {
793
- const row = database
794
- .prepare(`SELECT source_sequence, occurred_at FROM source_events
795
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence >= ?
796
- ORDER BY source_sequence LIMIT 1`)
797
- .get(epoch.runtime_id, epoch.generation, epoch.runtime_epoch, retained);
798
- if (!row || row.source_sequence !== retained)
799
- break;
800
- if (row.source_sequence > epoch.cloud_ack_through)
801
- break;
802
- if (pins.some((pin) => row.source_sequence >= pin.retainedFrom && row.source_sequence <= pin.highWater && pin.runtimeId === epoch.runtime_id && pin.generation === epoch.generation && pin.runtimeEpoch === epoch.runtime_epoch)) {
803
- break;
804
- }
805
- const age = now - Date.parse(row.occurred_at);
806
- if (Number.isFinite(age) && age < minRetainMs)
807
- break;
808
- database
809
- .prepare(`DELETE FROM source_events
810
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence = ?`)
811
- .run(epoch.runtime_id, epoch.generation, epoch.runtime_epoch, row.source_sequence);
812
- retained = row.source_sequence + 1;
813
- }
814
- if (retained !== epoch.retained_from) {
815
- database
816
- .prepare(`UPDATE source_epochs SET retained_from = ?
817
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
818
- .run(retained, epoch.runtime_id, epoch.generation, epoch.runtime_epoch);
819
- }
820
- }
821
- }
822
- function dump(database, includeEvents = true) {
823
- const snapshot = {
824
- workspaces: bucketObject(database, 'workspaces'),
825
- workspaceFileRevisions: workspaceFiles(database),
826
- operations: operationsObject(database),
827
- receipts: bucketObject(database, 'receipts'),
828
- // Startup and reclamation replace history; ordinary writes return only their new events.
829
- ...(includeEvents ? { events: eventsObject(database) } : {}),
830
- sessions: bucketObject(database, 'sessions'),
831
- terminals: bucketObject(database, 'terminals'),
832
- fileRevisions: bucketObject(database, 'file_revisions'),
833
- approvals: bucketObject(database, 'approvals'),
834
- managedWrites: bucketObject(database, 'managed_writes'),
835
- workspaceDependencies: bucketObject(database, 'workspace_dependencies'),
836
- executionGroup: executionGroup(database),
837
- stoppedRuntimeEpochs: stoppedExecutionEpochs(database, metaString(database, 'runtime_id'), metaNumber(database, 'generation')),
838
- runtimeEpoch: metaNumber(database, 'runtime_epoch'),
839
- lastSourceSequence: currentCounter(database),
840
- acceptedLeaseFence: metaNumber(database, 'accepted_lease_fence'),
841
- destroyed: metaString(database, 'destroyed') === '1',
842
- };
843
- return {
844
- snapshot,
845
- epochs: database.prepare('SELECT * FROM source_epochs').all(),
846
- sourceReadToken: metaString(database, 'source_read_token'),
847
- pendingInbox: database.prepare('SELECT count(*) AS n FROM native_inbox WHERE converted = 0').get().n,
848
- };
849
- }
850
- function eventsObject(database) {
851
- const rows = database.prepare('SELECT * FROM source_events ORDER BY runtime_epoch, source_sequence').all();
852
- const events = {};
853
- for (const row of rows) {
854
- const event = pendingSourceEventSchema.parse({
855
- source: {
856
- runtimeId: row.runtime_id,
857
- generation: row.generation,
858
- runtimeEpoch: row.runtime_epoch,
859
- sourceSequence: row.source_sequence,
860
- },
861
- payloadHash: row.payload_hash,
862
- type: row.type,
863
- payload: JSON.parse(String(row.payload_json)),
864
- occurredAt: row.occurred_at,
865
- });
866
- events[`${event.source.runtimeId}:${event.source.generation}:${event.source.runtimeEpoch}:${event.source.sourceSequence}`] = event;
867
- }
868
- return events;
869
- }
870
- function operationsObject(database) {
871
- const rows = database.prepare('SELECT scope_key, operation_json FROM operation_intents').all();
872
- const operations = {};
873
- for (const row of rows) {
874
- const parsed = operationRecordSchema.safeParse(JSON.parse(row.operation_json));
875
- if (parsed.success)
876
- operations[row.scope_key] = parsed.data;
877
- }
878
- return operations;
879
- }
880
- function putOperation(database, scopeKey, value) {
881
- const operation = operationRecordSchema.parse(value);
882
- if (metaString(database, 'runtime_id') && operation.generation === metaNumber(database, 'generation') && operation.runtimeEpoch === metaNumber(database, 'runtime_epoch')) {
883
- const key = JSON.stringify([scopeKey, operation.operationId]);
884
- database.prepare("INSERT INTO records (bucket, record_key, body_json) VALUES ('operation_execution_scopes', ?, ?) ON CONFLICT DO NOTHING")
885
- .run(key, JSON.stringify({ runtimeId: metaString(database, 'runtime_id'), generation: operation.generation, runtimeEpoch: operation.runtimeEpoch }));
886
- }
887
- database
888
- .prepare(`INSERT INTO operation_intents (
889
- scope_key, operation_json, gateway_operation_id, sandbox_operation_id, payload_hash,
890
- native_request_id, thread_id, turn_id, reconcile_status
891
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
892
- ON CONFLICT(scope_key) DO UPDATE SET
893
- operation_json = excluded.operation_json,
894
- gateway_operation_id = excluded.gateway_operation_id,
895
- sandbox_operation_id = excluded.sandbox_operation_id,
896
- payload_hash = excluded.payload_hash,
897
- native_request_id = excluded.native_request_id,
898
- thread_id = excluded.thread_id,
899
- turn_id = excluded.turn_id,
900
- reconcile_status = excluded.reconcile_status`)
901
- .run(scopeKey, JSON.stringify(operation), null, operation.sandboxOperationId ?? operation.operationId, operation.payloadHash, operation.nativeRequestId ?? null, operation.nativeThreadId ?? null, operation.nativeTurnId ?? null, operation.status);
902
- }
903
- function insertEventRow(database, event, links) {
904
- database
905
- .prepare(`INSERT INTO source_events (
906
- runtime_id, generation, runtime_epoch, source_sequence, payload_hash, workspace_id, session_id,
907
- operation_id, thread_id, turn_id, item_id, type, payload_json, occurred_at
908
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
909
- .run(event.source.runtimeId, event.source.generation, event.source.runtimeEpoch, event.source.sourceSequence, event.payloadHash, links.workspaceId ?? null, links.sessionId ?? null, links.operationId ?? null, links.threadId ?? null, links.turnId ?? null, links.itemId ?? null, event.type, JSON.stringify(event.payload), event.occurredAt);
910
- }
911
- function ensureEpoch(database, runtimeId, generation, runtimeEpoch) {
912
- database
913
- .prepare(`INSERT INTO source_epochs (
914
- runtime_id, generation, runtime_epoch, next_sequence, local_durable_through, cloud_ack_through, retained_from
915
- ) VALUES (?, ?, ?, 0, 0, 0, 1)
916
- ON CONFLICT DO NOTHING`)
917
- .run(runtimeId, generation, runtimeEpoch);
918
- }
919
- function writeExecutionEpoch(database, body) {
920
- const runtimeEpoch = Number(body.runtimeEpoch);
921
- const previousRuntimeEpoch = Number(body.previousRuntimeEpoch);
922
- const committedEvents = body.stopConfirmed === true && Number.isInteger(previousRuntimeEpoch) && previousRuntimeEpoch < runtimeEpoch && Number(body.previousPid) > 0
923
- ? settleStoppedExecution(database, { runtimeId: metaString(database, 'runtime_id'), generation: metaNumber(database, 'generation'), runtimeEpoch: previousRuntimeEpoch }, Number(body.previousPid), new Date().toISOString(), stopWriter(database)) : [];
924
- setMeta(database, 'runtime_epoch', String(runtimeEpoch));
925
- setMeta(database, 'execution_group', JSON.stringify({
926
- pid: body.pid,
927
- startedAt: body.startedAt,
928
- previousPid: body.previousPid,
929
- previousRuntimeEpoch: body.previousRuntimeEpoch,
930
- stopConfirmed: body.stopConfirmed === true,
931
- replaced: true,
932
- }));
933
- const runtimeId = metaString(database, 'runtime_id');
934
- const generation = metaNumber(database, 'generation');
935
- if (runtimeId)
936
- ensureEpoch(database, runtimeId, generation, runtimeEpoch);
937
- return { committedEvents };
938
- }
939
- function confirmExecutionStop(database, stopConfirmed) {
940
- const group = executionGroup(database);
941
- const epoch = Number(group.previousRuntimeEpoch);
942
- const confirmed = stopConfirmed && group.replaced === true && Number.isInteger(epoch) && epoch < metaNumber(database, 'runtime_epoch') && Number(group.previousPid) > 0;
943
- setMeta(database, 'execution_group', JSON.stringify({ ...group, stopConfirmed: confirmed }));
944
- return { committedEvents: confirmed
945
- ? settleStoppedExecution(database, { runtimeId: metaString(database, 'runtime_id'), generation: metaNumber(database, 'generation'), runtimeEpoch: epoch }, Number(group.previousPid), new Date().toISOString(), stopWriter(database)) : [] };
946
- }
947
- function stopWriter(database) {
948
- return { allocate: (input) => allocateEvent(database, input), putOperation: (key, operation) => putOperation(database, key, operation) };
949
- }
950
- function bindRuntime(database, body) {
951
- if (metaString(database, 'runtime_id') !== String(body.runtimeId) || metaNumber(database, 'generation') !== Number(body.generation))
952
- setMeta(database, 'execution_group', JSON.stringify({ stopConfirmed: false, replaced: false }));
953
- setMeta(database, 'runtime_id', String(body.runtimeId));
954
- setMeta(database, 'generation', String(Number(body.generation) || 0));
955
- const current = metaNumber(database, 'runtime_epoch');
956
- const next = Math.max(current, Number(body.runtimeEpoch) || 0);
957
- setMeta(database, 'runtime_epoch', String(next));
958
- ensureEpoch(database, String(body.runtimeId), Number(body.generation) || 0, next);
959
- }
960
- function upsertRecord(database, bucket, key, body) {
961
- database
962
- .prepare(`INSERT INTO records (bucket, record_key, body_json) VALUES (?, ?, ?)
963
- ON CONFLICT(bucket, record_key) DO UPDATE SET body_json = excluded.body_json`)
964
- .run(bucket, key, JSON.stringify(body));
965
- }
966
- function replaceBucket(database, bucket, entries) {
967
- database.prepare('DELETE FROM records WHERE bucket = ?').run(bucket);
968
- for (const entry of entries) {
969
- const record = asRecord(entry);
970
- upsertRecord(database, bucket, String(record.key), record.body);
971
- }
972
- }
973
- function importBucket(database, bucket, records, accept) {
974
- for (const [key, value] of Object.entries(records)) {
975
- if (accept(value))
976
- upsertRecord(database, bucket, key, value);
977
- }
978
- }
979
- function bucketObject(database, bucket) {
980
- const rows = database.prepare('SELECT record_key, body_json FROM records WHERE bucket = ? ORDER BY rowid').all(bucket);
981
- return Object.fromEntries(rows.map((row) => [row.record_key, JSON.parse(row.body_json)]));
982
- }
983
- function workspaceFiles(database) {
984
- const rows = database
985
- .prepare(`SELECT bucket, record_key, body_json FROM records WHERE bucket LIKE 'workspace_files:%'`)
986
- .all();
987
- const files = {};
988
- for (const row of rows) {
989
- const workspaceId = row.bucket.slice('workspace_files:'.length);
990
- (files[workspaceId] ??= {})[row.record_key] = JSON.parse(row.body_json);
991
- }
992
- return files;
993
- }
994
- function readSession(database, sessionId) {
995
- const row = database
996
- .prepare(`SELECT body_json FROM records WHERE bucket = 'sessions' AND record_key = ?`)
997
- .get(sessionId);
998
- return row ? asRecord(JSON.parse(row.body_json)) : undefined;
999
- }
1000
- function sessionByThread(database, threadId) {
1001
- const rows = database.prepare(`SELECT body_json FROM records WHERE bucket = 'sessions'`).all();
1002
- const matches = rows.map((row) => asRecord(JSON.parse(row.body_json))).filter((session) => session.threadId === threadId);
1003
- if (matches.length !== 1)
1004
- return undefined;
1005
- return matches[0];
1006
- }
1007
- function operationByTurn(database, turnId, threadId) {
1008
- const row = database
1009
- .prepare(`SELECT scope_key, operation_json FROM operation_intents WHERE turn_id = ?${threadId ? ' AND thread_id = ?' : ''} AND json_extract(operation_json, '$.kind') IN ('session.turn', 'session.review', 'session.compact')`)
1010
- .get(...(threadId ? [turnId, threadId] : [turnId]));
1011
- if (!row)
1012
- return undefined;
1013
- const parsed = operationRecordSchema.safeParse(JSON.parse(row.operation_json));
1014
- if (!parsed.success)
1015
- return undefined;
1016
- return { scopeKey: row.scope_key, operation: parsed.data };
1017
- }
1018
- function linkedPayload(params, session, subAgent) {
1019
- const payload = { ...asRecord(params) };
1020
- delete payload.workspaceId;
1021
- delete payload.sessionId;
1022
- delete payload.subAgent;
1023
- if (typeof session?.workspaceId === 'string')
1024
- payload.workspaceId = session.workspaceId;
1025
- if (typeof session?.sessionId === 'string')
1026
- payload.sessionId = session.sessionId;
1027
- if (subAgent)
1028
- payload.subAgent = subAgent;
1029
- return payload;
1030
- }
1031
- function nativeIds(params) {
1032
- const record = asRecord(params);
1033
- const turn = asRecord(record.turn);
1034
- const thread = asRecord(record.thread);
1035
- return {
1036
- threadId: optionalString(record.threadId) || optionalString(thread.id),
1037
- turnId: optionalString(record.turnId) || optionalString(turn.id),
1038
- itemId: optionalString(record.itemId) || optionalString(record.approvalId) || optionalString(asRecord(record.item).id),
1039
- };
1040
- }
1041
- function terminalTurn(method, payload) {
1042
- if (method !== 'turn/completed' && method !== 'turn.completed' && method !== 'turn.interrupted' && method !== 'turn.failed') {
1043
- return undefined;
1044
- }
1045
- if (method === 'turn.interrupted')
1046
- return 'interrupted';
1047
- if (method === 'turn.failed')
1048
- return 'failed';
1049
- const status = String(asRecord(payload.turn).status ?? payload.status ?? 'completed');
1050
- if (status === 'interrupted')
1051
- return 'interrupted';
1052
- if (status === 'failed')
1053
- return 'failed';
1054
- return 'completed';
1055
- }
1056
- function moveTurn(current, to) {
1057
- if (current === to)
1058
- return to;
1059
- if (current === 'starting' || current === 'running' || current === 'waiting_for_user' || current === 'completed' || current === 'failed' || current === 'interrupted') {
1060
- return canTransitionTurn(current, to) ? to : current;
1061
- }
1062
- return to;
1063
- }
1064
- function nextIngress(database) {
1065
- const next = metaNumber(database, 'ingress_counter') + 1;
1066
- setMeta(database, 'ingress_counter', String(next));
1067
- return next;
1068
- }
1069
- function epochRow(database, runtimeId, generation, runtimeEpoch) {
1070
- return database
1071
- .prepare('SELECT * FROM source_epochs WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?')
1072
- .get(runtimeId, generation, runtimeEpoch);
1073
- }
1074
- function activePins(database) {
1075
- const now = new Date().toISOString();
1076
- const rows = database.prepare('SELECT epochs_json, expires_at FROM replay_pins').all();
1077
- const pins = [];
1078
- for (const row of rows) {
1079
- if (row.expires_at <= now)
1080
- continue;
1081
- const epochs = JSON.parse(row.epochs_json);
1082
- for (const epoch of epochs)
1083
- pins.push(epoch);
1084
- }
1085
- return pins;
1086
- }
1087
- function currentCounter(database) {
1088
- const row = epochRow(database, metaString(database, 'runtime_id'), metaNumber(database, 'generation'), metaNumber(database, 'runtime_epoch'));
1089
- return row?.next_sequence ?? 0;
1090
- }
1091
- function executionGroup(database) {
1092
- const raw = metaString(database, 'execution_group');
1093
- if (!raw)
1094
- return { stopConfirmed: false, replaced: false };
1095
- try {
1096
- return asRecord(JSON.parse(raw));
1097
- }
1098
- catch {
1099
- return { stopConfirmed: false, replaced: false };
1100
- }
1101
- }
1102
- function setMeta(database, key, value) {
1103
- database
1104
- .prepare(`INSERT INTO library_meta (key, value) VALUES (?, ?)
1105
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`)
1106
- .run(key, value);
1107
- }
1108
- function metaString(database, key) {
1109
- const row = database.prepare('SELECT value FROM library_meta WHERE key = ?').get(key);
1110
- return row?.value ?? '';
1111
- }
1112
- function metaNumber(database, key) {
1113
- const parsed = Number(metaString(database, key));
1114
- return Number.isFinite(parsed) ? parsed : 0;
1115
- }
1116
- function requireDb() {
1117
- if (!db)
1118
- throw new Error('sqlite_not_open');
1119
- return db;
1120
- }
1121
- function sqliteAtLeast(version, minimum) {
1122
- const parts = version.split('.').map((item) => Number(item));
1123
- for (let index = 0; index < minimum.length; index += 1) {
1124
- const left = parts[index] ?? 0;
1125
- const right = minimum[index] ?? 0;
1126
- if (left > right)
1127
- return true;
1128
- if (left < right)
1129
- return false;
1130
- }
1131
- return true;
1132
- }
1133
- function asRecord(value) {
1134
- return value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};
1135
- }
1136
- function optionalString(value) {
1137
- return typeof value === 'string' && value.length > 0 ? value : undefined;
1138
- }
1139
- function firstString(snapshot, key) {
1140
- return typeof snapshot[key] === 'string' ? String(snapshot[key]) : undefined;
1141
- }
1142
- function eventRuntime(events, key) {
1143
- for (const value of Object.values(events)) {
1144
- const source = asRecord(asRecord(value).source);
1145
- if (typeof source[key] === 'string')
1146
- return String(source[key]);
1147
- }
1148
- return undefined;
1149
- }
1150
- function eventRuntimeNumber(events, key) {
1151
- for (const value of Object.values(events)) {
1152
- const source = asRecord(asRecord(value).source);
1153
- if (Number.isInteger(source[key]))
1154
- return Number(source[key]);
1155
- }
1156
- return 0;
1157
- }
1158
- function isApproval(value) {
1159
- const record = asRecord(value);
1160
- return typeof record.approvalId === 'string' && typeof record.sessionId === 'string' && typeof record.nativeRequestId === 'string';
1161
- }