@playcraft/cli 0.1.37-beta → 0.1.39-prod

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 (366) hide show
  1. package/README.md +3 -58
  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 +126 -68
  12. package/dist/commands/tools-image-context.js +23 -0
  13. package/dist/commands/tools.js +2 -2
  14. package/dist/config.js +4 -59
  15. package/dist/index.js +3 -40
  16. package/dist/remix/clone.js +0 -2
  17. package/dist/remix/init-template.js +0 -2
  18. package/dist/remix/pull.js +0 -2
  19. package/dist/remix/push.js +1 -21
  20. package/dist/utils/agent-api-client.js +30 -185
  21. package/dist/utils/image-asset-context.js +106 -0
  22. package/dist/utils/playcraft-http-client.js +4 -6
  23. package/package.json +5 -15
  24. package/project-template/.claude/agents/refs/ta-image-generation-detail.md +67 -123
  25. package/project-template/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  26. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  27. package/project-template/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  28. package/project-template/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  29. package/project-template/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  30. package/project-templates/phaser/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  31. package/project-templates/phaser/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  32. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  33. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  34. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  35. package/project-templates/phaser/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  36. package/project-templates/threejs/.claude/agents/refs/ta-image-generation-detail.md +59 -59
  37. package/project-templates/threejs/.claude/skills/playcraft-image-generation/SKILL.md +135 -63
  38. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +42 -33
  39. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +18 -20
  40. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +6 -7
  41. package/project-templates/threejs/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +39 -39
  42. package/SKILLS_REGISTRY.md +0 -149
  43. package/dist/auth/account-activation.js +0 -157
  44. package/dist/auth/messages.js +0 -30
  45. package/dist/commands/build-temp.js +0 -26
  46. package/dist/commands/platform-skills.generated.js +0 -19
  47. package/dist/commands/playable.js +0 -53
  48. package/dist/commands/tools-batch.js +0 -87
  49. package/dist/commands/video.js +0 -111
  50. package/dist/commands/workspace-runtime.js +0 -54
  51. package/dist/commands/workspace.js +0 -35
  52. package/dist/project-skills/builtin-import.js +0 -225
  53. package/dist/project-skills/commands.js +0 -77
  54. package/dist/project-skills/lifecycle.js +0 -28
  55. package/dist/project-skills/local-run.js +0 -112
  56. package/dist/project-skills/local-store.js +0 -221
  57. package/dist/project-skills/messages.js +0 -80
  58. package/dist/project-skills/reconcile.js +0 -62
  59. package/dist/project-skills/remote-cache.js +0 -51
  60. package/dist/project-skills/validation.js +0 -19
  61. package/dist/recording/messages.js +0 -40
  62. package/dist/recording/project-digest.js +0 -85
  63. package/dist/recording/recorder.js +0 -293
  64. package/dist/recording/scenario.js +0 -50
  65. package/dist/skill-registry/client.js +0 -345
  66. package/dist/skill-registry/commands.js +0 -180
  67. package/dist/skill-registry/evaluation-client.js +0 -156
  68. package/dist/skill-registry/evaluation-commands.js +0 -174
  69. package/dist/skill-registry/messages.js +0 -177
  70. package/dist/skill-registry/package.js +0 -275
  71. package/dist/skill-registry/platform-package.js +0 -100
  72. package/dist/utils/environment-endpoints.js +0 -17
  73. package/dist/utils/tool-operation-journal.js +0 -124
  74. package/dist/utils/video-evidence-git.js +0 -81
  75. package/dist/video-replication/client.js +0 -222
  76. package/dist/video-replication/comparison-evidence.js +0 -149
  77. package/dist/video-replication/comparison-files.js +0 -57
  78. package/dist/video-replication/comparison-log.js +0 -72
  79. package/dist/video-replication/comparison-messages.js +0 -104
  80. package/dist/video-replication/comparison-output.js +0 -63
  81. package/dist/video-replication/comparison-preflight.js +0 -66
  82. package/dist/video-replication/comparison.js +0 -216
  83. package/dist/video-replication/evidence.js +0 -165
  84. package/dist/video-replication/media.js +0 -111
  85. package/dist/video-replication/messages.js +0 -89
  86. package/dist/workspace/messages.js +0 -35
  87. package/dist/workspace/transfer-client.js +0 -174
  88. package/dist/workspace-runtime/codex/app-server.js +0 -1040
  89. package/dist/workspace-runtime/codex/compact.js +0 -46
  90. package/dist/workspace-runtime/codex/config-toml.js +0 -107
  91. package/dist/workspace-runtime/codex/jsonrpc-stdio.js +0 -145
  92. package/dist/workspace-runtime/codex/launcher-client.js +0 -177
  93. package/dist/workspace-runtime/codex/loopback.js +0 -146
  94. package/dist/workspace-runtime/codex/mcp-config.js +0 -39
  95. package/dist/workspace-runtime/codex/model-broker.js +0 -263
  96. package/dist/workspace-runtime/codex/native-adapter.js +0 -9
  97. package/dist/workspace-runtime/codex/permissions.js +0 -70
  98. package/dist/workspace-runtime/codex/thread-image.js +0 -38
  99. package/dist/workspace-runtime/codex/ui-instructions.js +0 -4
  100. package/dist/workspace-runtime/codex/ui-tools.js +0 -183
  101. package/dist/workspace-runtime/codex/work-mode.js +0 -15
  102. package/dist/workspace-runtime/main.js +0 -10
  103. package/dist/workspace-runtime/persistence/journal.js +0 -358
  104. package/dist/workspace-runtime/persistence/native-subagents.js +0 -162
  105. package/dist/workspace-runtime/persistence/sqlite-client.js +0 -77
  106. package/dist/workspace-runtime/persistence/sqlite-worker.js +0 -1161
  107. package/dist/workspace-runtime/persistence/stopped-execution.js +0 -77
  108. package/dist/workspace-runtime/previews/output.js +0 -36
  109. package/dist/workspace-runtime/previews/publish.js +0 -262
  110. package/dist/workspace-runtime/previews/single-file.js +0 -43
  111. package/dist/workspace-runtime/processes/managed-writes.js +0 -69
  112. package/dist/workspace-runtime/production/contracts.js +0 -19
  113. package/dist/workspace-runtime/production/controller.js +0 -218
  114. package/dist/workspace-runtime/production/dependencies.js +0 -60
  115. package/dist/workspace-runtime/production/native.js +0 -212
  116. package/dist/workspace-runtime/production/processes.js +0 -102
  117. package/dist/workspace-runtime/production/save-command.js +0 -35
  118. package/dist/workspace-runtime/production/source-archive.js +0 -137
  119. package/dist/workspace-runtime/production/source-copy.js +0 -91
  120. package/dist/workspace-runtime/production/stages.js +0 -303
  121. package/dist/workspace-runtime/production/storage.js +0 -60
  122. package/dist/workspace-runtime/serve.js +0 -214
  123. package/dist/workspace-runtime/server/auth.js +0 -21
  124. package/dist/workspace-runtime/server/connectivity.js +0 -201
  125. package/dist/workspace-runtime/server/desktop-host.js +0 -429
  126. package/dist/workspace-runtime/server/desktop-preview-config.js +0 -81
  127. package/dist/workspace-runtime/server/desktop-preview-service.js +0 -157
  128. package/dist/workspace-runtime/server/desktop-preview-static.js +0 -53
  129. package/dist/workspace-runtime/server/desktop-preview.js +0 -189
  130. package/dist/workspace-runtime/server/dispatch.js +0 -1217
  131. package/dist/workspace-runtime/server/endpoints.js +0 -21
  132. package/dist/workspace-runtime/server/execution-group.js +0 -90
  133. package/dist/workspace-runtime/server/http.js +0 -1243
  134. package/dist/workspace-runtime/server/lock.js +0 -53
  135. package/dist/workspace-runtime/server/rfb-watch-gate.js +0 -168
  136. package/dist/workspace-runtime/server/source-uploader.js +0 -138
  137. package/dist/workspace-runtime/server/terminal-host.js +0 -366
  138. package/dist/workspace-runtime/server/types.js +0 -1
  139. package/dist/workspace-runtime/server/workspace-transfer.js +0 -99
  140. package/dist/workspace-runtime/workspaces/archive.js +0 -207
  141. package/dist/workspace-runtime/workspaces/artifact-events.js +0 -55
  142. package/dist/workspace-runtime/workspaces/artifact-files.js +0 -197
  143. package/dist/workspace-runtime/workspaces/artifact-receipts.js +0 -89
  144. package/dist/workspace-runtime/workspaces/artifacts.js +0 -282
  145. package/dist/workspace-runtime/workspaces/attachment-content.js +0 -123
  146. package/dist/workspace-runtime/workspaces/attachments.js +0 -185
  147. package/dist/workspace-runtime/workspaces/batch-commit.js +0 -142
  148. package/dist/workspace-runtime/workspaces/cli-context.js +0 -23
  149. package/dist/workspace-runtime/workspaces/cli-credentials.js +0 -111
  150. package/dist/workspace-runtime/workspaces/composer-context.js +0 -147
  151. package/dist/workspace-runtime/workspaces/context-error.js +0 -2
  152. package/dist/workspace-runtime/workspaces/dependencies.js +0 -264
  153. package/dist/workspace-runtime/workspaces/developer-command.js +0 -64
  154. package/dist/workspace-runtime/workspaces/directories.js +0 -133
  155. package/dist/workspace-runtime/workspaces/directory-access.js +0 -37
  156. package/dist/workspace-runtime/workspaces/directory-attachments.js +0 -245
  157. package/dist/workspace-runtime/workspaces/directory-maintenance.js +0 -111
  158. package/dist/workspace-runtime/workspaces/directory-stats.js +0 -49
  159. package/dist/workspace-runtime/workspaces/disk-snapshot.js +0 -31
  160. package/dist/workspace-runtime/workspaces/empty-template.js +0 -42
  161. package/dist/workspace-runtime/workspaces/files-search.js +0 -116
  162. package/dist/workspace-runtime/workspaces/files.js +0 -120
  163. package/dist/workspace-runtime/workspaces/git-credential-broker.js +0 -312
  164. package/dist/workspace-runtime/workspaces/git-credential-helper.js +0 -68
  165. package/dist/workspace-runtime/workspaces/git-direct-store.js +0 -155
  166. package/dist/workspace-runtime/workspaces/git.js +0 -670
  167. package/dist/workspace-runtime/workspaces/instructions.js +0 -56
  168. package/dist/workspace-runtime/workspaces/json5-edit.js +0 -170
  169. package/dist/workspace-runtime/workspaces/library-attachments.js +0 -268
  170. package/dist/workspace-runtime/workspaces/optional-platform-skills.js +0 -14
  171. package/dist/workspace-runtime/workspaces/parameters.js +0 -272
  172. package/dist/workspace-runtime/workspaces/prepare.js +0 -278
  173. package/dist/workspace-runtime/workspaces/registry.js +0 -251
  174. package/dist/workspace-runtime/workspaces/remix-references.js +0 -301
  175. package/dist/workspace-runtime/workspaces/revisions.js +0 -31
  176. package/dist/workspace-runtime/workspaces/session-image.js +0 -104
  177. package/dist/workspace-runtime/workspaces/shared-git.js +0 -40
  178. package/dist/workspace-runtime/workspaces/skill-artifact-loader.js +0 -95
  179. package/dist/workspace-runtime/workspaces/skills.js +0 -191
  180. package/dist/workspace-runtime/workspaces/transfer.js +0 -288
  181. package/dist/workspace-runtime/workspaces/turn-changes.js +0 -75
  182. package/dist/workspace-runtime/workspaces/video-attachments.js +0 -49
  183. package/project-template/.claude/skills/playcraft-replica-validation/SKILL.md +0 -25
  184. package/project-template/.claude/skills/playcraft-replica-validation/references/contracts.md +0 -79
  185. package/project-template/.claude/skills/playcraft-video-understanding/SKILL.md +0 -29
  186. package/project-template/.claude/skills/playcraft-video-understanding/references/contracts.md +0 -37
  187. package/project-template-v2/.claude/agents/artist.md +0 -82
  188. package/project-template-v2/.claude/agents/developer.md +0 -153
  189. package/project-template-v2/.claude/agents/game-designer.md +0 -264
  190. package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +0 -533
  191. package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +0 -153
  192. package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +0 -27
  193. package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +0 -208
  194. package/project-template-v2/.claude/agents/refs/atom-skill-library.md +0 -81
  195. package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +0 -432
  196. package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +0 -252
  197. package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +0 -266
  198. package/project-template-v2/.claude/agents/refs/framework-mda.md +0 -147
  199. package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +0 -123
  200. package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +0 -88
  201. package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +0 -67
  202. package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +0 -120
  203. package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +0 -300
  204. package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +0 -495
  205. package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +0 -1141
  206. package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +0 -111
  207. package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +0 -365
  208. package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +0 -412
  209. package/project-template-v2/.claude/agents/reviewer.md +0 -75
  210. package/project-template-v2/.claude/agents/technical-artist.md +0 -86
  211. package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +0 -243
  212. package/project-template-v2/.claude/hooks/user-prompt.mjs +0 -133
  213. package/project-template-v2/.claude/settings.json +0 -33
  214. package/project-template-v2/.claude/skills/brainstorming/SKILL.md +0 -161
  215. package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +0 -270
  216. package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +0 -177
  217. package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +0 -354
  218. package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +0 -148
  219. package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +0 -56
  220. package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +0 -62
  221. package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  222. package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +0 -309
  223. package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +0 -182
  224. package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +0 -123
  225. package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +0 -205
  226. package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +0 -280
  227. package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +0 -184
  228. package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +0 -306
  229. package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +0 -298
  230. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +0 -123
  231. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +0 -254
  232. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +0 -324
  233. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +0 -97
  234. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +0 -118
  235. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +0 -137
  236. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +0 -397
  237. package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +0 -296
  238. package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +0 -122
  239. package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +0 -219
  240. package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +0 -373
  241. package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +0 -161
  242. package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +0 -215
  243. package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +0 -382
  244. package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +0 -423
  245. package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +0 -158
  246. package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +0 -100
  247. package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +0 -167
  248. package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +0 -270
  249. package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +0 -236
  250. package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +0 -130
  251. package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +0 -390
  252. package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +0 -23
  253. package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +0 -39
  254. package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +0 -87
  255. package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +0 -240
  256. package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +0 -226
  257. package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +0 -308
  258. package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +0 -275
  259. package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +0 -134
  260. package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +0 -142
  261. package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +0 -153
  262. package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +0 -386
  263. package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +0 -182
  264. package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +0 -72
  265. package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +0 -121
  266. package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +0 -102
  267. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +0 -127
  268. package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +0 -106
  269. package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +0 -128
  270. package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +0 -67
  271. package/project-template-v2/.claude/snapshot.mjs +0 -263
  272. package/project-template-v2/.playcraft/skills.lock.json +0 -152
  273. package/project-template-v2/CLAUDE.md +0 -146
  274. package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
  275. package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
  276. package/project-template-v2/assets/bundles/.gitkeep +0 -0
  277. package/project-template-v2/assets/images/bg/.gitkeep +0 -0
  278. package/project-template-v2/assets/images/reference/.gitkeep +0 -0
  279. package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
  280. package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
  281. package/project-template-v2/assets/images/ui/.gitkeep +0 -0
  282. package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
  283. package/project-template-v2/assets/models/.gitkeep +0 -0
  284. package/project-template-v2/docs/harness/iteration-1/context-flow.md +0 -254
  285. package/project-template-v2/docs/harness/iteration-1/generate-flow.md +0 -91
  286. package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +0 -214
  287. package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +0 -75
  288. package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +0 -63
  289. package/project-template-v2/docs/harness/iteration-2/context-flow.md +0 -223
  290. package/project-template-v2/docs/harness/iteration-2/generate-flow.md +0 -129
  291. package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +0 -267
  292. package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +0 -164
  293. package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +0 -115
  294. package/project-template-v2/docs/harness/orchestrator-flow.md +0 -364
  295. package/project-template-v2/docs/project-state.json +0 -60
  296. package/project-template-v2/docs/project-state.md +0 -72
  297. package/project-template-v2/docs/standards/README.md +0 -225
  298. package/project-template-v2/docs/standards/agent-behavior-standards.md +0 -174
  299. package/project-template-v2/docs/standards/artifacts/design-brief.md +0 -19
  300. package/project-template-v2/docs/standards/artifacts/design.md +0 -22
  301. package/project-template-v2/docs/standards/artifacts/game-code.md +0 -41
  302. package/project-template-v2/docs/standards/artifacts/todo-list.md +0 -41
  303. package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +0 -343
  304. package/project-template-v2/game/index.ts +0 -18
  305. package/project-template-v2/globals.d.ts +0 -51
  306. package/project-template-v2/index.css +0 -34
  307. package/project-template-v2/index.html +0 -18
  308. package/project-template-v2/main.ts +0 -9
  309. package/project-template-v2/package.json +0 -46
  310. package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +0 -31
  311. package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +0 -86
  312. package/project-template-v2/skills/_shared/scripts/dod-check.mjs +0 -153
  313. package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +0 -70
  314. package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +0 -91
  315. package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +0 -131
  316. package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +0 -80
  317. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +0 -90
  318. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +0 -93
  319. package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +0 -23
  320. package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +0 -230
  321. package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +0 -58
  322. package/project-template-v2/skills/_shared/scripts/state-get.mjs +0 -339
  323. package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +0 -39
  324. package/project-template-v2/skills/_shared/scripts/state-set.mjs +0 -94
  325. package/project-template-v2/skills/_shared/scripts/state-store.mjs +0 -783
  326. package/project-template-v2/skills/_shared/scripts/todo-add.mjs +0 -88
  327. package/project-template-v2/skills/_shared/scripts/todo-get.mjs +0 -130
  328. package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +0 -33
  329. package/project-template-v2/skills/_shared/scripts/todo-set.mjs +0 -47
  330. package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +0 -268
  331. package/project-template-v2/skills/_shared/scripts/verify-env.mjs +0 -285
  332. package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +0 -161
  333. package/project-template-v2/skills/playable-autoplay/SKILL.md +0 -176
  334. package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +0 -4
  335. package/project-template-v2/skills/playable-debug/SKILL.md +0 -116
  336. package/project-template-v2/skills/playable-debug/agents/openai.yaml +0 -4
  337. package/project-template-v2/skills/playable-debug/references/debug-config.md +0 -40
  338. package/project-template-v2/skills/playable-record/SKILL.md +0 -140
  339. package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +0 -104
  340. package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +0 -141
  341. package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +0 -425
  342. package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +0 -261
  343. package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +0 -398
  344. package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +0 -322
  345. package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +0 -105
  346. package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +0 -230
  347. package/project-template-v2/skills/playable-validate/SKILL.md +0 -61
  348. package/project-template-v2/skills/playable-validate/validation-rules.md +0 -14
  349. package/project-template-v2/skills/playable-verify-ui/SKILL.md +0 -63
  350. package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +0 -1
  351. package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +0 -218
  352. package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +0 -51
  353. package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +0 -59
  354. package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +0 -106
  355. package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +0 -116
  356. package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +0 -59
  357. package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +0 -64
  358. package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +0 -58
  359. package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +0 -333
  360. package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
  361. package/project-template-v2/tsconfig.json +0 -20
  362. package/project-template-v2/vite.config.ts +0 -27
  363. package/project-templates/batch-locks/phaser.yaml +0 -922
  364. package/project-templates/batch-locks/threejs.yaml +0 -978
  365. package/project-templates/empty/AGENTS.md +0 -41
  366. 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
- commitIdentity(database, body.event);
87
- return;
88
- case 'ingest':
89
- return ingest(database, body);
90
- case 'bindTurn':
91
- return bindTurn(database, body);
92
- case 'ack':
93
- acknowledge(database, body);
94
- return;
95
- case 'createPin':
96
- return createPin(database, body);
97
- case 'deletePin':
98
- database.prepare('DELETE FROM replay_pins WHERE pin_id = ?').run(String(body.pinId));
99
- return { deleted: true };
100
- case 'reclaim':
101
- reclaim(database, Number(body.minRetainMs) || 0);
102
- return;
103
- case 'maintain':
104
- database.pragma('wal_checkpoint(PASSIVE)');
105
- return;
106
- default:
107
- throw new Error(`unknown_sqlite_command:${type}`);
108
- }
109
- });
110
- const extra = write();
111
- return { ...dump(database), ...(extra && typeof extra === 'object' ? extra : {}) };
112
- }
113
- function openDatabase(payload) {
114
- if (db)
115
- throw new Error('sqlite_already_open');
116
- const filePath = String(payload.filePath ?? '');
117
- if (!filePath)
118
- throw new Error('sqlite_path_missing');
119
- mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
120
- const database = new Database(filePath);
121
- db = database;
122
- const version = String(database.prepare('select sqlite_version() as v').get().v);
123
- if (!sqliteAtLeast(version, MIN_SQLITE)) {
124
- database.close();
125
- db = undefined;
126
- throw new Error(`sqlite_engine_too_old:${version}`);
127
- }
128
- const existing = readSchemaVersion(database);
129
- if (existing !== null && existing !== SCHEMA_VERSION) {
130
- database.close();
131
- db = undefined;
132
- throw new Error(`unsupported_runtime_state:${existing}`);
133
- }
134
- database.pragma('journal_mode = WAL');
135
- database.pragma('synchronous = FULL');
136
- database.pragma('wal_autocheckpoint = 0');
137
- database.pragma('busy_timeout = 2000');
138
- const journalMode = String(database.pragma('journal_mode', { simple: true }));
139
- const synchronous = Number(database.pragma('synchronous', { simple: true }));
140
- if (journalMode !== 'wal')
141
- throw new Error(`journal_mode_not_wal:${journalMode}`);
142
- if (synchronous !== 2)
143
- throw new Error(`synchronous_not_full:${synchronous}`);
144
- chmodSync(filePath, 0o600);
145
- let imported = false;
146
- if (existing === null) {
147
- createSchema(database);
148
- if (payload.legacy && typeof payload.legacy === 'object') {
149
- importLegacy(database, payload.legacy);
150
- imported = true;
151
- }
152
- quarantineReservedTurns(database);
153
- }
154
- else {
155
- quarantineReservedTurns(database);
156
- }
157
- database.exec(`
158
- CREATE INDEX IF NOT EXISTS native_inbox_pending_thread
159
- ON native_inbox(thread_id, ingress_sequence) WHERE converted = 0;
160
- CREATE INDEX IF NOT EXISTS records_native_session_thread
161
- ON records(json_extract(body_json, '$.threadId')) WHERE bucket = 'sessions';
162
- CREATE INDEX IF NOT EXISTS native_inbox_pending_parent
163
- ON native_inbox(COALESCE(json_extract(payload_json, '$.thread.parentThreadId'),
164
- json_extract(payload_json, '$.thread.source.subAgent.thread_spawn.parent_thread_id')))
165
- WHERE converted = 0 AND method = 'thread/started';
166
- `);
167
- return {
168
- ...dump(database),
169
- imported,
170
- engine: { version, journalMode, synchronous, binding: BINDING },
171
- };
172
- }
173
- function createSchema(database) {
174
- database.exec(`
175
- CREATE TABLE IF NOT EXISTS library_meta (
176
- key TEXT PRIMARY KEY,
177
- value TEXT NOT NULL
178
- );
179
- CREATE TABLE IF NOT EXISTS source_epochs (
180
- runtime_id TEXT NOT NULL,
181
- generation INTEGER NOT NULL,
182
- runtime_epoch INTEGER NOT NULL,
183
- next_sequence INTEGER NOT NULL,
184
- local_durable_through INTEGER NOT NULL,
185
- cloud_ack_through INTEGER NOT NULL,
186
- retained_from INTEGER NOT NULL,
187
- PRIMARY KEY (runtime_id, generation, runtime_epoch)
188
- );
189
- CREATE TABLE IF NOT EXISTS source_events (
190
- runtime_id TEXT NOT NULL,
191
- generation INTEGER NOT NULL,
192
- runtime_epoch INTEGER NOT NULL,
193
- source_sequence INTEGER NOT NULL,
194
- payload_hash TEXT NOT NULL,
195
- workspace_id TEXT,
196
- session_id TEXT,
197
- operation_id TEXT,
198
- thread_id TEXT,
199
- turn_id TEXT,
200
- item_id TEXT,
201
- type TEXT NOT NULL,
202
- payload_json TEXT NOT NULL,
203
- occurred_at TEXT NOT NULL,
204
- PRIMARY KEY (runtime_id, generation, runtime_epoch, source_sequence)
205
- );
206
- CREATE TABLE IF NOT EXISTS native_inbox (
207
- ingress_sequence INTEGER PRIMARY KEY,
208
- runtime_epoch INTEGER NOT NULL,
209
- kind TEXT NOT NULL,
210
- method TEXT,
211
- thread_id TEXT,
212
- turn_id TEXT,
213
- item_id TEXT,
214
- payload_json TEXT NOT NULL,
215
- converted INTEGER NOT NULL DEFAULT 0,
216
- source_sequence INTEGER,
217
- received_at TEXT NOT NULL
218
- );
219
- CREATE TABLE IF NOT EXISTS operation_intents (
220
- scope_key TEXT PRIMARY KEY,
221
- operation_json TEXT NOT NULL,
222
- gateway_operation_id TEXT,
223
- sandbox_operation_id TEXT NOT NULL,
224
- payload_hash TEXT NOT NULL,
225
- native_request_id TEXT,
226
- thread_id TEXT,
227
- turn_id TEXT,
228
- reconcile_status TEXT NOT NULL
229
- );
230
- CREATE TABLE IF NOT EXISTS replay_pins (
231
- pin_id TEXT PRIMARY KEY,
232
- epochs_json TEXT NOT NULL,
233
- created_at TEXT NOT NULL,
234
- expires_at TEXT NOT NULL
235
- );
236
- CREATE TABLE IF NOT EXISTS records (
237
- bucket TEXT NOT NULL,
238
- record_key TEXT NOT NULL,
239
- body_json TEXT NOT NULL,
240
- PRIMARY KEY (bucket, record_key)
241
- );
242
- `);
243
- setMeta(database, 'schema_version', String(SCHEMA_VERSION));
244
- setMeta(database, 'migration_status', 'complete');
245
- setMeta(database, 'source_read_token', randomBytes(32).toString('base64url'));
246
- setMeta(database, 'accepted_lease_fence', '0');
247
- setMeta(database, 'destroyed', '0');
248
- setMeta(database, 'runtime_epoch', '0');
249
- setMeta(database, 'ingress_counter', '0');
250
- setMeta(database, 'execution_group', JSON.stringify({ stopConfirmed: false, replaced: false }));
251
- }
252
- function readSchemaVersion(database) {
253
- const table = database
254
- .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'library_meta'`)
255
- .get();
256
- if (!table)
257
- return null;
258
- const row = database.prepare(`SELECT value FROM library_meta WHERE key = 'schema_version'`).get();
259
- if (!row?.value)
260
- return null;
261
- const parsed = Number(row.value);
262
- return Number.isInteger(parsed) ? parsed : null;
263
- }
264
- function importLegacy(database, legacy) {
265
- const snapshot = asRecord(legacy);
266
- const operations = asRecord(snapshot.operations);
267
- for (const [scopeKey, value] of Object.entries(operations)) {
268
- const parsed = operationRecordSchema.safeParse(value);
269
- if (!parsed.success)
270
- continue;
271
- putOperation(database, scopeKey, parsed.data);
272
- }
273
- const events = asRecord(snapshot.events);
274
- const sequences = [];
275
- for (const value of Object.values(events)) {
276
- const parsed = pendingSourceEventSchema.safeParse(value);
277
- if (!parsed.success)
278
- continue;
279
- const hash = hashOperationPayload(parsed.data.payload);
280
- if (hash !== parsed.data.payloadHash)
281
- continue;
282
- insertEventRow(database, parsed.data, {});
283
- sequences.push(parsed.data.source.sourceSequence);
284
- }
285
- const declared = Number(snapshot.lastSourceSequence) || 0;
286
- const maxImported = sequences.reduce((max, value) => Math.max(max, value), 0);
287
- const counter = Math.max(declared, maxImported, 0);
288
- const runtimeId = firstString(snapshot, 'runtimeId') || eventRuntime(events, 'runtimeId');
289
- const generation = Number.isInteger(snapshot.generation) ? Number(snapshot.generation) : eventRuntimeNumber(events, 'generation');
290
- const runtimeEpoch = Number.isInteger(snapshot.runtimeEpoch)
291
- ? Number(snapshot.runtimeEpoch)
292
- : eventRuntimeNumber(events, 'runtimeEpoch');
293
- if (runtimeId) {
294
- ensureEpoch(database, runtimeId, generation, runtimeEpoch);
295
- database
296
- .prepare(`UPDATE source_epochs SET next_sequence = ?, local_durable_through = ?
297
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
298
- .run(counter, counter, runtimeId, generation, runtimeEpoch);
299
- setMeta(database, 'runtime_id', runtimeId);
300
- setMeta(database, 'generation', String(generation));
301
- }
302
- importBucket(database, 'workspaces', asRecord(snapshot.workspaces), (value) => workspaceRegistrationSchema.safeParse(value).success);
303
- importBucket(database, 'sessions', asRecord(snapshot.sessions), (value) => {
304
- const session = asRecord(value);
305
- return typeof session.sessionId === 'string' && typeof session.threadId === 'string';
306
- });
307
- importBucket(database, 'terminals', asRecord(snapshot.terminals), (value) => terminalRecordSchema.safeParse(value).success);
308
- importBucket(database, 'approvals', asRecord(snapshot.approvals), (value) => isApproval(value));
309
- importBucket(database, 'managed_writes', asRecord(snapshot.managedWrites), () => true);
310
- importBucket(database, 'file_revisions', asRecord(snapshot.fileRevisions), () => true);
311
- importBucket(database, 'receipts', asRecord(snapshot.receipts), () => true);
312
- const workspaceFiles = asRecord(snapshot.workspaceFileRevisions);
313
- for (const [workspaceId, files] of Object.entries(workspaceFiles)) {
314
- importBucket(database, `workspace_files:${workspaceId}`, asRecord(files), () => true);
315
- }
316
- setMeta(database, 'accepted_lease_fence', String(Number(snapshot.acceptedLeaseFence) || 0));
317
- setMeta(database, 'destroyed', snapshot.destroyed === true ? '1' : '0');
318
- setMeta(database, 'runtime_epoch', String(runtimeEpoch));
319
- if (snapshot.executionGroup && typeof snapshot.executionGroup === 'object') {
320
- setMeta(database, 'execution_group', JSON.stringify(snapshot.executionGroup));
321
- }
322
- setMeta(database, 'migration_status', 'complete');
323
- }
324
- function quarantineReservedTurns(database) {
325
- const rows = database.prepare('SELECT scope_key, operation_json FROM operation_intents').all();
326
- for (const row of rows) {
327
- const parsed = operationRecordSchema.safeParse(JSON.parse(row.operation_json));
328
- if (!parsed.success)
329
- continue;
330
- const operation = parsed.data;
331
- if (!['session.turn', 'session.review', 'session.compact', 'session.steer'].includes(operation.kind) || operation.status !== 'dispatched')
332
- continue;
333
- if (!operation.nativeRequestId || (operation.kind !== 'session.steer' && operation.nativeTurnId))
334
- continue;
335
- const uncertain = transitionOperation(operation, 'uncertain', { error: 'native_receipt_lost' });
336
- putOperation(database, row.scope_key, uncertain);
337
- if (operation.sessionId && operation.kind !== 'session.steer') {
338
- const session = readSession(database, operation.sessionId);
339
- if (session) {
340
- upsertRecord(database, 'sessions', operation.sessionId, { ...session, turnBlock: 'uncertain', turnStatus: 'starting' });
341
- }
342
- }
343
- }
344
- }
345
- function allocateEvent(database, body) {
346
- const runtimeId = String(body.runtimeId);
347
- const generation = Number(body.generation);
348
- const runtimeEpoch = Number(body.runtimeEpoch);
349
- ensureEpoch(database, runtimeId, generation, runtimeEpoch);
350
- const allocated = database
351
- .prepare(`UPDATE source_epochs
352
- SET next_sequence = next_sequence + 1,
353
- local_durable_through = next_sequence + 1
354
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?
355
- RETURNING next_sequence`)
356
- .get(runtimeId, generation, runtimeEpoch);
357
- const payload = body.payload ?? {};
358
- const event = pendingSourceEventSchema.parse({
359
- source: { runtimeId, generation, runtimeEpoch, sourceSequence: allocated.next_sequence },
360
- payloadHash: hashOperationPayload(payload),
361
- type: String(body.type),
362
- payload,
363
- occurredAt: String(body.occurredAt),
364
- });
365
- insertEventRow(database, event, {
366
- workspaceId: optionalString(body.workspaceId),
367
- sessionId: optionalString(body.sessionId),
368
- operationId: optionalString(body.operationId),
369
- threadId: optionalString(body.threadId),
370
- turnId: optionalString(body.turnId),
371
- itemId: optionalString(body.itemId),
372
- });
373
- return { committedEvents: [event] };
374
- }
375
- function commitIdentity(database, value) {
376
- const event = pendingSourceEventSchema.parse(value);
377
- const existing = database
378
- .prepare(`SELECT payload_hash, payload_json, type, occurred_at FROM source_events
379
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence = ?`)
380
- .get(event.source.runtimeId, event.source.generation, event.source.runtimeEpoch, event.source.sourceSequence);
381
- if (existing) {
382
- if (existing.payload_hash !== event.payloadHash)
383
- throw new Error('source_payload_mismatch');
384
- return { committedEvents: [] };
385
- }
386
- ensureEpoch(database, event.source.runtimeId, event.source.generation, event.source.runtimeEpoch);
387
- insertEventRow(database, event, {});
388
- database
389
- .prepare(`UPDATE source_epochs
390
- SET next_sequence = CASE WHEN next_sequence < ? THEN ? ELSE next_sequence END,
391
- local_durable_through = CASE WHEN local_durable_through < ? THEN ? ELSE local_durable_through END
392
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
393
- .run(event.source.sourceSequence, event.source.sourceSequence, event.source.sourceSequence, event.source.sourceSequence, event.source.runtimeId, event.source.generation, event.source.runtimeEpoch);
394
- return { committedEvents: [event] };
395
- }
396
- function ingest(database, body) {
397
- const native = nativeIds(body.params);
398
- const threadId = optionalString(body.threadId) || native.threadId;
399
- const turnId = optionalString(body.turnId) || native.turnId;
400
- const itemId = optionalString(body.itemId) || native.itemId;
401
- const ingress = nextIngress(database);
402
- const kind = String(body.kind);
403
- const method = optionalString(body.method);
404
- const hintedTurn = turnId || nativeIds(body.params).turnId;
405
- database
406
- .prepare(`INSERT INTO native_inbox (
407
- ingress_sequence, runtime_epoch, kind, method, thread_id, turn_id, item_id, payload_json, converted, received_at
408
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`)
409
- .run(ingress, Number(body.runtimeEpoch) || 0, kind, method ?? null, threadId ?? null, hintedTurn ?? null, itemId ?? null, JSON.stringify(body.params ?? {}), String(body.occurredAt));
410
- return { committedEvents: releaseInbox(database, { ingress }, { runtimeId: String(body.runtimeId), generation: Number(body.generation) || 0 }) };
411
- }
412
- function bindTurn(database, body) {
413
- const operation = operationRecordSchema.parse(body.operation);
414
- putOperation(database, String(body.scopeKey), operation);
415
- if (operation.generation === metaNumber(database, 'generation') && operation.runtimeEpoch !== undefined &&
416
- stoppedExecutionEpochs(database, metaString(database, 'runtime_id'), operation.generation).includes(operation.runtimeEpoch)) {
417
- const released = releaseInbox(database, { rootThreadId: String(body.threadId), turnId: String(body.turnId) });
418
- applyRecordedTurn(database, String(body.threadId), String(body.turnId));
419
- return { committedEvents: [...released, ...settleStoppedExecution(database, {
420
- runtimeId: metaString(database, 'runtime_id'), generation: operation.generation, runtimeEpoch: operation.runtimeEpoch,
421
- }, Number(executionGroup(database).previousPid ?? executionGroup(database).pid), new Date().toISOString(), stopWriter(database))] };
422
- }
423
- const sessionId = String(body.sessionId);
424
- const session = readSession(database, sessionId);
425
- if (session) {
426
- upsertRecord(database, 'sessions', sessionId, {
427
- ...session,
428
- turnStatus: 'running',
429
- turnBlock: undefined,
430
- activeTurnId: String(body.turnId),
431
- threadId: String(body.threadId),
432
- recoveryContextPending: undefined,
433
- });
434
- }
435
- const threadId = String(body.threadId);
436
- const turnId = String(body.turnId);
437
- const released = releaseInbox(database, { rootThreadId: threadId, turnId });
438
- applyRecordedTurn(database, threadId, turnId);
439
- return { committedEvents: released };
440
- }
441
- function releaseInbox(database, selection, identity) {
442
- const events = [];
443
- const stoppedProcesses = [];
444
- const rows = [];
445
- const queued = new Set();
446
- const enqueue = (pending) => {
447
- for (const row of pending) {
448
- const ingress = Number(row.ingress_sequence);
449
- if (!queued.has(ingress)) {
450
- queued.add(ingress);
451
- rows.push(row);
452
- }
453
- }
454
- };
455
- const enqueueChildren = (parentId) => enqueue(database.prepare(`
456
- SELECT * FROM native_inbox WHERE converted = 0 AND method = 'thread/started'
457
- AND COALESCE(json_extract(payload_json, '$.thread.parentThreadId'),
458
- json_extract(payload_json, '$.thread.source.subAgent.thread_spawn.parent_thread_id')) = ?
459
- ORDER BY ingress_sequence
460
- `).all(parentId));
461
- const enqueueThread = (threadId) => {
462
- enqueue(database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND thread_id = ? ORDER BY ingress_sequence').all(threadId));
463
- enqueueChildren(threadId);
464
- };
465
- if ('ingress' in selection) {
466
- const current = database.prepare('SELECT * FROM native_inbox WHERE converted = 0 AND ingress_sequence = ?').get(selection.ingress);
467
- if (current) {
468
- const threadId = optionalString(current.thread_id), turnId = optionalString(current.turn_id);
469
- if (nativeEventOwner(database, threadId, turnId)) {
470
- // A receipt can bind between notifications. Drain that thread's earlier events first.
471
- 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')
472
- .all(threadId, turnId ?? null, selection.ingress));
473
- }
474
- else
475
- enqueue([current]);
476
- }
477
- }
478
- else {
479
- 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')
480
- .all(selection.rootThreadId, selection.turnId));
481
- enqueueChildren(selection.rootThreadId);
482
- for (const threadId of nativeSubagentThreads(database, selection.rootThreadId))
483
- enqueueThread(threadId);
484
- }
485
- for (const stored of rows) {
486
- const threadId = optionalString(stored.thread_id);
487
- const turnId = optionalString(stored.turn_id);
488
- const method = optionalString(stored.method);
489
- const params = JSON.parse(String(stored.payload_json || '{}'));
490
- const stopped = stored.kind === 'process_exit' && Number(asRecord(params).pid) === executionGroup(database).pid &&
491
- Number(stored.runtime_epoch) === metaNumber(database, 'runtime_epoch') &&
492
- (!identity || (identity.runtimeId === metaString(database, 'runtime_id') && identity.generation === metaNumber(database, 'generation')));
493
- if (stopped)
494
- for (const child of clearPendingNativeFollowups(database))
495
- enqueueThread(child);
496
- for (const child of learnNativeSubagents(database, method ?? '', params, threadId, turnId))
497
- enqueueThread(child);
498
- const owner = nativeEventOwner(database, threadId, turnId);
499
- if (stored.kind !== 'process_exit' && !owner) {
500
- queued.delete(Number(stored.ingress_sequence));
501
- continue;
502
- }
503
- const event = convertInboxRow(database, Number(stored.ingress_sequence), {
504
- runtimeId: identity?.runtimeId ?? (metaString(database, 'runtime_id') || 'runtime'),
505
- generation: identity?.generation ?? metaNumber(database, 'generation'),
506
- runtimeEpoch: Number(stored.runtime_epoch),
507
- kind: String(stored.kind), method, threadId, turnId,
508
- itemId: optionalString(stored.item_id), occurredAt: String(stored.received_at),
509
- session: owner?.session, operation: owner?.operation, subAgent: owner?.subAgent, params,
510
- });
511
- if (event)
512
- events.push(event);
513
- if (stopped)
514
- stoppedProcesses.push({ runtimeEpoch: Number(stored.runtime_epoch), pid: Number(asRecord(params).pid), occurredAt: String(stored.received_at) });
515
- if (!owner?.subAgent && threadId && terminalTurn(method ?? '', asRecord(params)))
516
- for (const child of nativeSubagentThreads(database, threadId))
517
- enqueueThread(child);
518
- }
519
- // Release captured child events first so their real final messages precede synthetic interruption.
520
- for (const stopped of stoppedProcesses)
521
- events.push(...settleStoppedExecution(database, {
522
- runtimeId: metaString(database, 'runtime_id'), generation: metaNumber(database, 'generation'), runtimeEpoch: stopped.runtimeEpoch,
523
- }, stopped.pid, stopped.occurredAt, stopWriter(database)));
524
- return events;
525
- }
526
- function convertInboxRow(database, ingress, input) {
527
- const row = database.prepare('SELECT payload_json, converted FROM native_inbox WHERE ingress_sequence = ?').get(ingress);
528
- if (!row || row.converted)
529
- return undefined;
530
- const params = input.params ?? JSON.parse(row.payload_json || '{}');
531
- const payload = linkedPayload(params, input.session, input.subAgent);
532
- const nativeApprovalId = optionalString(payload.approvalId) || optionalString(payload.itemId) || input.itemId;
533
- const approvalRequest = input.method?.includes('requestApproval') || input.method === 'item/tool/requestUserInput';
534
- const stoppedEpoch = stoppedExecutionEpochs(database, input.runtimeId, input.generation).includes(input.runtimeEpoch);
535
- const ownerOperation = input.operation?.operation;
536
- const currentSource = input.runtimeId === metaString(database, 'runtime_id') && input.generation === metaNumber(database, 'generation') && input.runtimeEpoch === metaNumber(database, 'runtime_epoch');
537
- const ownerMatchesSource = ownerOperation && (ownerOperation.generation === undefined || ownerOperation.generation === input.generation) &&
538
- (ownerOperation.runtimeEpoch === undefined || ownerOperation.runtimeEpoch === input.runtimeEpoch) &&
539
- (currentSource || (ownerOperation.generation !== undefined && ownerOperation.runtimeEpoch !== undefined));
540
- const childApproval = input.subAgent && nativeApprovalId && approvalRequest && !stoppedEpoch && ownerMatchesSource;
541
- if (input.subAgent && nativeApprovalId && (approvalRequest || input.method === 'approval.resolved' || input.method === 'serverRequest/resolved')) {
542
- payload.approvalId = `subagent_${createHash('sha256').update(JSON.stringify([input.subAgent.threadId, nativeApprovalId])).digest('hex')}`;
543
- }
544
- const type = input.kind === 'process_exit' ? 'process.exited' : input.method || input.kind;
545
- const allocated = allocateEvent(database, {
546
- runtimeId: input.runtimeId || metaString(database, 'runtime_id'),
547
- generation: input.generation,
548
- runtimeEpoch: input.runtimeEpoch,
549
- type,
550
- payload,
551
- occurredAt: input.occurredAt,
552
- workspaceId: optionalString(input.session?.workspaceId),
553
- sessionId: optionalString(input.session?.sessionId),
554
- operationId: input.operation?.operation.operationId,
555
- threadId: input.threadId,
556
- turnId: input.turnId,
557
- itemId: input.itemId,
558
- });
559
- const event = allocated.committedEvents[0];
560
- if (!event)
561
- return undefined;
562
- database
563
- .prepare(`UPDATE native_inbox
564
- SET converted = 1, source_sequence = ?, payload_json = '{}'
565
- WHERE ingress_sequence = ?`)
566
- .run(event.source.sourceSequence, ingress);
567
- if (childApproval && input.operation?.operation.sessionId) {
568
- upsertRecord(database, 'approvals', `${input.operation.operation.sessionId}:${String(payload.approvalId)}`, {
569
- approvalId: payload.approvalId,
570
- sessionId: input.operation.operation.sessionId,
571
- runtimeId: input.runtimeId,
572
- generation: input.generation,
573
- runtimeEpoch: input.runtimeEpoch,
574
- nativeRequestId: optionalString(payload.requestId) ?? `approval:${nativeApprovalId}`,
575
- nativeApprovalId,
576
- nativeThreadId: input.subAgent.threadId,
577
- nativeTurnId: input.turnId,
578
- rootThreadId: input.subAgent.rootThreadId,
579
- rootTurnId: input.operation.operation.nativeTurnId,
580
- method: input.method,
581
- request: payload,
582
- status: 'pending',
583
- });
584
- }
585
- settleNativeApprovals(database, input, payload);
586
- if (!input.subAgent)
587
- applyNativeToState(database, { ...input, operation: resolveOperation(database, input, payload) }, payload);
588
- return event;
589
- }
590
- /** An ended native request must never reappear as a pending approval after reconnect. */
591
- function settleNativeApprovals(database, input, payload) {
592
- const resolved = input.method === 'approval.resolved' || input.method === 'serverRequest/resolved';
593
- const terminal = Boolean(terminalTurn(input.method ?? '', payload));
594
- if (!resolved && !terminal)
595
- return;
596
- const rows = database.prepare("SELECT record_key, body_json FROM records WHERE bucket = 'approvals'").all();
597
- for (const row of rows) {
598
- const approval = asRecord(JSON.parse(row.body_json));
599
- if (approval.status !== 'pending')
600
- continue;
601
- if (!input.runtimeId || input.generation === undefined || input.runtimeEpoch === undefined ||
602
- approval.runtimeId !== input.runtimeId || approval.generation !== input.generation || approval.runtimeEpoch !== input.runtimeEpoch)
603
- continue;
604
- const matchesThread = approval.nativeThreadId === input.threadId;
605
- const matchesRequest = (payload.approvalId && approval.approvalId === payload.approvalId)
606
- || (payload.requestId !== undefined && String(approval.nativeRequestId) === String(payload.requestId));
607
- const endedParent = terminal && approval.rootThreadId === input.threadId && approval.rootTurnId === input.turnId;
608
- if (!endedParent && (!matchesThread || (resolved ? !matchesRequest
609
- : input.turnId && approval.nativeTurnId && approval.nativeTurnId !== input.turnId)))
610
- continue;
611
- upsertRecord(database, 'approvals', row.record_key, { ...approval,
612
- status: input.method === 'approval.resolved' ? payload.accepted === false ? 'rejected' : 'approved' : 'expired' });
613
- }
614
- }
615
- function applyRecordedTurn(database, threadId, turnId) {
616
- const rows = database
617
- .prepare(`SELECT type, payload_json, thread_id, session_id, runtime_id, generation, runtime_epoch FROM source_events
618
- WHERE (thread_id = ? OR thread_id IS NULL) AND (turn_id = ? OR payload_json LIKE ?)
619
- ORDER BY source_sequence`)
620
- .all(threadId, turnId, `%"${turnId}"%`);
621
- for (const row of rows) {
622
- 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') {
623
- continue;
624
- }
625
- const payload = asRecord(JSON.parse(row.payload_json));
626
- if (payload.subAgent)
627
- continue;
628
- const session = sessionByThread(database, threadId) ?? (row.session_id ? readSession(database, row.session_id) : undefined);
629
- applyNativeToState(database, {
630
- method: row.type,
631
- kind: 'notification',
632
- runtimeId: row.runtime_id,
633
- generation: row.generation,
634
- runtimeEpoch: row.runtime_epoch,
635
- threadId: optionalString(row.thread_id) || threadId,
636
- turnId,
637
- session,
638
- operation: operationByTurn(database, turnId, threadId),
639
- }, payload);
640
- }
641
- }
642
- function resolveOperation(database, input, payload) {
643
- const turnId = input.turnId || nativeIds(payload).turnId;
644
- if (turnId)
645
- return input.operation ?? operationByTurn(database, turnId, input.threadId ?? nativeIds(payload).threadId);
646
- return input.operation;
647
- }
648
- function applyNativeToState(database, input, payload) {
649
- const method = input.method ?? '';
650
- settleNativeApprovals(database, input, payload);
651
- const currentSource = input.runtimeId === metaString(database, 'runtime_id') && input.generation === metaNumber(database, 'generation') &&
652
- input.runtimeEpoch === metaNumber(database, 'runtime_epoch');
653
- const operation = input.operation?.operation;
654
- const operationMatchesSource = !operation || ((operation.generation === undefined || operation.generation === input.generation) &&
655
- (operation.runtimeEpoch === undefined || operation.runtimeEpoch === input.runtimeEpoch) &&
656
- (currentSource || (operation.generation !== undefined && operation.runtimeEpoch !== undefined)));
657
- if (!operationMatchesSource)
658
- return;
659
- const session = input.session;
660
- // A late old-turn notification is history, not the state of a newer turn on this thread.
661
- if (currentSource && session?.sessionId && (!input.turnId || !session.activeTurnId || input.turnId === session.activeTurnId)) {
662
- const next = { ...session };
663
- const approval = method.includes('requestApproval') || method.includes('requestUserInput');
664
- if (approval)
665
- next.turnStatus = moveTurn(String(session.turnStatus ?? 'running'), 'waiting_for_user');
666
- else if (method === 'approval.resolved' || method === 'serverRequest/resolved') {
667
- 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")
668
- .get(session.sessionId, session.threadId);
669
- if (!waiting)
670
- next.turnStatus = moveTurn(String(session.turnStatus ?? 'waiting_for_user'), 'running');
671
- }
672
- else if (method.includes('delta') || method === 'turn/started' || method === 'turn.started') {
673
- const current = String(session.turnStatus ?? 'starting');
674
- if (current !== 'waiting_for_user')
675
- next.turnStatus = moveTurn(current, 'running');
676
- }
677
- const terminal = terminalTurn(method, payload);
678
- if (terminal) {
679
- let status = String(next.turnStatus ?? session.turnStatus ?? 'running');
680
- if (status === 'waiting_for_user')
681
- status = moveTurn(status, 'running');
682
- next.turnStatus = moveTurn(status, terminal);
683
- next.turnBlock = undefined;
684
- }
685
- upsertRecord(database, 'sessions', String(session.sessionId), next);
686
- }
687
- if (!input.operation)
688
- return;
689
- let ownedOperation = input.operation.operation;
690
- const approvalId = optionalString(payload.approvalId) || optionalString(payload.itemId) || input.itemId;
691
- if ((method.includes('requestApproval') || method.includes('requestUserInput')) && approvalId && ownedOperation.sessionId && currentSource &&
692
- !['completed', 'failed', 'interrupted'].includes(ownedOperation.status)) {
693
- upsertRecord(database, 'approvals', `${ownedOperation.sessionId}:${approvalId}`, {
694
- approvalId,
695
- sessionId: ownedOperation.sessionId,
696
- runtimeId: input.runtimeId,
697
- generation: input.generation,
698
- runtimeEpoch: input.runtimeEpoch,
699
- nativeRequestId: optionalString(payload.requestId) ?? `approval:${approvalId}`,
700
- nativeThreadId: input.threadId,
701
- nativeTurnId: input.turnId,
702
- nativeApprovalId: approvalId,
703
- method,
704
- request: payload,
705
- status: 'pending',
706
- });
707
- ownedOperation = operationRecordSchema.parse({
708
- ...ownedOperation,
709
- nativeApprovalId: approvalId,
710
- updatedAt: new Date().toISOString(),
711
- });
712
- putOperation(database, input.operation.scopeKey, ownedOperation);
713
- }
714
- const terminal = terminalTurn(method, payload);
715
- if (terminal && ownedOperation.status === 'accepted') {
716
- putOperation(database, input.operation.scopeKey, transitionOperation(ownedOperation, terminal, {
717
- nativeTurnId: input.turnId ?? ownedOperation.nativeTurnId,
718
- nativeThreadId: input.threadId ?? ownedOperation.nativeThreadId,
719
- }));
720
- }
721
- }
722
- function acknowledge(database, body) {
723
- const leaseFence = Number(body.leaseFence);
724
- const accepted = metaNumber(database, 'accepted_lease_fence');
725
- if (!Number.isInteger(leaseFence) || leaseFence < accepted)
726
- throw new Error('lease_fence_mismatch');
727
- const epochs = Array.isArray(body.epochs) ? body.epochs : [];
728
- for (const value of epochs) {
729
- const epoch = asRecord(value);
730
- if (epoch.runtimeId !== metaString(database, 'runtime_id') || Number(epoch.generation) !== metaNumber(database, 'generation'))
731
- throw new Error('source_identity_mismatch');
732
- const row = epochRow(database, String(epoch.runtimeId), Number(epoch.generation), Number(epoch.runtimeEpoch));
733
- if (!row)
734
- throw new Error('unknown_source_epoch');
735
- const next = Number(epoch.confirmedThrough);
736
- if (!Number.isInteger(next) || next < row.cloud_ack_through)
737
- throw new Error('ack_not_monotonic');
738
- if (next > row.local_durable_through)
739
- throw new Error('ack_ahead_of_local');
740
- database
741
- .prepare(`UPDATE source_epochs SET cloud_ack_through = ?
742
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
743
- .run(next, epoch.runtimeId, epoch.generation, epoch.runtimeEpoch);
744
- }
745
- // An archive acknowledgement is a control-authenticated write; adopting its fence creates no task.
746
- setMeta(database, 'accepted_lease_fence', String(leaseFence));
747
- }
748
- function createPin(database, body) {
749
- const now = Date.now();
750
- database.prepare('DELETE FROM replay_pins WHERE expires_at <= ?').run(new Date(now).toISOString());
751
- const count = database.prepare('SELECT count(*) AS n FROM replay_pins').get().n;
752
- if (count >= MAX_PINS)
753
- throw new Error('pin_quota');
754
- const ttl = Math.min(Number(body.ttlMs) || 60_000, MAX_PIN_MS);
755
- const identity = asRecord(body.identity);
756
- ensureEpoch(database, String(identity.runtimeId), Number(identity.generation), Number(identity.runtimeEpoch));
757
- const epochs = database.prepare('SELECT * FROM source_epochs').all().map((row) => ({
758
- runtimeId: row.runtime_id,
759
- generation: row.generation,
760
- runtimeEpoch: row.runtime_epoch,
761
- retainedFrom: row.retained_from,
762
- highWater: row.local_durable_through,
763
- }));
764
- const current = epochs.find((epoch) => epoch.runtimeId === identity.runtimeId &&
765
- epoch.generation === Number(identity.generation) &&
766
- epoch.runtimeEpoch === Number(identity.runtimeEpoch));
767
- const pinId = `pin_${randomUUID()}`;
768
- const createdAt = new Date(now).toISOString();
769
- const expiresAt = new Date(now + ttl).toISOString();
770
- database
771
- .prepare('INSERT INTO replay_pins (pin_id, epochs_json, created_at, expires_at) VALUES (?, ?, ?, ?)')
772
- .run(pinId, JSON.stringify(epochs), createdAt, expiresAt);
773
- const highWater = current?.highWater ?? 0;
774
- return {
775
- pin: {
776
- pinId,
777
- epochs,
778
- followBoundary: {
779
- runtimeId: String(identity.runtimeId),
780
- generation: Number(identity.generation),
781
- runtimeEpoch: Number(identity.runtimeEpoch),
782
- sourceSequence: Math.max(highWater, 1),
783
- },
784
- },
785
- };
786
- }
787
- function reclaim(database, minRetainMs) {
788
- const pins = activePins(database);
789
- const epochs = database.prepare('SELECT * FROM source_epochs').all();
790
- const now = Date.now();
791
- for (const epoch of epochs) {
792
- let retained = epoch.retained_from;
793
- while (true) {
794
- const row = database
795
- .prepare(`SELECT source_sequence, occurred_at FROM source_events
796
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence >= ?
797
- ORDER BY source_sequence LIMIT 1`)
798
- .get(epoch.runtime_id, epoch.generation, epoch.runtime_epoch, retained);
799
- if (!row || row.source_sequence !== retained)
800
- break;
801
- if (row.source_sequence > epoch.cloud_ack_through)
802
- break;
803
- 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)) {
804
- break;
805
- }
806
- const age = now - Date.parse(row.occurred_at);
807
- if (Number.isFinite(age) && age < minRetainMs)
808
- break;
809
- database
810
- .prepare(`DELETE FROM source_events
811
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ? AND source_sequence = ?`)
812
- .run(epoch.runtime_id, epoch.generation, epoch.runtime_epoch, row.source_sequence);
813
- retained = row.source_sequence + 1;
814
- }
815
- if (retained !== epoch.retained_from) {
816
- database
817
- .prepare(`UPDATE source_epochs SET retained_from = ?
818
- WHERE runtime_id = ? AND generation = ? AND runtime_epoch = ?`)
819
- .run(retained, epoch.runtime_id, epoch.generation, epoch.runtime_epoch);
820
- }
821
- }
822
- }
823
- function dump(database) {
824
- const snapshot = {
825
- workspaces: bucketObject(database, 'workspaces'),
826
- workspaceFileRevisions: workspaceFiles(database),
827
- operations: operationsObject(database),
828
- receipts: bucketObject(database, 'receipts'),
829
- 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
- }