@playdrop/playdrop-cli 0.16.16 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/client-meta.json +4 -4
- package/dist/apiClient.d.ts +1 -2
- package/dist/apiClient.js +1 -16
- package/dist/appUrls.js +4 -0
- package/dist/apps/build.d.ts +12 -4
- package/dist/apps/build.js +55 -22
- package/dist/apps/index.d.ts +2 -1
- package/dist/apps/index.js +4 -1
- package/dist/apps/loadCheck.d.ts +5 -1
- package/dist/apps/loadCheck.js +52 -18
- package/dist/apps/serverBuild.d.ts +17 -0
- package/dist/apps/serverBuild.js +155 -0
- package/dist/apps/staticHtml.d.ts +1 -0
- package/dist/apps/staticHtml.js +19 -10
- package/dist/apps/upload.d.ts +12 -2
- package/dist/apps/upload.js +127 -46
- package/dist/catalogue.d.ts +23 -1
- package/dist/catalogue.js +126 -100
- package/dist/commandContext.d.ts +6 -3
- package/dist/commandContext.js +108 -18
- package/dist/commands/build.js +0 -7
- package/dist/commands/captureListing.d.ts +3 -0
- package/dist/commands/captureListing.js +48 -4
- package/dist/commands/chat.d.ts +4 -0
- package/dist/commands/chat.js +133 -8
- package/dist/commands/check.js +34 -1
- package/dist/commands/create.js +30 -263
- package/dist/commands/dev.js +37 -3
- package/dist/commands/devBrowser.js +2 -2
- package/dist/commands/devGameServer.d.ts +16 -0
- package/dist/commands/devGameServer.js +237 -0
- package/dist/commands/devRuntimeAssets.d.ts +1 -0
- package/dist/commands/devRuntimeAssets.js +2 -0
- package/dist/commands/devServer.d.ts +3 -0
- package/dist/commands/devServer.js +111 -5
- package/dist/commands/generation.d.ts +33 -4
- package/dist/commands/generation.js +304 -115
- package/dist/commands/login.js +23 -7
- package/dist/commands/review.d.ts +1 -1
- package/dist/commands/review.js +68 -95
- package/dist/commands/source.d.ts +19 -0
- package/dist/commands/source.js +142 -0
- package/dist/commands/tweaks.js +5 -11
- package/dist/commands/upload-content.d.ts +1 -1
- package/dist/commands/upload-content.js +16 -12
- package/dist/commands/upload.d.ts +17 -1
- package/dist/commands/upload.js +47 -10
- package/dist/commands/worker/archive-staging.d.ts +1 -0
- package/dist/commands/worker/archive-staging.js +13 -2
- package/dist/commands/worker/e2e-fixtures.d.ts +32 -0
- package/dist/commands/worker/e2e-fixtures.js +466 -0
- package/dist/commands/worker/game-server-infra.d.ts +80 -0
- package/dist/commands/worker/game-server-infra.js +595 -0
- package/dist/commands/worker/game-source-git.d.ts +126 -0
- package/dist/commands/worker/game-source-git.js +780 -0
- package/dist/commands/worker/generation.d.ts +94 -0
- package/dist/commands/worker/generation.js +1458 -0
- package/dist/commands/worker/runtime.d.ts +8 -10
- package/dist/commands/worker/runtime.js +51 -45
- package/dist/commands/worker.d.ts +79 -19
- package/dist/commands/worker.js +1796 -419
- package/dist/config.d.ts +2 -0
- package/dist/config.js +16 -3
- package/dist/environment.d.ts +0 -1
- package/dist/environment.js +0 -8
- package/dist/index.js +92 -13
- package/dist/workerAppProject.d.ts +1 -1
- package/dist/workerAppProject.js +15 -2
- package/node_modules/@playdrop/api-client/dist/client.d.ts +72 -25
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +33 -0
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.js +17 -11
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +51 -6
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +338 -41
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +24 -1
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/ai.js +20 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +13 -8
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +158 -90
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +4 -0
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +27 -0
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +35 -3
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.js +89 -7
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.js +9 -0
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +1 -2
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +0 -11
- package/node_modules/@playdrop/api-client/dist/domains/social.d.ts +29 -0
- package/node_modules/@playdrop/api-client/dist/domains/social.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/social.js +79 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +80 -29
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +217 -56
- package/node_modules/@playdrop/api-client/dist/runtime.d.ts +18 -0
- package/node_modules/@playdrop/api-client/dist/runtime.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/runtime.js +54 -0
- package/node_modules/@playdrop/api-client/package.json +7 -0
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts +11 -0
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/creator-plans.js +13 -1
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts +0 -1
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/deployment.js +0 -8
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts +0 -1
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/public-deployment.js +0 -8
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +2 -2
- package/node_modules/@playdrop/config/dist/src/server/logging.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/logging.js +31 -2
- package/node_modules/@playdrop/config/dist/test/creator-plans.test.js +10 -0
- package/node_modules/@playdrop/config/dist/test/deployment.test.js +0 -4
- package/node_modules/@playdrop/config/dist/test/runtime-service-origins.test.js +2 -0
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/config/package.json +6 -0
- package/node_modules/@playdrop/game-source-git/dist/src/archive.d.ts +9 -0
- package/node_modules/@playdrop/game-source-git/dist/src/archive.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/archive.js +107 -0
- package/node_modules/@playdrop/game-source-git/dist/src/bundle-storage.d.ts +29 -0
- package/node_modules/@playdrop/game-source-git/dist/src/bundle-storage.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/bundle-storage.js +64 -0
- package/node_modules/@playdrop/game-source-git/dist/src/export.d.ts +8 -0
- package/node_modules/@playdrop/game-source-git/dist/src/export.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/export.js +25 -0
- package/node_modules/@playdrop/game-source-git/dist/src/git.d.ts +39 -0
- package/node_modules/@playdrop/game-source-git/dist/src/git.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/git.js +195 -0
- package/node_modules/@playdrop/game-source-git/dist/src/ignore.d.ts +6 -0
- package/node_modules/@playdrop/game-source-git/dist/src/ignore.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/ignore.js +51 -0
- package/node_modules/@playdrop/game-source-git/dist/src/index.d.ts +10 -0
- package/node_modules/@playdrop/game-source-git/dist/src/index.d.ts.map +1 -0
- package/node_modules/@playdrop/{boxel-core/dist/src/humanoid/r15 → game-source-git/dist/src}/index.js +9 -4
- package/node_modules/@playdrop/game-source-git/dist/src/lock.d.ts +17 -0
- package/node_modules/@playdrop/game-source-git/dist/src/lock.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/lock.js +72 -0
- package/node_modules/@playdrop/game-source-git/dist/src/materialize.d.ts +10 -0
- package/node_modules/@playdrop/game-source-git/dist/src/materialize.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/materialize.js +99 -0
- package/node_modules/@playdrop/game-source-git/dist/src/paths.d.ts +24 -0
- package/node_modules/@playdrop/game-source-git/dist/src/paths.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/paths.js +207 -0
- package/node_modules/@playdrop/game-source-git/dist/src/repository.d.ts +66 -0
- package/node_modules/@playdrop/game-source-git/dist/src/repository.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/src/repository.js +213 -0
- package/node_modules/@playdrop/game-source-git/dist/test/core.test.d.ts +2 -0
- package/node_modules/@playdrop/game-source-git/dist/test/core.test.d.ts.map +1 -0
- package/node_modules/@playdrop/game-source-git/dist/test/core.test.js +441 -0
- package/node_modules/@playdrop/game-source-git/dist/tsconfig.tsbuildinfo +1 -0
- package/node_modules/@playdrop/game-source-git/package.json +28 -0
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-terminal.js +15 -9
- package/node_modules/@playdrop/types/dist/api.d.ts +728 -164
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +60 -11
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +2 -0
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.d.ts +22 -8
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.js +26 -26
- package/node_modules/@playdrop/types/dist/chat.d.ts +107 -3
- package/node_modules/@playdrop/types/dist/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/chat.js +33 -4
- package/node_modules/@playdrop/types/dist/index.d.ts +3 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +7 -1
- package/node_modules/@playdrop/types/dist/public-cache-tags.d.ts +11 -0
- package/node_modules/@playdrop/types/dist/public-cache-tags.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/public-cache-tags.js +78 -0
- package/node_modules/@playdrop/types/dist/realtime.d.ts +3 -15
- package/node_modules/@playdrop/types/dist/realtime.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/social.d.ts +64 -0
- package/node_modules/@playdrop/types/dist/social.d.ts.map +1 -0
- package/node_modules/@playdrop/{boxel-three/dist/src/types.js → types/dist/social.js} +14 -2
- package/node_modules/@playdrop/types/dist/spritesheet.d.ts +51 -0
- package/node_modules/@playdrop/types/dist/spritesheet.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/spritesheet.js +73 -0
- package/node_modules/@playdrop/types/dist/version.d.ts +86 -58
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +111 -0
- package/node_modules/@playdrop/types/package.json +14 -0
- package/node_modules/@playdrop/vox-three/dist/src/index.d.ts +1 -1
- package/node_modules/@playdrop/vox-three/dist/src/index.js +1 -1
- package/node_modules/@playdrop/vox-three/dist/src/index.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +0 -3
- package/node_modules/@playdrop/vox-three/dist/src/vox.js +0 -110
- package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +0 -18
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
- package/node_modules/@playdrop/vox-three/package.json +8 -4
- package/package.json +9 -11
- package/dist/assets/model-artifacts.d.ts +0 -8
- package/dist/assets/model-artifacts.js +0 -382
- package/dist/assets/model-worker.d.ts +0 -22
- package/dist/assets/model-worker.js +0 -123
- package/node_modules/@playdrop/ai-client/dist/index.d.ts +0 -441
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +0 -1
- package/node_modules/@playdrop/ai-client/dist/index.js +0 -499
- package/node_modules/@playdrop/ai-client/package.json +0 -22
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.d.ts +0 -13
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +0 -141
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.d.ts +0 -47
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js +0 -313
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.d.ts +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js +0 -36
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.d.ts +0 -23
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.js +0 -11
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.d.ts +0 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.js +0 -21
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.d.ts +0 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.js +0 -24
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/index.d.ts +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/index.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.d.ts +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +0 -387
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/types.d.ts +0 -6
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/types.js +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/types.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.d.ts +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +0 -373
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.d.ts +0 -12
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js +0 -215
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.d.ts +0 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.js +0 -18
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.d.ts +0 -42
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js +0 -145
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.d.ts +0 -26
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.js +0 -65
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.d.ts +0 -33
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js +0 -175
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/humanoid_r15.json +0 -1517
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/humanoid_r6.json +0 -61
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.d.ts +0 -10
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.js +0 -23
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/index.d.ts +0 -19
- package/node_modules/@playdrop/boxel-core/dist/src/index.js +0 -36
- package/node_modules/@playdrop/boxel-core/dist/src/index.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/materials.d.ts +0 -26
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js +0 -184
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.d.ts +0 -68
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js +0 -488
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.d.ts +0 -4
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +0 -380
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/textures.d.ts +0 -29
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js +0 -208
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.d.ts +0 -9
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js +0 -312
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.d.ts +0 -9
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +0 -154
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.d.ts +0 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js +0 -121
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +0 -20
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +0 -389
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.d.ts +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js +0 -206
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.d.ts +0 -12
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js +0 -81
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +0 -12
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js +0 -318
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +0 -168
- package/node_modules/@playdrop/boxel-core/dist/src/types.js +0 -3
- package/node_modules/@playdrop/boxel-core/dist/src/types.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +0 -24
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +0 -620
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.d.ts +0 -11
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js +0 -135
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.d.ts +0 -23
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js +0 -52
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/package.json +0 -19
- package/node_modules/@playdrop/boxel-three/dist/src/animations.d.ts +0 -4
- package/node_modules/@playdrop/boxel-three/dist/src/animations.js +0 -92
- package/node_modules/@playdrop/boxel-three/dist/src/builders.d.ts +0 -8
- package/node_modules/@playdrop/boxel-three/dist/src/builders.js +0 -363
- package/node_modules/@playdrop/boxel-three/dist/src/context.d.ts +0 -24
- package/node_modules/@playdrop/boxel-three/dist/src/context.js +0 -58
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +0 -100
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +0 -927
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.d.ts +0 -18
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.js +0 -294
- package/node_modules/@playdrop/boxel-three/dist/src/index.d.ts +0 -17
- package/node_modules/@playdrop/boxel-three/dist/src/index.js +0 -64
- package/node_modules/@playdrop/boxel-three/dist/src/instantiate.d.ts +0 -40
- package/node_modules/@playdrop/boxel-three/dist/src/instantiate.js +0 -96
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.d.ts +0 -8
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +0 -88
- package/node_modules/@playdrop/boxel-three/dist/src/overlays.d.ts +0 -7
- package/node_modules/@playdrop/boxel-three/dist/src/overlays.js +0 -123
- package/node_modules/@playdrop/boxel-three/dist/src/primitives.d.ts +0 -4
- package/node_modules/@playdrop/boxel-three/dist/src/primitives.js +0 -48
- package/node_modules/@playdrop/boxel-three/dist/src/scene.d.ts +0 -3
- package/node_modules/@playdrop/boxel-three/dist/src/scene.js +0 -168
- package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.d.ts +0 -29
- package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.js +0 -623
- package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.d.ts +0 -13
- package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.js +0 -133
- package/node_modules/@playdrop/boxel-three/dist/src/textures.d.ts +0 -17
- package/node_modules/@playdrop/boxel-three/dist/src/textures.js +0 -216
- package/node_modules/@playdrop/boxel-three/dist/src/types.d.ts +0 -112
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.d.ts +0 -28
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.js +0 -344
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.d.ts +0 -24
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.js +0 -113
- package/node_modules/@playdrop/boxel-three/dist/test/export-image.playwright.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/export-image.playwright.test.js +0 -137
- package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.d.ts +0 -20
- package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.js +0 -85
- package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/glb-skinned.test.js +0 -104
- package/node_modules/@playdrop/boxel-three/dist/test/index.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/index.test.js +0 -30
- package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/instantiate.test.js +0 -88
- package/node_modules/@playdrop/boxel-three/dist/test/overlays.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/overlays.test.js +0 -42
- package/node_modules/@playdrop/boxel-three/dist/test/scene-filter.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/scene-filter.test.js +0 -73
- package/node_modules/@playdrop/boxel-three/dist/test/scene-smoke.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/scene-smoke.test.js +0 -85
- package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/skinned-mesh.test.js +0 -73
- package/node_modules/@playdrop/boxel-three/dist/test/textured-overlay.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/textured-overlay.test.js +0 -136
- package/node_modules/@playdrop/boxel-three/dist/test/voxels.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-three/dist/test/voxels.test.js +0 -42
- package/node_modules/@playdrop/boxel-three/package.json +0 -28
package/dist/commands/worker.js
CHANGED
|
@@ -3,34 +3,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.WORKER_TASK_WORKSPACE_RETENTION = exports.WORKER_CONTEXT_PROJECT_VALIDATE_NOT_ALLOWED_MESSAGE = exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.WORKSPACE_ARCHIVE_DOWNLOAD_TIMEOUT_MS = exports.runLoggedProcess = exports.readEnvBoolean = exports.
|
|
6
|
+
exports.WORKER_TASK_WORKSPACE_RETENTION = exports.WORKER_CONTEXT_PROJECT_VALIDATE_NOT_ALLOWED_MESSAGE = exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.WORKSPACE_ARCHIVE_DOWNLOAD_TIMEOUT_MS = exports.runLoggedProcess = exports.readEnvBoolean = exports.readCodexRolloutTokenUsageForThread = exports.extractCodexTokensUsed = exports.extractGrokRunMetadata = exports.extractCodexThreadId = exports.extractClaudeSessionId = exports.extractCodexRolloutTokenUsage = exports.extractAgentTokenUsage = exports.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS = exports.DEFAULT_CREATOR_AGENT_TIMEOUT_MS = exports.DEFAULT_CODEX_TIMEOUT_MS = exports.buildWorkerChildEnv = exports.buildPlaywrightMcpCommand = exports.buildGrokExecArgs = exports.buildCodexExecArgs = exports.buildCleanRoomChildEnv = exports.buildClaudePermissionSettings = exports.buildClaudeExecArgs = exports.extractZipArchive = void 0;
|
|
7
7
|
exports.usesCreatorRecovery = usesCreatorRecovery;
|
|
8
8
|
exports.buildDeterministicReleaseSmokeArtifact = buildDeterministicReleaseSmokeArtifact;
|
|
9
9
|
exports.buildCreatorRecoveryPrompt = buildCreatorRecoveryPrompt;
|
|
10
|
+
exports.buildMissingAgentOutcomeRecoveryPrompt = buildMissingAgentOutcomeRecoveryPrompt;
|
|
11
|
+
exports.shouldRecoverMissingAgentOutcome = shouldRecoverMissingAgentOutcome;
|
|
12
|
+
exports.missingAgentOutcomeTerminalReason = missingAgentOutcomeTerminalReason;
|
|
13
|
+
exports.creationTimeoutTerminalReason = creationTimeoutTerminalReason;
|
|
10
14
|
exports.buildGameEvalRetryPrompt = buildGameEvalRetryPrompt;
|
|
11
15
|
exports.resolveInitialCreatorProgress = resolveInitialCreatorProgress;
|
|
12
16
|
exports.allocateWorkerDevPort = allocateWorkerDevPort;
|
|
13
17
|
exports.isWorkerContextCommandAllowed = isWorkerContextCommandAllowed;
|
|
14
18
|
exports.resolveWorkerContextCommandBlockMessage = resolveWorkerContextCommandBlockMessage;
|
|
15
19
|
exports.resolveWorkerExecutionTargetFromRole = resolveWorkerExecutionTargetFromRole;
|
|
16
|
-
exports.
|
|
17
|
-
exports.
|
|
18
|
-
exports.normalizeWorkerClaimResponseV3 = normalizeWorkerClaimResponseV3;
|
|
20
|
+
exports.normalizeWorkerGameTaskAssignment = normalizeWorkerGameTaskAssignment;
|
|
21
|
+
exports.normalizeWorkerClaimResponseV5 = normalizeWorkerClaimResponseV5;
|
|
19
22
|
exports.nextClaimBackoffMs = nextClaimBackoffMs;
|
|
20
23
|
exports.claimBackoffDelayMs = claimBackoffDelayMs;
|
|
21
24
|
exports.acknowledgeWorkerTaskClaim = acknowledgeWorkerTaskClaim;
|
|
22
25
|
exports.containWorkerTaskRun = containWorkerTaskRun;
|
|
23
26
|
exports.resolveWorkerHomeDir = resolveWorkerHomeDir;
|
|
27
|
+
exports.configureWorkerFal = configureWorkerFal;
|
|
24
28
|
exports.pruneWorkerTaskWorkspaces = pruneWorkerTaskWorkspaces;
|
|
25
29
|
exports.prepareClaimedTaskWorkspace = prepareClaimedTaskWorkspace;
|
|
26
30
|
exports.buildWorkerTaskContextV2 = buildWorkerTaskContextV2;
|
|
27
31
|
exports.resolveWorkerInstrumentSurface = resolveWorkerInstrumentSurface;
|
|
28
32
|
exports.hasAgentTaskUploadedArtifact = hasAgentTaskUploadedArtifact;
|
|
29
|
-
exports.
|
|
33
|
+
exports.buildWorkerTranscriptUploadHeaders = buildWorkerTranscriptUploadHeaders;
|
|
34
|
+
exports.describeTaskUploadFailure = describeTaskUploadFailure;
|
|
35
|
+
exports.recordTaskUploadFailure = recordTaskUploadFailure;
|
|
36
|
+
exports.readTaskUploadFailureFile = readTaskUploadFailureFile;
|
|
37
|
+
exports.readTaskUploadResultFile = readTaskUploadResultFile;
|
|
30
38
|
exports.readTaskNextStepsFile = readTaskNextStepsFile;
|
|
31
39
|
exports.assertTaskUploadResultMatchesContext = assertTaskUploadResultMatchesContext;
|
|
32
|
-
exports.
|
|
33
|
-
exports.stageAssignmentWorkspaceV3 = stageAssignmentWorkspaceV3;
|
|
40
|
+
exports.stageAssignmentWorkspace = stageAssignmentWorkspace;
|
|
34
41
|
exports.assertAssignmentPluginBundleExtractionBounds = assertAssignmentPluginBundleExtractionBounds;
|
|
35
42
|
exports.stageDevPluginWorkingTree = stageDevPluginWorkingTree;
|
|
36
43
|
exports.stageAssignmentPluginBundle = stageAssignmentPluginBundle;
|
|
@@ -61,16 +68,21 @@ exports.buildGrokPromptJson = buildGrokPromptJson;
|
|
|
61
68
|
exports.buildGrokIsolationEnv = buildGrokIsolationEnv;
|
|
62
69
|
exports.resolveHostGrokAuthPath = resolveHostGrokAuthPath;
|
|
63
70
|
exports.mergeCreatorRecoveryResults = mergeCreatorRecoveryResults;
|
|
71
|
+
exports.mergeMissingAgentOutcomeRecoveryResults = mergeMissingAgentOutcomeRecoveryResults;
|
|
64
72
|
exports.buildAgentFailureCode = buildAgentFailureCode;
|
|
65
73
|
exports.classifyAgentAvailabilityFailureReason = classifyAgentAvailabilityFailureReason;
|
|
66
74
|
exports.describeAgentFailureForEvent = describeAgentFailureForEvent;
|
|
75
|
+
exports.normalizeWorkerFailureErrorCode = normalizeWorkerFailureErrorCode;
|
|
67
76
|
exports.createLocalPlaydropShim = createLocalPlaydropShim;
|
|
68
77
|
exports.parseCodexModelCatalog = parseCodexModelCatalog;
|
|
78
|
+
exports.codexFeatureEnabled = codexFeatureEnabled;
|
|
69
79
|
exports.parseClaudeModelCatalog = parseClaudeModelCatalog;
|
|
70
80
|
exports.claudeModelProbeReportsSelection = claudeModelProbeReportsSelection;
|
|
71
81
|
exports.parseGrokModelCatalog = parseGrokModelCatalog;
|
|
72
82
|
exports.probeWorkerProviderIndependently = probeWorkerProviderIndependently;
|
|
73
83
|
exports.describeWorkerPreflightReason = describeWorkerPreflightReason;
|
|
84
|
+
exports.falAuthenticationProbeExitCode = falAuthenticationProbeExitCode;
|
|
85
|
+
exports.probeFalAuthentication = probeFalAuthentication;
|
|
74
86
|
exports.buildWorkerSetupActions = buildWorkerSetupActions;
|
|
75
87
|
exports.resolveWorkerStateDir = resolveWorkerStateDir;
|
|
76
88
|
exports.resolveWorkerSupervisorLockFile = resolveWorkerSupervisorLockFile;
|
|
@@ -90,6 +102,8 @@ exports.assertLaunchAgentPlistCompatible = assertLaunchAgentPlistCompatible;
|
|
|
90
102
|
exports.launchManagedWorker = launchManagedWorker;
|
|
91
103
|
exports.showWorkerStatus = showWorkerStatus;
|
|
92
104
|
exports.setupWorker = setupWorker;
|
|
105
|
+
exports.completeWorkerAgentTaskWithRetry = completeWorkerAgentTaskWithRetry;
|
|
106
|
+
exports.classifyWorkerSupervisorFailure = classifyWorkerSupervisorFailure;
|
|
93
107
|
exports.assignmentRequiresRegisteredBundle = assignmentRequiresRegisteredBundle;
|
|
94
108
|
exports.startWorker = startWorker;
|
|
95
109
|
exports.reportTask = reportTask;
|
|
@@ -126,16 +140,21 @@ const config_1 = require("../config");
|
|
|
126
140
|
const messages_1 = require("../messages");
|
|
127
141
|
const output_1 = require("../output");
|
|
128
142
|
const shellProbe_1 = require("../shellProbe");
|
|
143
|
+
const taskSelection_1 = require("../taskSelection");
|
|
129
144
|
const review_1 = require("./review");
|
|
130
145
|
const upload_1 = require("./upload");
|
|
146
|
+
const staticHtml_1 = require("../apps/staticHtml");
|
|
131
147
|
const runtime_1 = require("./worker/runtime");
|
|
132
148
|
const instrument_evidence_1 = require("./worker/instrument-evidence");
|
|
133
149
|
const provider_telemetry_1 = require("./worker/provider-telemetry");
|
|
134
150
|
const archive_staging_1 = require("./worker/archive-staging");
|
|
151
|
+
const game_server_infra_1 = require("./worker/game-server-infra");
|
|
152
|
+
const game_source_git_1 = require("./worker/game-source-git");
|
|
153
|
+
const generation_1 = require("./worker/generation");
|
|
154
|
+
const e2e_fixtures_1 = require("./worker/e2e-fixtures");
|
|
135
155
|
var archive_staging_2 = require("./worker/archive-staging");
|
|
136
156
|
Object.defineProperty(exports, "extractZipArchive", { enumerable: true, get: function () { return archive_staging_2.extractZipArchive; } });
|
|
137
157
|
var runtime_2 = require("./worker/runtime");
|
|
138
|
-
Object.defineProperty(exports, "assertWorkerTokenUsageWithinCap", { enumerable: true, get: function () { return runtime_2.assertWorkerTokenUsageWithinCap; } });
|
|
139
158
|
Object.defineProperty(exports, "buildClaudeExecArgs", { enumerable: true, get: function () { return runtime_2.buildClaudeExecArgs; } });
|
|
140
159
|
Object.defineProperty(exports, "buildClaudePermissionSettings", { enumerable: true, get: function () { return runtime_2.buildClaudePermissionSettings; } });
|
|
141
160
|
Object.defineProperty(exports, "buildCleanRoomChildEnv", { enumerable: true, get: function () { return runtime_2.buildCleanRoomChildEnv; } });
|
|
@@ -146,7 +165,6 @@ Object.defineProperty(exports, "buildWorkerChildEnv", { enumerable: true, get: f
|
|
|
146
165
|
Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
|
|
147
166
|
Object.defineProperty(exports, "DEFAULT_CREATOR_AGENT_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CREATOR_AGENT_TIMEOUT_MS; } });
|
|
148
167
|
Object.defineProperty(exports, "DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS; } });
|
|
149
|
-
Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
|
|
150
168
|
Object.defineProperty(exports, "extractAgentTokenUsage", { enumerable: true, get: function () { return runtime_2.extractAgentTokenUsage; } });
|
|
151
169
|
Object.defineProperty(exports, "extractCodexRolloutTokenUsage", { enumerable: true, get: function () { return runtime_2.extractCodexRolloutTokenUsage; } });
|
|
152
170
|
Object.defineProperty(exports, "extractClaudeSessionId", { enumerable: true, get: function () { return runtime_2.extractClaudeSessionId; } });
|
|
@@ -154,7 +172,6 @@ Object.defineProperty(exports, "extractCodexThreadId", { enumerable: true, get:
|
|
|
154
172
|
Object.defineProperty(exports, "extractGrokRunMetadata", { enumerable: true, get: function () { return runtime_2.extractGrokRunMetadata; } });
|
|
155
173
|
Object.defineProperty(exports, "extractCodexTokensUsed", { enumerable: true, get: function () { return runtime_2.extractCodexTokensUsed; } });
|
|
156
174
|
Object.defineProperty(exports, "readCodexRolloutTokenUsageForThread", { enumerable: true, get: function () { return runtime_2.readCodexRolloutTokenUsageForThread; } });
|
|
157
|
-
Object.defineProperty(exports, "readCodexSandboxMode", { enumerable: true, get: function () { return runtime_2.readCodexSandboxMode; } });
|
|
158
175
|
Object.defineProperty(exports, "readEnvBoolean", { enumerable: true, get: function () { return runtime_2.readEnvBoolean; } });
|
|
159
176
|
Object.defineProperty(exports, "runLoggedProcess", { enumerable: true, get: function () { return runtime_2.runLoggedProcess; } });
|
|
160
177
|
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
@@ -171,8 +188,7 @@ const FAIL_REPORT_RETRY_DELAY_MS = 2000;
|
|
|
171
188
|
const CLAIM_ACK_RETRY_DELAY_MS = 500;
|
|
172
189
|
const CLAIM_ACK_MAX_ATTEMPTS = 4;
|
|
173
190
|
exports.WORKSPACE_ARCHIVE_DOWNLOAD_TIMEOUT_MS = 5 * 60000;
|
|
174
|
-
const
|
|
175
|
-
const WORKER_WRAPPER_CONTRACT_VERSION = 3;
|
|
191
|
+
const WORKER_WRAPPER_CONTRACT_VERSION = 5;
|
|
176
192
|
const WORKER_SUPPORTED_KINDS = [
|
|
177
193
|
"NEW_GAME",
|
|
178
194
|
"STATIC_GAME",
|
|
@@ -181,6 +197,14 @@ const WORKER_SUPPORTED_KINDS = [
|
|
|
181
197
|
"GAME_REVIEW",
|
|
182
198
|
"GAME_EVAL",
|
|
183
199
|
"RELEASE_SMOKE",
|
|
200
|
+
"TEXT",
|
|
201
|
+
"JSON",
|
|
202
|
+
"IMAGE",
|
|
203
|
+
"MODEL_3D",
|
|
204
|
+
"VIDEO",
|
|
205
|
+
"SFX",
|
|
206
|
+
"SPEECH",
|
|
207
|
+
"MUSIC",
|
|
184
208
|
];
|
|
185
209
|
const GAME_CREATION_TASK_KINDS = new Set(["NEW_GAME", "STATIC_GAME", "REMIX_GAME", "GAME_UPDATE"]);
|
|
186
210
|
const AGENT_MANAGED_UPLOAD_TASK_KINDS = new Set(["NEW_GAME", "REMIX_GAME", "GAME_UPDATE"]);
|
|
@@ -222,6 +246,46 @@ function buildCreatorRecoveryPrompt(input) {
|
|
|
222
246
|
`Original request: ${prompt}`,
|
|
223
247
|
].join("\n\n");
|
|
224
248
|
}
|
|
249
|
+
function buildMissingAgentOutcomeRecoveryPrompt(kind) {
|
|
250
|
+
if (kind === "STATIC_GAME" || kind === "RELEASE_SMOKE") {
|
|
251
|
+
throw new Error(`agent_task_outcome_recovery_unsupported:${kind}`);
|
|
252
|
+
}
|
|
253
|
+
return [
|
|
254
|
+
"You stopped without reporting task success or failure.",
|
|
255
|
+
"Please read the task-worker skill, resume your work in this same task and conversation, and report failure or success before you exit.",
|
|
256
|
+
].join(" ");
|
|
257
|
+
}
|
|
258
|
+
function shouldRecoverMissingAgentOutcome(input) {
|
|
259
|
+
return (input.kind !== "STATIC_GAME" &&
|
|
260
|
+
input.kind !== "RELEASE_SMOKE" &&
|
|
261
|
+
input.taskStatus === "RUNNING" &&
|
|
262
|
+
!input.completionReported &&
|
|
263
|
+
input.result.exitCode === 0 &&
|
|
264
|
+
input.result.signal === null &&
|
|
265
|
+
!input.result.timedOut);
|
|
266
|
+
}
|
|
267
|
+
function missingAgentOutcomeTerminalReason() {
|
|
268
|
+
const code = "agent_stopped_without_outcome";
|
|
269
|
+
return {
|
|
270
|
+
kind: "failure",
|
|
271
|
+
category: "agent_failure",
|
|
272
|
+
code,
|
|
273
|
+
message: "The agent stopped without being able to upload the game. Please retry.",
|
|
274
|
+
retryable: true,
|
|
275
|
+
source: "worker",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function creationTimeoutTerminalReason() {
|
|
279
|
+
const code = "agent_task_creation_timeout";
|
|
280
|
+
return {
|
|
281
|
+
kind: "failure",
|
|
282
|
+
category: "provider_timeout",
|
|
283
|
+
code,
|
|
284
|
+
message: "The task took too long and couldn't finish on time. Retry it, or clear it and ask for a simpler change.",
|
|
285
|
+
retryable: true,
|
|
286
|
+
source: "worker",
|
|
287
|
+
};
|
|
288
|
+
}
|
|
225
289
|
function buildGameEvalRetryPrompt(prompt) {
|
|
226
290
|
return [
|
|
227
291
|
prompt,
|
|
@@ -263,12 +327,36 @@ const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
|
263
327
|
["documentation", "read"],
|
|
264
328
|
["help"],
|
|
265
329
|
["ai", "create"],
|
|
266
|
-
["ai", "
|
|
267
|
-
["
|
|
330
|
+
["ai", "tasks", "browse"],
|
|
331
|
+
["chat", "channels"],
|
|
332
|
+
["chat", "inbox"],
|
|
333
|
+
["chat", "conversation"],
|
|
334
|
+
["chat", "read"],
|
|
335
|
+
["chat", "thread"],
|
|
336
|
+
["chat", "send"],
|
|
337
|
+
["chat", "reply"],
|
|
338
|
+
["chat", "dm"],
|
|
339
|
+
["chat", "react"],
|
|
340
|
+
["chat", "unreact"],
|
|
341
|
+
["ai", "tasks", "detail"],
|
|
268
342
|
["ai", "generations"],
|
|
269
343
|
];
|
|
270
344
|
exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = "worker_context_command_not_allowed: inside a PlayDrop worker task only task progress, task asset-runtime, task claim-slug, task upload/done/fail, assigned app scaffolding, project build/dev/check/capture, read-only help/catalogue/documentation lookup, and PlayDrop AI generation are permitted.";
|
|
271
345
|
exports.WORKER_CONTEXT_PROJECT_VALIDATE_NOT_ALLOWED_MESSAGE = 'worker_context_project_validate_not_allowed: "playdrop project validate" is not available inside a PlayDrop worker task. Run "playdrop task upload --preflight-only" as the sole pre-upload validation gate.';
|
|
346
|
+
const TASK_UPLOAD_FAILURE_CATEGORIES = new Set([
|
|
347
|
+
"provider_capacity",
|
|
348
|
+
"provider_connection",
|
|
349
|
+
"provider_timeout",
|
|
350
|
+
"instrument_error",
|
|
351
|
+
"upload_validation",
|
|
352
|
+
"agent_failure",
|
|
353
|
+
]);
|
|
354
|
+
const TASK_UPLOAD_FAILURE_SOURCES = new Set([
|
|
355
|
+
"worker",
|
|
356
|
+
"agent",
|
|
357
|
+
"provider",
|
|
358
|
+
"instrument",
|
|
359
|
+
]);
|
|
272
360
|
function resolveInitialCreatorProgress(kind, creationRecovery = false) {
|
|
273
361
|
if (creationRecovery) {
|
|
274
362
|
return null;
|
|
@@ -395,7 +483,13 @@ function isAssignedAppScaffoldCommandAllowed(argv) {
|
|
|
395
483
|
return normalizeRemixFlagValue(argv) === context.remixSourceRef;
|
|
396
484
|
}
|
|
397
485
|
function resolveWorkerExecutionTargetFromRole(role) {
|
|
398
|
-
|
|
486
|
+
if (role === "CLOUD_WORKER") {
|
|
487
|
+
return "FIRST_PARTY";
|
|
488
|
+
}
|
|
489
|
+
if (role === "CREATOR") {
|
|
490
|
+
return "PERSONAL";
|
|
491
|
+
}
|
|
492
|
+
throw new Error(`worker_role_not_supported:${String(role ?? "unknown")}`);
|
|
399
493
|
}
|
|
400
494
|
function normalizeAssignmentWorkspaceFolder(value) {
|
|
401
495
|
const folderName = typeof value === "string" ? value.trim() : "";
|
|
@@ -439,138 +533,6 @@ function normalizeAssignmentContent(input) {
|
|
|
439
533
|
}
|
|
440
534
|
return { path: relativePath, contentBase64, sha256, sizeBytes, buffer };
|
|
441
535
|
}
|
|
442
|
-
function normalizeWorkerTaskAssignmentV2(claim) {
|
|
443
|
-
if (claim.protocolVersion !== 2) {
|
|
444
|
-
throw new Error("agent_task_claim_protocol_version_mismatch");
|
|
445
|
-
}
|
|
446
|
-
if (claim.action !== "run") {
|
|
447
|
-
return null;
|
|
448
|
-
}
|
|
449
|
-
const assignment = claim.assignment;
|
|
450
|
-
const taskId = parsePositiveInteger(assignment.task.id, "agent_task_assignment_task_id_missing");
|
|
451
|
-
const taskToken = typeof assignment.task.token === "string" ? assignment.task.token.trim() : "";
|
|
452
|
-
const taskType = typeof assignment.task.type === "string" ? assignment.task.type.trim() : "";
|
|
453
|
-
const attempt = parsePositiveInteger(assignment.task.attempt, "agent_task_assignment_attempt_missing");
|
|
454
|
-
const prompt = typeof assignment.request.prompt === "string" ? assignment.request.prompt : "";
|
|
455
|
-
const model = typeof assignment.agent.model === "string" ? assignment.agent.model.trim() : "";
|
|
456
|
-
if (!taskToken || !taskType || !prompt.trim() || !model) {
|
|
457
|
-
throw new Error("agent_task_assignment_invalid");
|
|
458
|
-
}
|
|
459
|
-
const workspacePaths = new Set();
|
|
460
|
-
const files = assignment.workspace.files.map((file) => {
|
|
461
|
-
const normalized = normalizeAssignmentContent({
|
|
462
|
-
path: file.path,
|
|
463
|
-
contentBase64: file.contentBase64,
|
|
464
|
-
sha256: file.sha256,
|
|
465
|
-
sizeBytes: file.sizeBytes,
|
|
466
|
-
code: "agent_task_assignment_workspace_file",
|
|
467
|
-
});
|
|
468
|
-
const contentType = typeof file.contentType === "string" ? file.contentType.trim() : "";
|
|
469
|
-
if (!contentType) {
|
|
470
|
-
throw new Error("agent_task_assignment_workspace_file_content_type_missing");
|
|
471
|
-
}
|
|
472
|
-
if (workspacePaths.has(normalized.path)) {
|
|
473
|
-
throw new Error("agent_task_assignment_workspace_path_duplicate");
|
|
474
|
-
}
|
|
475
|
-
workspacePaths.add(normalized.path);
|
|
476
|
-
return {
|
|
477
|
-
path: normalized.path,
|
|
478
|
-
sha256: normalized.sha256,
|
|
479
|
-
sizeBytes: normalized.sizeBytes,
|
|
480
|
-
contentType,
|
|
481
|
-
contentBase64: normalized.contentBase64,
|
|
482
|
-
};
|
|
483
|
-
});
|
|
484
|
-
const archives = assignment.workspace.archives.map((archive) => {
|
|
485
|
-
if (archive.format !== "zip") {
|
|
486
|
-
throw new Error("agent_task_assignment_workspace_archive_format_invalid");
|
|
487
|
-
}
|
|
488
|
-
const normalized = normalizeAssignmentContent({
|
|
489
|
-
path: archive.path,
|
|
490
|
-
contentBase64: archive.contentBase64,
|
|
491
|
-
sha256: archive.sha256,
|
|
492
|
-
sizeBytes: archive.sizeBytes,
|
|
493
|
-
code: "agent_task_assignment_workspace_archive",
|
|
494
|
-
});
|
|
495
|
-
if (workspacePaths.has(normalized.path)) {
|
|
496
|
-
throw new Error("agent_task_assignment_workspace_path_duplicate");
|
|
497
|
-
}
|
|
498
|
-
workspacePaths.add(normalized.path);
|
|
499
|
-
return {
|
|
500
|
-
path: normalized.path,
|
|
501
|
-
format: "zip",
|
|
502
|
-
sha256: normalized.sha256,
|
|
503
|
-
sizeBytes: normalized.sizeBytes,
|
|
504
|
-
contentBase64: normalized.contentBase64,
|
|
505
|
-
};
|
|
506
|
-
});
|
|
507
|
-
const fileByPath = new Map(files.map((file) => [file.path, file]));
|
|
508
|
-
const attachmentIds = new Set();
|
|
509
|
-
const attachments = assignment.request.attachments.map((attachment) => {
|
|
510
|
-
const id = typeof attachment.id === "string" ? attachment.id.trim() : "";
|
|
511
|
-
const attachmentPath = normalizeAssignmentWorkspaceRelativePath(attachment.path);
|
|
512
|
-
const fileName = typeof attachment.fileName === "string" ? attachment.fileName.trim() : "";
|
|
513
|
-
const contentType = typeof attachment.contentType === "string" ? attachment.contentType.trim() : "";
|
|
514
|
-
const sha256 = typeof attachment.sha256 === "string" ? attachment.sha256.trim() : "";
|
|
515
|
-
const sizeBytes = Number(attachment.sizeBytes);
|
|
516
|
-
const file = fileByPath.get(attachmentPath);
|
|
517
|
-
if (!id || attachmentIds.has(id) || !fileName || !contentType || !file) {
|
|
518
|
-
throw new Error("agent_task_assignment_attachment_invalid");
|
|
519
|
-
}
|
|
520
|
-
if (sha256 !== file.sha256 || sizeBytes !== file.sizeBytes || contentType !== file.contentType) {
|
|
521
|
-
throw new Error("agent_task_assignment_attachment_metadata_mismatch");
|
|
522
|
-
}
|
|
523
|
-
attachmentIds.add(id);
|
|
524
|
-
return {
|
|
525
|
-
id,
|
|
526
|
-
path: attachmentPath,
|
|
527
|
-
fileName,
|
|
528
|
-
contentType,
|
|
529
|
-
sizeBytes,
|
|
530
|
-
sha256,
|
|
531
|
-
description: typeof attachment.description === "string" ? attachment.description : null,
|
|
532
|
-
};
|
|
533
|
-
});
|
|
534
|
-
for (const attachment of attachments) {
|
|
535
|
-
if (!fileByPath.has(attachment.path)) {
|
|
536
|
-
throw new Error("agent_task_assignment_attachment_file_missing");
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
const browser = assignment.agent.browser;
|
|
540
|
-
if (browser !== "NONE" && browser !== "CHROME" && browser !== "PLAYWRIGHT") {
|
|
541
|
-
throw new Error("agent_task_assignment_browser_invalid");
|
|
542
|
-
}
|
|
543
|
-
const reasoningEffort = assignment.agent.reasoningEffort;
|
|
544
|
-
if (reasoningEffort !== null && typeof reasoningEffort !== "string") {
|
|
545
|
-
throw new Error("agent_task_assignment_reasoning_effort_invalid");
|
|
546
|
-
}
|
|
547
|
-
return {
|
|
548
|
-
task: {
|
|
549
|
-
id: taskId,
|
|
550
|
-
token: taskToken,
|
|
551
|
-
type: taskType,
|
|
552
|
-
executionTarget: normalizeAssignmentTarget(assignment.task.executionTarget),
|
|
553
|
-
attempt,
|
|
554
|
-
metadata: assignment.task.metadata,
|
|
555
|
-
},
|
|
556
|
-
workspace: {
|
|
557
|
-
folderName: normalizeAssignmentWorkspaceFolder(assignment.workspace.folderName),
|
|
558
|
-
archives,
|
|
559
|
-
files,
|
|
560
|
-
},
|
|
561
|
-
plugin: assignment.plugin,
|
|
562
|
-
request: {
|
|
563
|
-
prompt,
|
|
564
|
-
attachments,
|
|
565
|
-
},
|
|
566
|
-
agent: {
|
|
567
|
-
runtime: normalizeAssignmentAgent(assignment.agent.runtime),
|
|
568
|
-
model,
|
|
569
|
-
reasoningEffort,
|
|
570
|
-
browser,
|
|
571
|
-
},
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
536
|
function requireClaimRecord(value, code = "agent_task_claim_response_malformed") {
|
|
575
537
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
576
538
|
throw new Error(code);
|
|
@@ -616,33 +578,168 @@ function normalizeWorkspaceArchiveDownloadUrl(value) {
|
|
|
616
578
|
}
|
|
617
579
|
return parsed.toString();
|
|
618
580
|
}
|
|
619
|
-
function
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
581
|
+
function normalizeGameSourceAssignment(value) {
|
|
582
|
+
if (value === undefined)
|
|
583
|
+
return undefined;
|
|
584
|
+
const source = requireClaimRecord(value, "agent_task_assignment_game_source_invalid");
|
|
585
|
+
if (source.version !== 2 || !["NEW", "STATIC", "REMIX", "UPDATE"].includes(String(source.mode))) {
|
|
586
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
587
|
+
}
|
|
588
|
+
const normalizeVersions = (entries) => {
|
|
589
|
+
if (!Array.isArray(entries))
|
|
590
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
591
|
+
return entries.map((entry) => {
|
|
592
|
+
const item = requireClaimRecord(entry, "agent_task_assignment_game_source_invalid");
|
|
593
|
+
const versionId = parsePositiveInteger(item.versionId, "agent_task_assignment_game_source_invalid");
|
|
594
|
+
const major = Number(item.major);
|
|
595
|
+
const minor = Number(item.minor);
|
|
596
|
+
const patch = Number(item.patch);
|
|
597
|
+
const commitSha = String(item.commitSha ?? "").trim().toLowerCase();
|
|
598
|
+
if ([major, minor, patch].some((part) => !Number.isSafeInteger(part) || part < 0) || !/^[0-9a-f]{40}$/.test(commitSha)) {
|
|
599
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
600
|
+
}
|
|
601
|
+
return { versionId, major, minor, patch, commitSha };
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
const authoritativeVersions = normalizeVersions(source.authoritativeVersions);
|
|
605
|
+
if (!Array.isArray(source.transcripts)) {
|
|
606
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
607
|
+
}
|
|
608
|
+
const transcripts = source.transcripts.map((entry) => {
|
|
609
|
+
const item = requireClaimRecord(entry, "agent_task_assignment_game_source_invalid");
|
|
610
|
+
const taskId = parsePositiveInteger(item.taskId, "agent_task_assignment_game_source_invalid");
|
|
611
|
+
const attempt = parsePositiveInteger(item.attempt, "agent_task_assignment_game_source_invalid");
|
|
612
|
+
const sha256 = String(item.sha256 ?? "").trim().toLowerCase();
|
|
613
|
+
const bytes = Number(item.bytes);
|
|
614
|
+
if (!/^[0-9a-f]{64}$/.test(sha256) || !Number.isSafeInteger(bytes) || bytes < 0) {
|
|
615
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
627
616
|
}
|
|
628
|
-
return
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
617
|
+
return {
|
|
618
|
+
taskId,
|
|
619
|
+
attempt,
|
|
620
|
+
downloadUrl: normalizeWorkspaceArchiveDownloadUrl(item.downloadUrl),
|
|
621
|
+
sha256,
|
|
622
|
+
bytes,
|
|
623
|
+
};
|
|
624
|
+
});
|
|
625
|
+
const listingObject = source.listingSync === null
|
|
626
|
+
? null
|
|
627
|
+
: requireClaimRecord(source.listingSync, "agent_task_assignment_game_source_invalid");
|
|
628
|
+
const listingSync = listingObject
|
|
629
|
+
? (() => {
|
|
630
|
+
const stringOrNull = (value) => {
|
|
631
|
+
if (value === null)
|
|
632
|
+
return null;
|
|
633
|
+
if (typeof value !== "string")
|
|
634
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
635
|
+
return value;
|
|
636
|
+
};
|
|
637
|
+
const stringArray = (value) => {
|
|
638
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
639
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
640
|
+
}
|
|
641
|
+
return value;
|
|
642
|
+
};
|
|
643
|
+
if (!Array.isArray(listingObject.files) || typeof listingObject.updatedAt !== "string" || !Number.isFinite(Date.parse(listingObject.updatedAt))) {
|
|
644
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
645
|
+
}
|
|
646
|
+
const files = listingObject.files.map((entry) => {
|
|
647
|
+
const item = requireClaimRecord(entry, "agent_task_assignment_game_source_invalid");
|
|
648
|
+
const filePath = typeof item.path === "string" ? item.path.trim() : "";
|
|
649
|
+
const fileSha256 = String(item.sha256 ?? "").trim().toLowerCase();
|
|
650
|
+
const fileBytes = Number(item.bytes);
|
|
651
|
+
if (!filePath.startsWith("listing/") || !/^[0-9a-f]{64}$/.test(fileSha256) || !Number.isSafeInteger(fileBytes) || fileBytes < 0) {
|
|
652
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
path: filePath,
|
|
656
|
+
downloadUrl: normalizeWorkspaceArchiveDownloadUrl(item.downloadUrl),
|
|
657
|
+
sha256: fileSha256,
|
|
658
|
+
bytes: fileBytes,
|
|
659
|
+
};
|
|
660
|
+
});
|
|
661
|
+
const version = String(listingObject.version ?? "").trim();
|
|
662
|
+
if (!/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.test(version)) {
|
|
663
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
versionId: parsePositiveInteger(listingObject.versionId, "agent_task_assignment_game_source_invalid"),
|
|
667
|
+
version,
|
|
668
|
+
displayName: stringOrNull(listingObject.displayName),
|
|
669
|
+
subtitle: stringOrNull(listingObject.subtitle),
|
|
670
|
+
description: stringOrNull(listingObject.description),
|
|
671
|
+
iconPath: stringOrNull(listingObject.iconPath),
|
|
672
|
+
heroPortraitPath: stringOrNull(listingObject.heroPortraitPath),
|
|
673
|
+
heroLandscapePath: stringOrNull(listingObject.heroLandscapePath),
|
|
674
|
+
socialLandscapePath: stringOrNull(listingObject.socialLandscapePath),
|
|
675
|
+
screenshotsPortrait: stringArray(listingObject.screenshotsPortrait),
|
|
676
|
+
screenshotsLandscape: stringArray(listingObject.screenshotsLandscape),
|
|
677
|
+
videosPortrait: stringArray(listingObject.videosPortrait),
|
|
678
|
+
videosLandscape: stringArray(listingObject.videosLandscape),
|
|
679
|
+
updatedAt: listingObject.updatedAt,
|
|
680
|
+
files,
|
|
681
|
+
};
|
|
682
|
+
})()
|
|
683
|
+
: null;
|
|
684
|
+
const selectedBaseVersionId = source.selectedBaseVersionId === null
|
|
685
|
+
? null
|
|
686
|
+
: parsePositiveInteger(source.selectedBaseVersionId, "agent_task_assignment_game_source_invalid");
|
|
687
|
+
const baseObject = source.baseRepository === null
|
|
688
|
+
? null
|
|
689
|
+
: requireClaimRecord(source.baseRepository, "agent_task_assignment_game_source_invalid");
|
|
690
|
+
const baseRepository = baseObject
|
|
691
|
+
? {
|
|
692
|
+
appId: parsePositiveInteger(baseObject.appId, "agent_task_assignment_game_source_invalid"),
|
|
693
|
+
repoGeneration: String(baseObject.repoGeneration ?? "").trim(),
|
|
694
|
+
bundleDownloadUrl: normalizeWorkspaceArchiveDownloadUrl(baseObject.bundleDownloadUrl),
|
|
695
|
+
expectedHeadSha: String(baseObject.expectedHeadSha ?? "").trim().toLowerCase(),
|
|
696
|
+
selectedCommitSha: String(baseObject.selectedCommitSha ?? "").trim().toLowerCase(),
|
|
697
|
+
}
|
|
698
|
+
: null;
|
|
699
|
+
const targetObject = requireClaimRecord(source.targetRepository, "agent_task_assignment_game_source_invalid");
|
|
700
|
+
const targetRepository = {
|
|
701
|
+
appId: targetObject.appId === null
|
|
702
|
+
? null
|
|
703
|
+
: parsePositiveInteger(targetObject.appId, "agent_task_assignment_game_source_invalid"),
|
|
704
|
+
repoGeneration: targetObject.repoGeneration === null ? null : String(targetObject.repoGeneration).trim(),
|
|
705
|
+
expectedHeadSha: targetObject.expectedHeadSha === null ? null : String(targetObject.expectedHeadSha).trim().toLowerCase(),
|
|
706
|
+
};
|
|
707
|
+
if ((baseRepository && (!/^[1-9][0-9]*$/.test(baseRepository.repoGeneration) ||
|
|
708
|
+
!/^[0-9a-f]{40}$/.test(baseRepository.expectedHeadSha) ||
|
|
709
|
+
!/^[0-9a-f]{40}$/.test(baseRepository.selectedCommitSha))) ||
|
|
710
|
+
((source.mode === "UPDATE" || source.mode === "REMIX") && baseRepository === null) ||
|
|
711
|
+
((source.mode === "NEW" || source.mode === "STATIC") && baseRepository !== null) ||
|
|
712
|
+
(targetRepository.repoGeneration !== null && !/^[1-9][0-9]*$/.test(targetRepository.repoGeneration)) ||
|
|
713
|
+
(targetRepository.expectedHeadSha !== null && !/^[0-9a-f]{40}$/.test(targetRepository.expectedHeadSha)) ||
|
|
714
|
+
((targetRepository.repoGeneration === null) !== (targetRepository.expectedHeadSha === null)))
|
|
715
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
716
|
+
return {
|
|
717
|
+
version: 2,
|
|
718
|
+
mode: source.mode,
|
|
719
|
+
baseRepository,
|
|
720
|
+
targetRepository,
|
|
721
|
+
authoritativeVersions,
|
|
722
|
+
selectedBaseVersionId,
|
|
723
|
+
transcripts,
|
|
724
|
+
listingSync,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function normalizeWorkerGameTaskAssignment(value) {
|
|
728
|
+
const assignment = requireClaimRecord(value, "agent_task_assignment_invalid");
|
|
634
729
|
const task = requireClaimRecord(assignment.task, "agent_task_assignment_invalid");
|
|
635
730
|
const workspace = requireClaimRecord(assignment.workspace, "agent_task_assignment_workspace_missing");
|
|
636
731
|
const request = requireClaimRecord(assignment.request, "agent_task_assignment_invalid");
|
|
637
732
|
const agent = requireClaimRecord(assignment.agent, "agent_task_assignment_invalid");
|
|
638
733
|
const taskId = parsePositiveInteger(task.id, "agent_task_assignment_task_id_missing");
|
|
639
734
|
const taskToken = typeof task.token === "string" ? task.token.trim() : "";
|
|
735
|
+
const taskAccessToken = typeof task.accessToken === "string" ? task.accessToken.trim() : "";
|
|
640
736
|
const taskType = typeof task.type === "string" ? task.type.trim() : "";
|
|
641
737
|
const attempt = parsePositiveInteger(task.attempt, "agent_task_assignment_attempt_missing");
|
|
642
738
|
const prompt = typeof request.prompt === "string" ? request.prompt : "";
|
|
643
739
|
const model = typeof agent.model === "string" ? agent.model.trim() : "";
|
|
644
740
|
const metadata = requireClaimRecord(task.metadata, "agent_task_assignment_metadata_invalid");
|
|
645
|
-
|
|
741
|
+
const executionTarget = normalizeAssignmentTarget(task.executionTarget);
|
|
742
|
+
if (!taskToken || !taskType || !prompt.trim() || !model || (executionTarget === "FIRST_PARTY" && !taskAccessToken)) {
|
|
646
743
|
throw new Error("agent_task_assignment_invalid");
|
|
647
744
|
}
|
|
648
745
|
if (!Array.isArray(workspace.files) || !Array.isArray(workspace.archives) || !Array.isArray(request.attachments)) {
|
|
@@ -678,7 +775,11 @@ function normalizeWorkerTaskAssignmentV3(claim) {
|
|
|
678
775
|
const archive = requireClaimRecord(value, "agent_task_assignment_workspace_archive_invalid");
|
|
679
776
|
const archivePath = normalizeAssignmentWorkspaceRelativePath(archive.path);
|
|
680
777
|
const sizeBytes = Number(archive.sizeBytes);
|
|
681
|
-
|
|
778
|
+
const sha256 = typeof archive.sha256 === "string" ? archive.sha256.trim().toLowerCase() : "";
|
|
779
|
+
if (archive.format !== "zip" ||
|
|
780
|
+
!Number.isInteger(sizeBytes) ||
|
|
781
|
+
sizeBytes <= 0 ||
|
|
782
|
+
!/^[0-9a-f]{64}$/.test(sha256)) {
|
|
682
783
|
throw new Error("agent_task_assignment_workspace_archive_metadata_invalid");
|
|
683
784
|
}
|
|
684
785
|
if (workspacePaths.has(archivePath)) {
|
|
@@ -690,6 +791,7 @@ function normalizeWorkerTaskAssignmentV3(claim) {
|
|
|
690
791
|
format: "zip",
|
|
691
792
|
downloadUrl: normalizeWorkspaceArchiveDownloadUrl(archive.downloadUrl),
|
|
692
793
|
sizeBytes,
|
|
794
|
+
sha256,
|
|
693
795
|
};
|
|
694
796
|
});
|
|
695
797
|
const fileByPath = new Map(files.map((file) => [file.path, file]));
|
|
@@ -728,12 +830,17 @@ function normalizeWorkerTaskAssignmentV3(claim) {
|
|
|
728
830
|
if (reasoningEffort !== null && typeof reasoningEffort !== "string") {
|
|
729
831
|
throw new Error("agent_task_assignment_reasoning_effort_invalid");
|
|
730
832
|
}
|
|
833
|
+
const gameSource = normalizeGameSourceAssignment(assignment.gameSource);
|
|
834
|
+
if (gameSource && executionTarget !== "FIRST_PARTY") {
|
|
835
|
+
throw new Error("agent_task_assignment_game_source_invalid");
|
|
836
|
+
}
|
|
731
837
|
return {
|
|
732
838
|
task: {
|
|
733
839
|
id: taskId,
|
|
734
840
|
token: taskToken,
|
|
841
|
+
...(taskAccessToken ? { accessToken: taskAccessToken } : {}),
|
|
735
842
|
type: taskType,
|
|
736
|
-
executionTarget
|
|
843
|
+
executionTarget,
|
|
737
844
|
attempt,
|
|
738
845
|
metadata,
|
|
739
846
|
},
|
|
@@ -750,38 +857,103 @@ function normalizeWorkerTaskAssignmentV3(claim) {
|
|
|
750
857
|
reasoningEffort,
|
|
751
858
|
browser,
|
|
752
859
|
},
|
|
860
|
+
gameSource,
|
|
753
861
|
};
|
|
754
862
|
}
|
|
755
|
-
function
|
|
863
|
+
function normalizeWorkerClaimResponseV5(value) {
|
|
756
864
|
const response = requireClaimRecord(value);
|
|
757
|
-
|
|
865
|
+
if (response.protocolVersion !== 5)
|
|
866
|
+
throw new Error("agent_task_claim_protocol_version_mismatch");
|
|
758
867
|
const updatePolicy = response.updatePolicy;
|
|
759
868
|
if (updatePolicy !== null && (typeof updatePolicy !== "object" || Array.isArray(updatePolicy))) {
|
|
760
869
|
throw new Error("agent_task_claim_response_malformed");
|
|
761
870
|
}
|
|
762
871
|
if (response.action === "idle" || response.action === "quiesce_for_update") {
|
|
872
|
+
if (response.assignment !== null || response.lease !== null)
|
|
873
|
+
throw new Error("agent_task_claim_response_malformed");
|
|
763
874
|
return {
|
|
764
|
-
protocolVersion:
|
|
875
|
+
protocolVersion: 5,
|
|
765
876
|
action: response.action,
|
|
766
877
|
assignment: null,
|
|
767
878
|
lease: null,
|
|
768
879
|
updatePolicy: updatePolicy,
|
|
769
880
|
};
|
|
770
881
|
}
|
|
771
|
-
if (
|
|
772
|
-
throw new Error("
|
|
882
|
+
if (response.action !== "run")
|
|
883
|
+
throw new Error("agent_task_claim_response_malformed");
|
|
884
|
+
const rawAssignment = requireClaimRecord(response.assignment, "agent_task_assignment_invalid");
|
|
885
|
+
let assignment;
|
|
886
|
+
if (rawAssignment.operation === "GAME") {
|
|
887
|
+
const { operation: _operation, ...gameAssignment } = rawAssignment;
|
|
888
|
+
const game = normalizeWorkerGameTaskAssignment(gameAssignment);
|
|
889
|
+
assignment = { operation: "GAME", ...game };
|
|
890
|
+
}
|
|
891
|
+
else if (rawAssignment.operation === "SCREENING") {
|
|
892
|
+
const task = requireClaimRecord(rawAssignment.task, "agent_task_assignment_invalid");
|
|
893
|
+
const request = requireClaimRecord(rawAssignment.request, "agent_task_assignment_invalid");
|
|
894
|
+
const execution = requireClaimRecord(rawAssignment.execution, "agent_task_assignment_invalid");
|
|
895
|
+
const taskId = parsePositiveInteger(task.id, "agent_task_assignment_task_id_missing");
|
|
896
|
+
const token = typeof task.token === "string" ? task.token.trim() : "";
|
|
897
|
+
const accessToken = typeof task.accessToken === "string" ? task.accessToken.trim() : "";
|
|
898
|
+
const taskType = typeof task.type === "string" ? task.type.trim().toUpperCase() : "";
|
|
899
|
+
const prompt = typeof request.prompt === "string" ? request.prompt : "";
|
|
900
|
+
if (!token ||
|
|
901
|
+
!accessToken ||
|
|
902
|
+
task.attempt !== 0 ||
|
|
903
|
+
!["NEW_GAME", "STATIC_GAME", "REMIX_GAME", "GAME_UPDATE"].includes(taskType) ||
|
|
904
|
+
!prompt.trim() ||
|
|
905
|
+
!Array.isArray(request.attachments) ||
|
|
906
|
+
!request.outputSchema ||
|
|
907
|
+
typeof request.outputSchema !== "object" ||
|
|
908
|
+
!Array.isArray(rawAssignment.inputs) ||
|
|
909
|
+
execution.provider !== "CODEX" ||
|
|
910
|
+
execution.model !== "gpt-5.6-luna" ||
|
|
911
|
+
execution.reasoningEffort !== "high" ||
|
|
912
|
+
execution.sandbox !== "read-only") {
|
|
913
|
+
throw new Error("agent_task_assignment_invalid");
|
|
914
|
+
}
|
|
915
|
+
assignment = rawAssignment;
|
|
916
|
+
assignment.task.id = taskId;
|
|
917
|
+
}
|
|
918
|
+
else if (rawAssignment.operation === "GENERATION") {
|
|
919
|
+
const task = requireClaimRecord(rawAssignment.task, "agent_task_assignment_invalid");
|
|
920
|
+
const request = requireClaimRecord(rawAssignment.request, "agent_task_assignment_invalid");
|
|
921
|
+
const execution = requireClaimRecord(rawAssignment.execution, "agent_task_assignment_invalid");
|
|
922
|
+
const taskId = parsePositiveInteger(task.id, "agent_task_assignment_task_id_missing");
|
|
923
|
+
const attempt = parsePositiveInteger(task.attempt, "agent_task_assignment_attempt_missing");
|
|
924
|
+
const token = typeof task.token === "string" ? task.token.trim() : "";
|
|
925
|
+
const accessToken = typeof task.accessToken === "string" ? task.accessToken.trim() : "";
|
|
926
|
+
const taskType = typeof task.type === "string" ? task.type.trim().toUpperCase() : "";
|
|
927
|
+
const requestKind = typeof request.kind === "string" ? request.kind.trim().toUpperCase() : "";
|
|
928
|
+
const provider = execution.provider;
|
|
929
|
+
const model = typeof execution.model === "string" ? execution.model.trim() : "";
|
|
930
|
+
if (!token ||
|
|
931
|
+
!accessToken ||
|
|
932
|
+
!["TEXT", "JSON", "IMAGE", "MODEL_3D", "VIDEO", "SFX", "SPEECH", "MUSIC"].includes(taskType) ||
|
|
933
|
+
requestKind !== taskType ||
|
|
934
|
+
(provider !== "CODEX" && provider !== "FAL") ||
|
|
935
|
+
!model ||
|
|
936
|
+
!Array.isArray(rawAssignment.inputs)) {
|
|
937
|
+
throw new Error("agent_task_assignment_invalid");
|
|
938
|
+
}
|
|
939
|
+
assignment = rawAssignment;
|
|
940
|
+
assignment.task.id = taskId;
|
|
941
|
+
assignment.task.attempt = attempt;
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
throw new Error("agent_task_assignment_operation_invalid");
|
|
773
945
|
}
|
|
774
946
|
const lease = requireClaimRecord(response.lease, "agent_task_claim_missing_lease_token");
|
|
775
|
-
const
|
|
947
|
+
const leaseToken = typeof lease.token === "string" ? lease.token.trim() : "";
|
|
776
948
|
const expiresAt = typeof lease.expiresAt === "string" ? lease.expiresAt.trim() : "";
|
|
777
|
-
if (!
|
|
949
|
+
if (!leaseToken || !expiresAt || Number.isNaN(Date.parse(expiresAt))) {
|
|
778
950
|
throw new Error("agent_task_claim_missing_lease_token");
|
|
779
951
|
}
|
|
780
952
|
return {
|
|
781
|
-
protocolVersion:
|
|
953
|
+
protocolVersion: 5,
|
|
782
954
|
action: "run",
|
|
783
955
|
assignment,
|
|
784
|
-
lease: { token, expiresAt },
|
|
956
|
+
lease: { token: leaseToken, expiresAt },
|
|
785
957
|
updatePolicy: updatePolicy,
|
|
786
958
|
};
|
|
787
959
|
}
|
|
@@ -837,6 +1009,10 @@ function resolveWorkerHomeDir() {
|
|
|
837
1009
|
}
|
|
838
1010
|
return node_path_1.default.join(node_os_1.default.homedir(), "PlayDropWorker");
|
|
839
1011
|
}
|
|
1012
|
+
async function configureWorkerFal() {
|
|
1013
|
+
await (0, generation_1.configureFalCredential)(resolveWorkerHomeDir());
|
|
1014
|
+
(0, output_1.printSuccess)("Fal is configured for this PlayDrop worker.");
|
|
1015
|
+
}
|
|
840
1016
|
function workerStateFilePath() {
|
|
841
1017
|
return node_path_1.default.join(resolveWorkerStateDir(), "current-task.json");
|
|
842
1018
|
}
|
|
@@ -853,13 +1029,24 @@ async function pruneWorkerTaskWorkspaces(incomingWorkspaceDir, retention = expor
|
|
|
853
1029
|
return [];
|
|
854
1030
|
}
|
|
855
1031
|
const incoming = node_path_1.default.resolve(incomingWorkspaceDir);
|
|
1032
|
+
const retainedCutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
856
1033
|
const entries = (0, node_fs_1.readdirSync)(tasksRoot, { withFileTypes: true })
|
|
857
1034
|
.filter((entry) => entry.isDirectory())
|
|
858
1035
|
.map((entry) => node_path_1.default.join(tasksRoot, entry.name))
|
|
859
1036
|
.filter((dir) => dir !== incoming)
|
|
860
|
-
.map((dir) =>
|
|
1037
|
+
.map((dir) => {
|
|
1038
|
+
const marker = node_path_1.default.join(dir, ".playdrop", "game-source-retained.json");
|
|
1039
|
+
const markerMtime = (0, node_fs_1.existsSync)(marker) ? (0, node_fs_1.statSync)(marker).mtimeMs : null;
|
|
1040
|
+
return {
|
|
1041
|
+
dir,
|
|
1042
|
+
mtimeMs: (0, node_fs_1.statSync)(dir).mtimeMs,
|
|
1043
|
+
retainedGameSource: markerMtime !== null && markerMtime >= retainedCutoff,
|
|
1044
|
+
};
|
|
1045
|
+
})
|
|
861
1046
|
.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
862
|
-
const stale = entries
|
|
1047
|
+
const stale = entries
|
|
1048
|
+
.filter((entry) => !entry.retainedGameSource)
|
|
1049
|
+
.slice(Math.max(0, retention - 1));
|
|
863
1050
|
const removed = [];
|
|
864
1051
|
for (const entry of stale) {
|
|
865
1052
|
await (0, promises_1.rm)(entry.dir, { recursive: true, force: true });
|
|
@@ -889,11 +1076,15 @@ async function prepareClaimedTaskWorkspace(input) {
|
|
|
889
1076
|
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, ".playdrop", "task-events"), { recursive: true, force: true }),
|
|
890
1077
|
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, ".playdrop", "task-upload.lock"), { force: true }),
|
|
891
1078
|
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, ".playdrop", "task-upload-result.json"), { force: true }),
|
|
1079
|
+
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, ".playdrop", "task-upload-failure.json"), { force: true }),
|
|
892
1080
|
...retainedProjectBindings.map((file) => (0, promises_1.rm)(file, { force: true })),
|
|
893
1081
|
]);
|
|
894
1082
|
return { resumed: true };
|
|
895
1083
|
}
|
|
896
1084
|
function buildWorkerTaskContext(input) {
|
|
1085
|
+
if (input.target === "FIRST_PARTY" && !input.taskAccessToken?.trim()) {
|
|
1086
|
+
throw new Error("agent_task_access_token_missing");
|
|
1087
|
+
}
|
|
897
1088
|
const playdrop = input.metadata.playdrop;
|
|
898
1089
|
if (!playdrop || typeof playdrop !== "object" || Array.isArray(playdrop)) {
|
|
899
1090
|
throw new Error("agent_task_context_playdrop_metadata_missing");
|
|
@@ -937,6 +1128,7 @@ function buildWorkerTaskContext(input) {
|
|
|
937
1128
|
taskId: input.taskId,
|
|
938
1129
|
attempt: input.attempt,
|
|
939
1130
|
taskToken: input.taskToken,
|
|
1131
|
+
taskAccessToken: input.taskAccessToken?.trim() || null,
|
|
940
1132
|
taskType: input.taskType,
|
|
941
1133
|
metadata: input.metadata,
|
|
942
1134
|
kind,
|
|
@@ -951,6 +1143,7 @@ function buildWorkerTaskContext(input) {
|
|
|
951
1143
|
remixSourceRef,
|
|
952
1144
|
allowedTemplateKeys,
|
|
953
1145
|
reviewAppVersionId,
|
|
1146
|
+
gameSourceGit: input.gameSourceGit === true,
|
|
954
1147
|
};
|
|
955
1148
|
}
|
|
956
1149
|
function buildWorkerTaskContextV2(assignment) {
|
|
@@ -958,10 +1151,12 @@ function buildWorkerTaskContextV2(assignment) {
|
|
|
958
1151
|
taskId: assignment.task.id,
|
|
959
1152
|
attempt: assignment.task.attempt,
|
|
960
1153
|
taskToken: assignment.task.token,
|
|
1154
|
+
taskAccessToken: assignment.task.accessToken ?? null,
|
|
961
1155
|
taskType: assignment.task.type,
|
|
962
1156
|
metadata: assignment.task.metadata,
|
|
963
1157
|
prompt: assignment.request.prompt,
|
|
964
1158
|
target: assignment.task.executionTarget,
|
|
1159
|
+
gameSourceGit: Boolean("gameSource" in assignment && assignment.gameSource),
|
|
965
1160
|
});
|
|
966
1161
|
}
|
|
967
1162
|
function normalizeWorkerInstrumentSurface(value) {
|
|
@@ -1040,9 +1235,30 @@ function workerTaskContextPath(workspaceDir) {
|
|
|
1040
1235
|
function workerTaskUploadResultPath(workspaceDir) {
|
|
1041
1236
|
return node_path_1.default.join(workspaceDir, ".playdrop", "task-upload-result.json");
|
|
1042
1237
|
}
|
|
1238
|
+
function workerTaskUploadFailurePath(workspaceDir) {
|
|
1239
|
+
return node_path_1.default.join(workspaceDir, ".playdrop", "task-upload-failure.json");
|
|
1240
|
+
}
|
|
1043
1241
|
function workerTaskMaterialsPath(workspaceDir) {
|
|
1044
1242
|
return node_path_1.default.join(workspaceDir, ".playdrop", "task-materials.json");
|
|
1045
1243
|
}
|
|
1244
|
+
function workerGameSourceReadinessPath(workspaceDir) {
|
|
1245
|
+
return node_path_1.default.join(workspaceDir, ".playdrop", "game-source-ready.json");
|
|
1246
|
+
}
|
|
1247
|
+
function workerGameSourceCompletionPath(workspaceDir) {
|
|
1248
|
+
return node_path_1.default.join(workspaceDir, ".playdrop", "game-source-completion.json");
|
|
1249
|
+
}
|
|
1250
|
+
function readWorkerGameSourceCompletion(workspaceDir) {
|
|
1251
|
+
const file = workerGameSourceCompletionPath(workspaceDir);
|
|
1252
|
+
if (!(0, node_fs_1.existsSync)(file))
|
|
1253
|
+
return null;
|
|
1254
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
|
|
1255
|
+
if (!Number.isSafeInteger(parsed.taskId) || Number(parsed.taskId) <= 0 ||
|
|
1256
|
+
!Number.isSafeInteger(parsed.attempt) || Number(parsed.attempt) <= 0 ||
|
|
1257
|
+
typeof parsed.summary !== "string" || !parsed.summary.trim()) {
|
|
1258
|
+
throw new Error("game_source_completion_intent_invalid");
|
|
1259
|
+
}
|
|
1260
|
+
return parsed;
|
|
1261
|
+
}
|
|
1046
1262
|
function serializeWorkerTaskContext(taskContext) {
|
|
1047
1263
|
const playdrop = taskContext.metadata.playdrop;
|
|
1048
1264
|
if (!playdrop || typeof playdrop !== "object" || Array.isArray(playdrop)) {
|
|
@@ -1072,6 +1288,7 @@ function serializeWorkerTaskContext(taskContext) {
|
|
|
1072
1288
|
id: taskContext.taskId,
|
|
1073
1289
|
attempt: taskContext.attempt,
|
|
1074
1290
|
token: taskContext.taskToken,
|
|
1291
|
+
...(taskContext.taskAccessToken ? { accessToken: taskContext.taskAccessToken } : {}),
|
|
1075
1292
|
type: taskContext.taskType,
|
|
1076
1293
|
executionTarget: taskContext.target,
|
|
1077
1294
|
metadata,
|
|
@@ -1082,6 +1299,7 @@ function serializeWorkerTaskContext(taskContext) {
|
|
|
1082
1299
|
worker: {
|
|
1083
1300
|
devPort: taskContext.devPort,
|
|
1084
1301
|
env: taskContext.env,
|
|
1302
|
+
gameSourceGit: taskContext.gameSourceGit,
|
|
1085
1303
|
},
|
|
1086
1304
|
};
|
|
1087
1305
|
}
|
|
@@ -1098,7 +1316,7 @@ async function stageWorkerTaskContext(input) {
|
|
|
1098
1316
|
async function writeWorkerTaskContextFile(workspaceDir, taskContext) {
|
|
1099
1317
|
await (0, promises_1.writeFile)(workerTaskContextPath(workspaceDir), JSON.stringify(serializeWorkerTaskContext(taskContext), null, 2), "utf8");
|
|
1100
1318
|
}
|
|
1101
|
-
function
|
|
1319
|
+
function findOptionalWorkspaceTaskFile(startDir, basename) {
|
|
1102
1320
|
let current = node_path_1.default.resolve(startDir);
|
|
1103
1321
|
while (true) {
|
|
1104
1322
|
const candidate = node_path_1.default.join(current, ".playdrop", basename);
|
|
@@ -1107,11 +1325,18 @@ function findWorkspaceTaskFile(startDir, basename) {
|
|
|
1107
1325
|
}
|
|
1108
1326
|
const parent = node_path_1.default.dirname(current);
|
|
1109
1327
|
if (parent === current) {
|
|
1110
|
-
|
|
1328
|
+
return null;
|
|
1111
1329
|
}
|
|
1112
1330
|
current = parent;
|
|
1113
1331
|
}
|
|
1114
1332
|
}
|
|
1333
|
+
function findWorkspaceTaskFile(startDir, basename) {
|
|
1334
|
+
const file = findOptionalWorkspaceTaskFile(startDir, basename);
|
|
1335
|
+
if (!file) {
|
|
1336
|
+
throw new Error(`task_context_missing:${basename}`);
|
|
1337
|
+
}
|
|
1338
|
+
return file;
|
|
1339
|
+
}
|
|
1115
1340
|
function readTaskContextFile(startDir = node_process_1.default.cwd()) {
|
|
1116
1341
|
const file = findWorkspaceTaskFile(startDir, "task.json");
|
|
1117
1342
|
const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
|
|
@@ -1121,6 +1346,7 @@ function readTaskContextFile(startDir = node_process_1.default.cwd()) {
|
|
|
1121
1346
|
const taskId = Number(task?.id);
|
|
1122
1347
|
const attempt = Number(task?.attempt);
|
|
1123
1348
|
const taskToken = typeof task?.token === "string" ? task.token.trim() : "";
|
|
1349
|
+
const taskAccessToken = typeof task?.accessToken === "string" ? task.accessToken.trim() : "";
|
|
1124
1350
|
const taskType = typeof task?.type === "string" ? task.type.trim() : "";
|
|
1125
1351
|
const metadata = task?.metadata && typeof task.metadata === "object" && !Array.isArray(task.metadata) ? task.metadata : null;
|
|
1126
1352
|
const prompt = typeof request?.prompt === "string" ? request.prompt : "";
|
|
@@ -1132,6 +1358,7 @@ function readTaskContextFile(startDir = node_process_1.default.cwd()) {
|
|
|
1132
1358
|
!Number.isInteger(attempt) ||
|
|
1133
1359
|
attempt <= 0 ||
|
|
1134
1360
|
!taskToken ||
|
|
1361
|
+
(task?.executionTarget === "FIRST_PARTY" && !taskAccessToken) ||
|
|
1135
1362
|
!taskType ||
|
|
1136
1363
|
!metadata ||
|
|
1137
1364
|
(task?.executionTarget !== "FIRST_PARTY" && task?.executionTarget !== "PERSONAL") ||
|
|
@@ -1148,10 +1375,12 @@ function readTaskContextFile(startDir = node_process_1.default.cwd()) {
|
|
|
1148
1375
|
taskId,
|
|
1149
1376
|
attempt,
|
|
1150
1377
|
taskToken,
|
|
1378
|
+
taskAccessToken,
|
|
1151
1379
|
taskType,
|
|
1152
1380
|
metadata,
|
|
1153
1381
|
prompt,
|
|
1154
1382
|
target: task.executionTarget,
|
|
1383
|
+
gameSourceGit: worker?.gameSourceGit === true,
|
|
1155
1384
|
}),
|
|
1156
1385
|
devPort,
|
|
1157
1386
|
env,
|
|
@@ -1165,24 +1394,189 @@ function hasAgentTaskUploadedArtifact(task) {
|
|
|
1165
1394
|
return ((Number.isInteger(task.appId) && Number(task.appId) > 0) ||
|
|
1166
1395
|
(Number.isInteger(task.appVersionId) && Number(task.appVersionId) > 0));
|
|
1167
1396
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1397
|
+
function buildWorkerTranscriptUploadHeaders(input) {
|
|
1398
|
+
return {
|
|
1399
|
+
...input.headers,
|
|
1400
|
+
"content-type": input.contentType,
|
|
1401
|
+
"content-md5": input.md5Base64,
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
async function archiveWorkerTranscriptBestEffort(input) {
|
|
1405
|
+
if (!input.transcriptPath || !(0, node_fs_1.existsSync)(input.transcriptPath))
|
|
1406
|
+
return;
|
|
1407
|
+
try {
|
|
1408
|
+
const content = (0, node_fs_1.readFileSync)(input.transcriptPath);
|
|
1409
|
+
const contentSha256 = (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
|
|
1410
|
+
const contentMd5Base64 = (0, node_crypto_1.createHash)("md5").update(content).digest("base64");
|
|
1411
|
+
const request = {
|
|
1175
1412
|
workerKey: input.workerKey,
|
|
1176
1413
|
leaseToken: input.leaseToken,
|
|
1177
|
-
|
|
1178
|
-
|
|
1414
|
+
attempt: input.attempt,
|
|
1415
|
+
sha256: contentSha256,
|
|
1416
|
+
md5Base64: contentMd5Base64,
|
|
1417
|
+
bytes: content.length,
|
|
1418
|
+
};
|
|
1419
|
+
const prepared = await input.client.workerPrepareAgentTaskTranscript(input.taskId, request);
|
|
1420
|
+
if (prepared.upload) {
|
|
1421
|
+
const response = await fetch(prepared.upload.url, {
|
|
1422
|
+
method: "PUT",
|
|
1423
|
+
headers: buildWorkerTranscriptUploadHeaders({
|
|
1424
|
+
headers: prepared.upload.headers,
|
|
1425
|
+
contentType: prepared.upload.contentType,
|
|
1426
|
+
md5Base64: contentMd5Base64,
|
|
1427
|
+
}),
|
|
1428
|
+
body: Uint8Array.from(content),
|
|
1429
|
+
});
|
|
1430
|
+
if (!response.ok)
|
|
1431
|
+
throw new Error(`agent_task_transcript_upload_failed:${response.status}`);
|
|
1432
|
+
}
|
|
1433
|
+
await input.client.workerCompleteAgentTaskTranscript(input.taskId, request);
|
|
1434
|
+
}
|
|
1435
|
+
catch (error) {
|
|
1436
|
+
const archiveDir = node_path_1.default.join(resolveWorkerHomeDir(), "transcripts", "unarchived");
|
|
1437
|
+
try {
|
|
1438
|
+
(0, node_fs_1.mkdirSync)(archiveDir, { recursive: true });
|
|
1439
|
+
const retainedPath = node_path_1.default.join(archiveDir, `task-${input.taskId}-attempt-${input.attempt}.ndjson`);
|
|
1440
|
+
await (0, promises_1.copyFile)(input.transcriptPath, retainedPath);
|
|
1441
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
1442
|
+
for (const entry of (0, node_fs_1.readdirSync)(archiveDir)) {
|
|
1443
|
+
const candidate = node_path_1.default.join(archiveDir, entry);
|
|
1444
|
+
if ((0, node_fs_1.statSync)(candidate).isFile() && (0, node_fs_1.statSync)(candidate).mtimeMs < cutoff) {
|
|
1445
|
+
(0, node_fs_1.rmSync)(candidate, { force: true });
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
catch (retentionError) {
|
|
1450
|
+
console.error(`Transcript spool retention failed: ${retentionError instanceof Error ? retentionError.message : String(retentionError)}`);
|
|
1451
|
+
}
|
|
1452
|
+
console.error(`Transcript archival did not complete; the local spool was retained for seven days: ${error instanceof Error ? error.message : String(error)}`);
|
|
1179
1453
|
}
|
|
1180
1454
|
}
|
|
1181
1455
|
function resolveTaskWorkspaceDir(startDir = node_process_1.default.cwd()) {
|
|
1182
1456
|
return node_path_1.default.dirname(node_path_1.default.dirname(findWorkspaceTaskFile(startDir, "task.json")));
|
|
1183
1457
|
}
|
|
1458
|
+
function describeTaskUploadFailure(error) {
|
|
1459
|
+
const fullRawError = (error instanceof Error ? error.message : String(error)).trim() || "task_upload_failed";
|
|
1460
|
+
const errorText = fullRawError.slice(0, 40000);
|
|
1461
|
+
const explicitCode = (/^([a-z0-9_.-]+):/.exec(errorText)?.[1] ||
|
|
1462
|
+
(error instanceof types_1.ApiError && typeof error.code === "string" ? error.code.trim() : "") ||
|
|
1463
|
+
"").slice(0, 160);
|
|
1464
|
+
if (explicitCode === "agent_task_playtest_smoke_check_not_interactive") {
|
|
1465
|
+
return {
|
|
1466
|
+
kind: "failure",
|
|
1467
|
+
category: "upload_validation",
|
|
1468
|
+
code: explicitCode,
|
|
1469
|
+
message: "We built the game, but PlayDrop could not verify that its recorded controls changed the game, so it was not uploaded. Retry to repair the controls or playtest recording.",
|
|
1470
|
+
retryable: true,
|
|
1471
|
+
source: "agent",
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
let terminalReason = {
|
|
1475
|
+
...(0, types_1.classifyAgentTaskTerminalReason)(errorText, "worker"),
|
|
1476
|
+
...(explicitCode ? { code: explicitCode } : {}),
|
|
1477
|
+
};
|
|
1478
|
+
if (error instanceof types_1.ApiError && error.status === 429) {
|
|
1479
|
+
terminalReason = {
|
|
1480
|
+
...terminalReason,
|
|
1481
|
+
category: "provider_capacity",
|
|
1482
|
+
code: explicitCode || "task_upload_rate_limited",
|
|
1483
|
+
retryable: true,
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
else if (error instanceof types_1.ApiError && error.status >= 500) {
|
|
1487
|
+
terminalReason = {
|
|
1488
|
+
...terminalReason,
|
|
1489
|
+
category: "agent_failure",
|
|
1490
|
+
code: explicitCode || "task_upload_platform_error",
|
|
1491
|
+
retryable: true,
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
let creatorMessage = null;
|
|
1495
|
+
if (error instanceof types_1.ApiError && error.status >= 500) {
|
|
1496
|
+
creatorMessage = "PlayDrop could not complete the game upload.";
|
|
1497
|
+
}
|
|
1498
|
+
else if (terminalReason.category === "provider_connection") {
|
|
1499
|
+
creatorMessage = "PlayDrop could not be reached while uploading the game.";
|
|
1500
|
+
}
|
|
1501
|
+
else if (terminalReason.category === "provider_timeout") {
|
|
1502
|
+
creatorMessage = "The game upload did not finish before the timeout.";
|
|
1503
|
+
}
|
|
1504
|
+
else if (terminalReason.category === "provider_capacity") {
|
|
1505
|
+
creatorMessage = "PlayDrop could not accept the game upload yet.";
|
|
1506
|
+
}
|
|
1507
|
+
if (creatorMessage) {
|
|
1508
|
+
return {
|
|
1509
|
+
...terminalReason,
|
|
1510
|
+
message: creatorMessage,
|
|
1511
|
+
source: "worker",
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
return terminalReason;
|
|
1515
|
+
}
|
|
1516
|
+
async function recordTaskUploadFailure(input) {
|
|
1517
|
+
const failure = {
|
|
1518
|
+
schemaVersion: 2,
|
|
1519
|
+
taskId: input.taskContext.taskId,
|
|
1520
|
+
attempt: input.taskContext.attempt,
|
|
1521
|
+
terminalReason: describeTaskUploadFailure(input.error),
|
|
1522
|
+
failedAt: new Date().toISOString(),
|
|
1523
|
+
};
|
|
1524
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(workerTaskUploadFailurePath(input.workspaceDir)), { recursive: true });
|
|
1525
|
+
await (0, promises_1.writeFile)(workerTaskUploadFailurePath(input.workspaceDir), JSON.stringify(failure, null, 2), "utf8");
|
|
1526
|
+
return failure;
|
|
1527
|
+
}
|
|
1528
|
+
function readTaskUploadFailureFile(startDir = node_process_1.default.cwd()) {
|
|
1529
|
+
const file = findOptionalWorkspaceTaskFile(startDir, "task-upload-failure.json");
|
|
1530
|
+
if (!file)
|
|
1531
|
+
return null;
|
|
1532
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
|
|
1533
|
+
const taskId = Number(parsed.taskId);
|
|
1534
|
+
const attempt = Number(parsed.attempt);
|
|
1535
|
+
const terminalReasonValue = parsed.terminalReason;
|
|
1536
|
+
const code = typeof terminalReasonValue?.code === "string" ? terminalReasonValue.code.trim() : "";
|
|
1537
|
+
const message = typeof terminalReasonValue?.message === "string" ? terminalReasonValue.message.trim() : "";
|
|
1538
|
+
const failedAt = typeof parsed.failedAt === "string" ? parsed.failedAt.trim() : "";
|
|
1539
|
+
if (parsed.schemaVersion !== 2 ||
|
|
1540
|
+
!Number.isInteger(taskId) ||
|
|
1541
|
+
taskId <= 0 ||
|
|
1542
|
+
!Number.isInteger(attempt) ||
|
|
1543
|
+
attempt <= 0 ||
|
|
1544
|
+
terminalReasonValue?.kind !== "failure" ||
|
|
1545
|
+
!TASK_UPLOAD_FAILURE_CATEGORIES.has(terminalReasonValue.category) ||
|
|
1546
|
+
!code ||
|
|
1547
|
+
code.length > 160 ||
|
|
1548
|
+
!message ||
|
|
1549
|
+
message.length > 600 ||
|
|
1550
|
+
typeof terminalReasonValue.retryable !== "boolean" ||
|
|
1551
|
+
!TASK_UPLOAD_FAILURE_SOURCES.has(terminalReasonValue.source) ||
|
|
1552
|
+
!failedAt ||
|
|
1553
|
+
!Number.isFinite(Date.parse(failedAt))) {
|
|
1554
|
+
throw new Error(`task_upload_failure_invalid:${file}`);
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
schemaVersion: 2,
|
|
1558
|
+
taskId,
|
|
1559
|
+
attempt,
|
|
1560
|
+
terminalReason: {
|
|
1561
|
+
kind: "failure",
|
|
1562
|
+
category: terminalReasonValue.category,
|
|
1563
|
+
code,
|
|
1564
|
+
message,
|
|
1565
|
+
retryable: terminalReasonValue.retryable,
|
|
1566
|
+
source: terminalReasonValue.source,
|
|
1567
|
+
},
|
|
1568
|
+
failedAt,
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1184
1571
|
function readTaskUploadResultFile(startDir = node_process_1.default.cwd()) {
|
|
1185
|
-
const file =
|
|
1572
|
+
const file = findOptionalWorkspaceTaskFile(startDir, "task-upload-result.json");
|
|
1573
|
+
if (!file) {
|
|
1574
|
+
const failure = readTaskUploadFailureFile(startDir);
|
|
1575
|
+
if (failure) {
|
|
1576
|
+
throw new Error(`task_done_blocked_by_upload_failure:${failure.terminalReason.code}: ${failure.terminalReason.message}`);
|
|
1577
|
+
}
|
|
1578
|
+
throw new Error("task_context_missing:task-upload-result.json");
|
|
1579
|
+
}
|
|
1186
1580
|
const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
|
|
1187
1581
|
const taskId = Number(parsed.taskId);
|
|
1188
1582
|
const appId = Number(parsed.appId);
|
|
@@ -1380,34 +1774,7 @@ function resolveWorkspaceFileDestination(workspaceDir, relativePath) {
|
|
|
1380
1774
|
}
|
|
1381
1775
|
return destination;
|
|
1382
1776
|
}
|
|
1383
|
-
async function
|
|
1384
|
-
for (const file of workspace.files) {
|
|
1385
|
-
const normalized = normalizeAssignmentContent({
|
|
1386
|
-
path: file.path,
|
|
1387
|
-
contentBase64: file.contentBase64,
|
|
1388
|
-
sha256: file.sha256,
|
|
1389
|
-
sizeBytes: file.sizeBytes,
|
|
1390
|
-
code: "agent_task_assignment_workspace_file",
|
|
1391
|
-
});
|
|
1392
|
-
const destination = resolveWorkspaceFileDestination(workspaceDir, normalized.path);
|
|
1393
|
-
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
1394
|
-
await (0, promises_1.writeFile)(destination, normalized.buffer);
|
|
1395
|
-
}
|
|
1396
|
-
for (const archive of workspace.archives) {
|
|
1397
|
-
const normalized = normalizeAssignmentContent({
|
|
1398
|
-
path: archive.path,
|
|
1399
|
-
contentBase64: archive.contentBase64,
|
|
1400
|
-
sha256: archive.sha256,
|
|
1401
|
-
sizeBytes: archive.sizeBytes,
|
|
1402
|
-
code: "agent_task_assignment_workspace_archive",
|
|
1403
|
-
});
|
|
1404
|
-
const destination = resolveWorkspaceFileDestination(workspaceDir, normalized.path);
|
|
1405
|
-
await (0, promises_1.mkdir)(destination, { recursive: true });
|
|
1406
|
-
(0, archive_staging_1.extractZipArchive)(new Uint8Array(normalized.buffer), destination);
|
|
1407
|
-
await removeStaleWorkerControlFiles(destination);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
async function stageAssignmentWorkspaceV3(workspaceDir, workspace, options = {}) {
|
|
1777
|
+
async function stageAssignmentWorkspace(workspaceDir, workspace, options = {}) {
|
|
1411
1778
|
for (const file of workspace.files) {
|
|
1412
1779
|
const normalized = normalizeAssignmentContent({
|
|
1413
1780
|
path: file.path,
|
|
@@ -1425,6 +1792,7 @@ async function stageAssignmentWorkspaceV3(workspaceDir, workspace, options = {})
|
|
|
1425
1792
|
await (0, archive_staging_1.stageWorkspaceArchiveInChild)({
|
|
1426
1793
|
downloadUrl: normalizeWorkspaceArchiveDownloadUrl(archive.downloadUrl),
|
|
1427
1794
|
expectedSize: Number(archive.sizeBytes),
|
|
1795
|
+
expectedSha256: archive.sha256,
|
|
1428
1796
|
targetDir: destination,
|
|
1429
1797
|
timeoutMs: options.timeoutMs ?? exports.WORKSPACE_ARCHIVE_DOWNLOAD_TIMEOUT_MS,
|
|
1430
1798
|
});
|
|
@@ -1975,13 +2343,15 @@ function buildWorkerCapabilities(input) {
|
|
|
1975
2343
|
: null;
|
|
1976
2344
|
const maxParallelTasks = input.maxParallelTasks ?? DEFAULT_WORKER_MAX_PARALLEL_TASKS;
|
|
1977
2345
|
const runningTaskCount = input.runningTaskCount ?? 0;
|
|
2346
|
+
const requireGenerationRuntime = input.requireGenerationRuntime === true;
|
|
1978
2347
|
const degradedReasons = [];
|
|
2348
|
+
const gameSourceInventory = (0, game_source_git_1.readGameSourceWorkerInventory)(resolveWorkerHomeDir());
|
|
1979
2349
|
const agents = [];
|
|
1980
2350
|
if (codexVersion) {
|
|
1981
|
-
if (!codexAuthenticated) {
|
|
2351
|
+
if (requireGenerationRuntime && !codexAuthenticated) {
|
|
1982
2352
|
degradedReasons.push("codex_not_authenticated");
|
|
1983
2353
|
}
|
|
1984
|
-
if (codexModels.length === 0) {
|
|
2354
|
+
if (requireGenerationRuntime && codexModels.length === 0) {
|
|
1985
2355
|
degradedReasons.push("codex_models_unavailable");
|
|
1986
2356
|
}
|
|
1987
2357
|
agents.push({
|
|
@@ -1994,12 +2364,6 @@ function buildWorkerCapabilities(input) {
|
|
|
1994
2364
|
});
|
|
1995
2365
|
}
|
|
1996
2366
|
if (claudeVersion) {
|
|
1997
|
-
if (!claudeAuthenticated) {
|
|
1998
|
-
degradedReasons.push("claude_not_authenticated");
|
|
1999
|
-
}
|
|
2000
|
-
if (claudeModels.length === 0) {
|
|
2001
|
-
degradedReasons.push("claude_models_unavailable");
|
|
2002
|
-
}
|
|
2003
2367
|
agents.push({
|
|
2004
2368
|
agent: "CLAUDE_CODE",
|
|
2005
2369
|
cliVersion: claudeVersion,
|
|
@@ -2010,12 +2374,6 @@ function buildWorkerCapabilities(input) {
|
|
|
2010
2374
|
});
|
|
2011
2375
|
}
|
|
2012
2376
|
if (grokVersion) {
|
|
2013
|
-
if (!grokAuthenticated) {
|
|
2014
|
-
degradedReasons.push("grok_not_authenticated");
|
|
2015
|
-
}
|
|
2016
|
-
if (grokModels.length === 0) {
|
|
2017
|
-
degradedReasons.push("grok_models_unavailable");
|
|
2018
|
-
}
|
|
2019
2377
|
agents.push({
|
|
2020
2378
|
agent: "GROK_BUILD",
|
|
2021
2379
|
cliVersion: grokVersion,
|
|
@@ -2026,10 +2384,6 @@ function buildWorkerCapabilities(input) {
|
|
|
2026
2384
|
});
|
|
2027
2385
|
}
|
|
2028
2386
|
if (cursorVersion) {
|
|
2029
|
-
degradedReasons.push("cursor_runner_not_configured");
|
|
2030
|
-
if (!cursorAuthenticated) {
|
|
2031
|
-
degradedReasons.push("cursor_not_authenticated");
|
|
2032
|
-
}
|
|
2033
2387
|
agents.push({
|
|
2034
2388
|
agent: "CURSOR_COMPOSER",
|
|
2035
2389
|
cliVersion: cursorVersion,
|
|
@@ -2041,17 +2395,120 @@ function buildWorkerCapabilities(input) {
|
|
|
2041
2395
|
if (agents.length === 0) {
|
|
2042
2396
|
throw new Error("agent_cli_not_found: install and authenticate Codex, Claude Code, Grok Build, or Cursor before starting a PlayDrop worker.");
|
|
2043
2397
|
}
|
|
2398
|
+
const codexReady = agents.some((agent) => agent.agent === "CODEX" && agent.ready === true);
|
|
2399
|
+
const codexImageGenerationAvailable = input.codexImageGenerationAvailable === true;
|
|
2400
|
+
const falConfigured = input.falConfigured ?? (0, generation_1.hasFalCredential)(resolveWorkerHomeDir());
|
|
2401
|
+
const falAuthenticated = input.falAuthenticated ?? falConfigured;
|
|
2402
|
+
const ffmpegReady = input.ffmpegReady ?? (workerCommandExists("ffmpeg") && workerCommandExists("ffprobe"));
|
|
2403
|
+
const generationProviders = [];
|
|
2404
|
+
if (codexReady) {
|
|
2405
|
+
generationProviders.push({
|
|
2406
|
+
provider: "CODEX",
|
|
2407
|
+
kind: "TEXT",
|
|
2408
|
+
model: "gpt-5.6-luna",
|
|
2409
|
+
sourceModes: ["TEXT"],
|
|
2410
|
+
authenticated: true,
|
|
2411
|
+
ready: true,
|
|
2412
|
+
features: ["SANDBOXED_EXEC"],
|
|
2413
|
+
}, {
|
|
2414
|
+
provider: "CODEX",
|
|
2415
|
+
kind: "JSON",
|
|
2416
|
+
model: "gpt-5.6-luna",
|
|
2417
|
+
sourceModes: ["TEXT"],
|
|
2418
|
+
authenticated: true,
|
|
2419
|
+
ready: true,
|
|
2420
|
+
features: ["SANDBOXED_EXEC", "JSON_SCHEMA"],
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
if (codexReady && codexImageGenerationAvailable) {
|
|
2424
|
+
generationProviders.push({
|
|
2425
|
+
provider: "CODEX",
|
|
2426
|
+
kind: "IMAGE",
|
|
2427
|
+
model: "gpt-5.6-luna",
|
|
2428
|
+
sourceModes: ["TEXT", "IMAGE", "MULTI_IMAGE"],
|
|
2429
|
+
authenticated: true,
|
|
2430
|
+
ready: true,
|
|
2431
|
+
features: ["SANDBOXED_EXEC", "IMAGE_GENERATION"],
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
if (requireGenerationRuntime && !codexVersion)
|
|
2435
|
+
degradedReasons.push("codex_not_installed");
|
|
2436
|
+
if (requireGenerationRuntime && codexReady && !codexImageGenerationAvailable) {
|
|
2437
|
+
degradedReasons.push("codex_image_generation_unavailable");
|
|
2438
|
+
}
|
|
2439
|
+
if (falConfigured && falAuthenticated && codexReady && ffmpegReady) {
|
|
2440
|
+
generationProviders.push({
|
|
2441
|
+
provider: "FAL",
|
|
2442
|
+
kind: "SPEECH",
|
|
2443
|
+
model: "fal-ai/elevenlabs/tts/turbo-v2.5",
|
|
2444
|
+
sourceModes: ["TEXT"],
|
|
2445
|
+
authenticated: true,
|
|
2446
|
+
ready: true,
|
|
2447
|
+
features: ["CODEX_METADATA", "FFMPEG", "WAVEFORM"],
|
|
2448
|
+
}, ...[
|
|
2449
|
+
["fal-ai/veo3.1/fast", "TEXT"],
|
|
2450
|
+
["fal-ai/veo3.1/fast/image-to-video", "IMAGE"],
|
|
2451
|
+
["fal-ai/veo3.1/fast/first-last-frame-to-video", "FIRST_LAST_FRAME"],
|
|
2452
|
+
].map(([model, sourceMode]) => ({
|
|
2453
|
+
provider: "FAL",
|
|
2454
|
+
kind: "VIDEO",
|
|
2455
|
+
model,
|
|
2456
|
+
sourceModes: [sourceMode],
|
|
2457
|
+
authenticated: true,
|
|
2458
|
+
ready: true,
|
|
2459
|
+
features: ["CODEX_METADATA", "FFMPEG", "THUMBNAIL"],
|
|
2460
|
+
})), ...[
|
|
2461
|
+
["fal-ai/meshy/v6/text-to-3d", "TEXT"],
|
|
2462
|
+
["fal-ai/meshy/v6/image-to-3d", "IMAGE"],
|
|
2463
|
+
["fal-ai/meshy/v6/multi-image-to-3d", "MULTI_IMAGE"],
|
|
2464
|
+
].map(([model, sourceMode]) => ({
|
|
2465
|
+
provider: "FAL",
|
|
2466
|
+
kind: "MODEL_3D",
|
|
2467
|
+
model,
|
|
2468
|
+
sourceModes: [sourceMode],
|
|
2469
|
+
authenticated: true,
|
|
2470
|
+
ready: true,
|
|
2471
|
+
features: ["CODEX_METADATA", "GLB_VALIDATION", "THUMBNAIL"],
|
|
2472
|
+
})));
|
|
2473
|
+
if (codexImageGenerationAvailable) {
|
|
2474
|
+
generationProviders.push({
|
|
2475
|
+
provider: "FAL",
|
|
2476
|
+
kind: "MUSIC",
|
|
2477
|
+
model: "fal-ai/elevenlabs/music",
|
|
2478
|
+
sourceModes: ["TEXT"],
|
|
2479
|
+
authenticated: true,
|
|
2480
|
+
ready: true,
|
|
2481
|
+
features: ["CODEX_METADATA", "CODEX_IMAGE", "FFMPEG"],
|
|
2482
|
+
}, {
|
|
2483
|
+
provider: "FAL",
|
|
2484
|
+
kind: "SFX",
|
|
2485
|
+
model: "fal-ai/elevenlabs/sound-effects/v2",
|
|
2486
|
+
sourceModes: ["TEXT"],
|
|
2487
|
+
authenticated: true,
|
|
2488
|
+
ready: true,
|
|
2489
|
+
features: ["CODEX_METADATA", "CODEX_IMAGE", "FFMPEG"],
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
const supportedGenerationKinds = new Set(generationProviders.map((provider) => provider.kind));
|
|
2494
|
+
const supportedKinds = WORKER_SUPPORTED_KINDS.filter((kind) => !["TEXT", "JSON", "IMAGE", "MODEL_3D", "VIDEO", "SFX", "SPEECH", "MUSIC"].includes(kind) || supportedGenerationKinds.has(kind));
|
|
2495
|
+
const hasReadyAgent = agents.some((agent) => agent.ready === true);
|
|
2496
|
+
if (!hasReadyAgent && degradedReasons.length === 0)
|
|
2497
|
+
degradedReasons.push("agent_cli_not_authenticated");
|
|
2044
2498
|
return {
|
|
2045
2499
|
agent: agents[0].agent,
|
|
2046
|
-
supportedKinds
|
|
2500
|
+
supportedKinds,
|
|
2047
2501
|
maxParallelTasks,
|
|
2048
2502
|
runningTaskCount,
|
|
2049
2503
|
cliVersion: (0, clientInfo_1.getCliVersion)(),
|
|
2050
2504
|
nodeVersion: node_process_1.default.versions.node,
|
|
2051
2505
|
agents,
|
|
2052
|
-
|
|
2506
|
+
generationProviders,
|
|
2507
|
+
ready: hasReadyAgent && degradedReasons.length === 0,
|
|
2053
2508
|
degradedReasons,
|
|
2054
2509
|
wrapperContractVersion: (0, runtime_1.readPositiveEnvInt)("PLAYDROP_WORKER_WRAPPER_CONTRACT_VERSION", WORKER_WRAPPER_CONTRACT_VERSION),
|
|
2510
|
+
gameSourceGit: 1,
|
|
2511
|
+
gameSource: gameSourceInventory,
|
|
2055
2512
|
...(npmVersion ? { npmVersion } : {}),
|
|
2056
2513
|
...(playwright ? { playwright } : {}),
|
|
2057
2514
|
os: node_process_1.default.platform,
|
|
@@ -2060,7 +2517,7 @@ function buildWorkerCapabilities(input) {
|
|
|
2060
2517
|
}
|
|
2061
2518
|
function buildWorkerClaimBody(input) {
|
|
2062
2519
|
return {
|
|
2063
|
-
protocolVersion:
|
|
2520
|
+
protocolVersion: 5,
|
|
2064
2521
|
workerKey: input.workerKey,
|
|
2065
2522
|
capabilities: {
|
|
2066
2523
|
...input.capabilities,
|
|
@@ -2317,6 +2774,7 @@ async function prepareCodexRunHomes(input) {
|
|
|
2317
2774
|
workerUsername: input.workerUsername,
|
|
2318
2775
|
envName: input.envName,
|
|
2319
2776
|
playdropConfig: input.playdropConfig,
|
|
2777
|
+
accessToken: input.playdropAccessToken,
|
|
2320
2778
|
});
|
|
2321
2779
|
}
|
|
2322
2780
|
return { homeDir, codexHomeDir };
|
|
@@ -2326,7 +2784,8 @@ async function stageWorkerPlaydropSession(input) {
|
|
|
2326
2784
|
const envName = input.envName.trim();
|
|
2327
2785
|
const config = input.playdropConfig ?? (0, config_1.loadConfig)();
|
|
2328
2786
|
const session = config.accounts?.[workerUsername]?.[envName];
|
|
2329
|
-
|
|
2787
|
+
const accessToken = input.accessToken?.trim() || session?.token?.trim() || "";
|
|
2788
|
+
if (!workerUsername || !envName || !accessToken) {
|
|
2330
2789
|
throw new Error(`worker_playdrop_session_missing:${workerUsername || "unknown"}:${envName || "unknown"}`);
|
|
2331
2790
|
}
|
|
2332
2791
|
const playdropConfigDir = node_path_1.default.join(input.homeDir, ".playdrop");
|
|
@@ -2334,13 +2793,13 @@ async function stageWorkerPlaydropSession(input) {
|
|
|
2334
2793
|
await (0, promises_1.mkdir)(playdropConfigDir, { recursive: true, mode: 0o700 });
|
|
2335
2794
|
await (0, promises_1.chmod)(playdropConfigDir, 0o700);
|
|
2336
2795
|
await (0, promises_1.writeFile)(playdropConfigPath, JSON.stringify({
|
|
2337
|
-
version:
|
|
2796
|
+
version: 3,
|
|
2338
2797
|
currentAccount: { username: workerUsername, env: envName },
|
|
2339
2798
|
accounts: {
|
|
2340
2799
|
[workerUsername]: {
|
|
2341
2800
|
[envName]: {
|
|
2342
|
-
token:
|
|
2343
|
-
updatedAt: session.
|
|
2801
|
+
token: accessToken,
|
|
2802
|
+
updatedAt: session?.updatedAt ?? new Date().toISOString(),
|
|
2344
2803
|
},
|
|
2345
2804
|
},
|
|
2346
2805
|
},
|
|
@@ -2370,6 +2829,7 @@ async function prepareClaudeRunConfig(input) {
|
|
|
2370
2829
|
workerUsername: input.workerUsername,
|
|
2371
2830
|
envName: input.envName,
|
|
2372
2831
|
playdropConfig: input.playdropConfig,
|
|
2832
|
+
accessToken: input.playdropAccessToken,
|
|
2373
2833
|
}),
|
|
2374
2834
|
};
|
|
2375
2835
|
}
|
|
@@ -2393,6 +2853,7 @@ async function prepareGrokRunConfig(input) {
|
|
|
2393
2853
|
workerUsername: input.workerUsername,
|
|
2394
2854
|
envName: input.envName,
|
|
2395
2855
|
playdropConfig: input.playdropConfig,
|
|
2856
|
+
accessToken: input.playdropAccessToken,
|
|
2396
2857
|
});
|
|
2397
2858
|
return {
|
|
2398
2859
|
homeDir,
|
|
@@ -2428,6 +2889,7 @@ async function runCodex(input) {
|
|
|
2428
2889
|
workspaceDir: input.stateDir ?? input.workspaceDir,
|
|
2429
2890
|
workerUsername: input.workerUsername,
|
|
2430
2891
|
envName: input.envName,
|
|
2892
|
+
playdropAccessToken: input.taskAccessToken,
|
|
2431
2893
|
stagePlaydropSession: !input.cleanRoom,
|
|
2432
2894
|
preserveExisting: Boolean(input.resumeSessionId),
|
|
2433
2895
|
});
|
|
@@ -2468,8 +2930,6 @@ async function runCodex(input) {
|
|
|
2468
2930
|
workspaceDir: input.workspaceDir,
|
|
2469
2931
|
model: input.codexModel.model,
|
|
2470
2932
|
reasoningEffort: input.codexModel.reasoningEffort,
|
|
2471
|
-
networkAccess: input.cleanRoom || (0, runtime_1.readEnvBoolean)("PLAYDROP_WORKER_AGENT_NETWORK_ACCESS", false),
|
|
2472
|
-
sandboxMode: (0, runtime_1.readCodexSandboxMode)(),
|
|
2473
2933
|
imagePaths: input.imagePaths,
|
|
2474
2934
|
resumeSessionId: input.resumeSessionId,
|
|
2475
2935
|
}),
|
|
@@ -2492,6 +2952,7 @@ async function runCodex(input) {
|
|
|
2492
2952
|
homeDir,
|
|
2493
2953
|
codexHomeDir,
|
|
2494
2954
|
playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
|
|
2955
|
+
gameServerDev: input.gameServerDev ?? undefined,
|
|
2495
2956
|
}),
|
|
2496
2957
|
stdin: input.prompt,
|
|
2497
2958
|
timeoutMs: input.timeoutMs ?? (0, runtime_1.readPositiveEnvInt)("PLAYDROP_WORKER_CODEX_TIMEOUT_MS", runtime_1.DEFAULT_CODEX_TIMEOUT_MS),
|
|
@@ -2538,6 +2999,142 @@ async function runCodex(input) {
|
|
|
2538
2999
|
tokensUsed: rolloutUsage.totalTokens,
|
|
2539
3000
|
};
|
|
2540
3001
|
}
|
|
3002
|
+
async function runPlaydropE2EGameFixture(input) {
|
|
3003
|
+
if (!(0, e2e_fixtures_1.isPlaydropE2EGameKind)(input.taskContext.kind)) {
|
|
3004
|
+
throw new Error(`playdrop_e2e_game_kind_invalid:${input.taskContext.kind}`);
|
|
3005
|
+
}
|
|
3006
|
+
const runRoot = node_path_1.default.join(input.workspaceDir, ".playdrop", "e2e-run");
|
|
3007
|
+
const homeDir = node_path_1.default.join(runRoot, "home");
|
|
3008
|
+
await (0, promises_1.rm)(runRoot, { recursive: true, force: true });
|
|
3009
|
+
await (0, promises_1.mkdir)(homeDir, { recursive: true, mode: 0o700 });
|
|
3010
|
+
await (0, promises_1.chmod)(homeDir, 0o700);
|
|
3011
|
+
const playdropConfigPath = await stageWorkerPlaydropSession({
|
|
3012
|
+
homeDir,
|
|
3013
|
+
workerUsername: input.workerUsername,
|
|
3014
|
+
envName: input.envName,
|
|
3015
|
+
accessToken: input.taskContext.taskAccessToken,
|
|
3016
|
+
});
|
|
3017
|
+
const childEnv = (0, runtime_1.buildWorkerChildEnv)({
|
|
3018
|
+
binDir: input.binDir,
|
|
3019
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
3020
|
+
taskId: input.taskContext.taskId,
|
|
3021
|
+
taskToken: input.taskContext.taskToken,
|
|
3022
|
+
attempt: input.taskContext.attempt,
|
|
3023
|
+
envName: input.envName,
|
|
3024
|
+
eventDir: input.eventDir,
|
|
3025
|
+
devPort: input.devPort,
|
|
3026
|
+
homeDir,
|
|
3027
|
+
playdropConfigPath,
|
|
3028
|
+
gameServerDev: input.gameServerDev ?? undefined,
|
|
3029
|
+
});
|
|
3030
|
+
const results = [];
|
|
3031
|
+
const run = async (args) => {
|
|
3032
|
+
const result = await (0, runtime_1.runLoggedProcess)({
|
|
3033
|
+
command: "playdrop",
|
|
3034
|
+
args,
|
|
3035
|
+
cwd: input.projectDir,
|
|
3036
|
+
env: childEnv,
|
|
3037
|
+
timeoutMs: 10 * 60 * 1000,
|
|
3038
|
+
maxOutputChars: runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS,
|
|
3039
|
+
transcriptFlushIntervalMs: runtime_1.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS,
|
|
3040
|
+
onTranscriptChunks: input.onTranscriptChunks,
|
|
3041
|
+
onChild: input.onChild,
|
|
3042
|
+
});
|
|
3043
|
+
results.push(result);
|
|
3044
|
+
return !result.timedOut && result.exitCode === 0 && result.signal === null;
|
|
3045
|
+
};
|
|
3046
|
+
try {
|
|
3047
|
+
const claimsSlug = input.taskContext.kind === "NEW_GAME" || input.taskContext.kind === "REMIX_GAME";
|
|
3048
|
+
const appName = claimsSlug
|
|
3049
|
+
? `playdrop-e2e-${input.taskContext.taskId}`
|
|
3050
|
+
: input.taskContext.outputAppName?.trim() ?? "";
|
|
3051
|
+
if (!appName)
|
|
3052
|
+
throw new Error("playdrop_e2e_game_output_app_name_missing");
|
|
3053
|
+
const updateSource = input.taskContext.kind === "GAME_UPDATE"
|
|
3054
|
+
? (0, taskSelection_1.selectTasks)(input.projectDir).tasks.find((task) => task.kind === "app" && task.name === appName)
|
|
3055
|
+
: null;
|
|
3056
|
+
if (input.taskContext.kind === "GAME_UPDATE" && !updateSource) {
|
|
3057
|
+
throw new Error(`playdrop_e2e_update_source_app_missing:${appName}`);
|
|
3058
|
+
}
|
|
3059
|
+
const displayName = claimsSlug
|
|
3060
|
+
? `PlayDrop E2E ${input.taskContext.taskId}`
|
|
3061
|
+
: input.taskContext.outputDisplayName?.trim() ||
|
|
3062
|
+
updateSource?.displayName?.trim() ||
|
|
3063
|
+
`PlayDrop E2E ${input.taskContext.taskId}`;
|
|
3064
|
+
const subtitle = claimsSlug
|
|
3065
|
+
? "Deterministic worker fixture"
|
|
3066
|
+
: input.taskContext.outputSubtitle?.trim() || updateSource?.subtitle?.trim() || "Deterministic worker fixture";
|
|
3067
|
+
const primarySurface = claimsSlug
|
|
3068
|
+
? "DESKTOP"
|
|
3069
|
+
: input.taskContext.outputPrimarySurface ?? updateSource?.primarySurface ?? "DESKTOP";
|
|
3070
|
+
if (input.taskContext.kind === "REMIX_GAME" && !input.taskContext.remixSourceRef?.trim()) {
|
|
3071
|
+
throw new Error("playdrop_e2e_remix_source_missing");
|
|
3072
|
+
}
|
|
3073
|
+
if (claimsSlug &&
|
|
3074
|
+
!(await run([
|
|
3075
|
+
"task",
|
|
3076
|
+
"claim-slug",
|
|
3077
|
+
"--app-name",
|
|
3078
|
+
appName,
|
|
3079
|
+
"--display-name",
|
|
3080
|
+
displayName,
|
|
3081
|
+
"--subtitle",
|
|
3082
|
+
subtitle,
|
|
3083
|
+
"--primary-surface",
|
|
3084
|
+
primarySurface,
|
|
3085
|
+
]))) {
|
|
3086
|
+
return mergePlaydropE2EProcessResults(results);
|
|
3087
|
+
}
|
|
3088
|
+
await (0, e2e_fixtures_1.replaceWithPlaydropE2EGameFixture)({
|
|
3089
|
+
projectDir: input.projectDir,
|
|
3090
|
+
taskId: input.taskContext.taskId,
|
|
3091
|
+
kind: input.taskContext.kind,
|
|
3092
|
+
appName,
|
|
3093
|
+
displayName,
|
|
3094
|
+
subtitle,
|
|
3095
|
+
primarySurface,
|
|
3096
|
+
outputVersion: input.taskContext.outputVersion,
|
|
3097
|
+
remixSourceRef: input.taskContext.remixSourceRef?.trim() || null,
|
|
3098
|
+
});
|
|
3099
|
+
if (!(await run(["task", "upload"])))
|
|
3100
|
+
return mergePlaydropE2EProcessResults(results);
|
|
3101
|
+
await run([
|
|
3102
|
+
"task",
|
|
3103
|
+
"done",
|
|
3104
|
+
"--summary",
|
|
3105
|
+
`Completed the deterministic ${input.taskContext.kind} PlayDrop E2E fixture.`,
|
|
3106
|
+
]);
|
|
3107
|
+
return mergePlaydropE2EProcessResults(results);
|
|
3108
|
+
}
|
|
3109
|
+
finally {
|
|
3110
|
+
await (0, promises_1.rm)(runRoot, { recursive: true, force: true });
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
function mergePlaydropE2EProcessResults(results) {
|
|
3114
|
+
const stdout = results.map((result) => result.stdout).filter(Boolean).join("\n");
|
|
3115
|
+
const stderr = results.map((result) => result.stderr).filter(Boolean).join("\n");
|
|
3116
|
+
const outputTail = results.map((result) => result.outputTail).filter(Boolean).join("\n").slice(-runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS);
|
|
3117
|
+
const failed = results.find((result) => result.timedOut || result.exitCode !== 0 || result.signal !== null);
|
|
3118
|
+
return {
|
|
3119
|
+
exitCode: failed?.exitCode ?? results.at(-1)?.exitCode ?? 0,
|
|
3120
|
+
signal: failed?.signal ?? null,
|
|
3121
|
+
stdout,
|
|
3122
|
+
stderr,
|
|
3123
|
+
outputTail,
|
|
3124
|
+
timedOut: failed?.timedOut ?? false,
|
|
3125
|
+
tokenUsage: {
|
|
3126
|
+
inputTokens: 0,
|
|
3127
|
+
outputTokens: 0,
|
|
3128
|
+
cacheCreationInputTokens: 0,
|
|
3129
|
+
cacheReadInputTokens: 0,
|
|
3130
|
+
totalTokens: 0,
|
|
3131
|
+
rawProviderUsage: { fixture: "playdrop-e2e-v1" },
|
|
3132
|
+
usageParseError: null,
|
|
3133
|
+
},
|
|
3134
|
+
tokensUsed: 0,
|
|
3135
|
+
providerSessionId: null,
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
2541
3138
|
async function runClaude(input) {
|
|
2542
3139
|
const playdropConfigPath = input.cleanRoom
|
|
2543
3140
|
? null
|
|
@@ -2545,6 +3142,7 @@ async function runClaude(input) {
|
|
|
2545
3142
|
workspaceDir: input.stateDir ?? input.workspaceDir,
|
|
2546
3143
|
workerUsername: input.workerUsername,
|
|
2547
3144
|
envName: input.envName,
|
|
3145
|
+
playdropAccessToken: input.taskAccessToken,
|
|
2548
3146
|
})).playdropConfigPath;
|
|
2549
3147
|
const denyReadRoots = resolveClaudeDenyReadRoots(input.workspaceDir);
|
|
2550
3148
|
const screenshotCollector = input.enableChrome || input.enablePlaywrightMcp
|
|
@@ -2566,6 +3164,7 @@ async function runClaude(input) {
|
|
|
2566
3164
|
denyReadRoots,
|
|
2567
3165
|
pluginDir: input.taskPluginRoot,
|
|
2568
3166
|
playdropCliEnabled: !input.cleanRoom,
|
|
3167
|
+
allowGit: input.allowGit,
|
|
2569
3168
|
chrome: input.enableChrome,
|
|
2570
3169
|
playwrightMcp: input.enablePlaywrightMcp,
|
|
2571
3170
|
playwrightMcpConfigPath,
|
|
@@ -2587,6 +3186,7 @@ async function runClaude(input) {
|
|
|
2587
3186
|
devPort: input.devPort,
|
|
2588
3187
|
playdropConfigPath: playdropConfigPath,
|
|
2589
3188
|
playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
|
|
3189
|
+
gameServerDev: input.gameServerDev ?? undefined,
|
|
2590
3190
|
}),
|
|
2591
3191
|
stdin: `${JSON.stringify({
|
|
2592
3192
|
type: "user",
|
|
@@ -2669,6 +3269,7 @@ async function runGrok(input) {
|
|
|
2669
3269
|
workspaceDir: input.stateDir ?? input.workspaceDir,
|
|
2670
3270
|
workerUsername: input.workerUsername,
|
|
2671
3271
|
envName: input.envName,
|
|
3272
|
+
playdropAccessToken: input.taskAccessToken,
|
|
2672
3273
|
preserveExisting: Boolean(input.resumeSessionId),
|
|
2673
3274
|
stagePlaydropSession: !input.cleanRoom,
|
|
2674
3275
|
});
|
|
@@ -2715,6 +3316,7 @@ async function runGrok(input) {
|
|
|
2715
3316
|
homeDir,
|
|
2716
3317
|
playdropConfigPath: playdropConfigPath,
|
|
2717
3318
|
playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
|
|
3319
|
+
gameServerDev: input.gameServerDev ?? undefined,
|
|
2718
3320
|
});
|
|
2719
3321
|
childEnv.HOME = homeDir;
|
|
2720
3322
|
childEnv.GROK_HOME = grokHomeDir;
|
|
@@ -2779,9 +3381,9 @@ function resolveClaudeDenyReadRoots(workspaceDir) {
|
|
|
2779
3381
|
}
|
|
2780
3382
|
return roots;
|
|
2781
3383
|
}
|
|
2782
|
-
// Raw stdout/stderr tails stay out of task results on purpose
|
|
2783
|
-
//
|
|
2784
|
-
function
|
|
3384
|
+
// Raw stdout/stderr tails stay out of task results on purpose. The local spool
|
|
3385
|
+
// is archived directly to private storage after the agent exits.
|
|
3386
|
+
function mergeAgentContinuationResults(runtime, initial, recovery, timeoutMarkerCode) {
|
|
2785
3387
|
const sumNullable = (left, right) => left === null && right === null ? null : (left ?? 0) + (right ?? 0);
|
|
2786
3388
|
const tokenUsage = runtime === "CODEX" && recovery.tokenUsage.totalTokens !== null
|
|
2787
3389
|
? recovery.tokenUsage
|
|
@@ -2799,7 +3401,7 @@ function mergeCreatorRecoveryResults(runtime, initial, recovery) {
|
|
|
2799
3401
|
? "creator_recovery_usage_incomplete"
|
|
2800
3402
|
: null,
|
|
2801
3403
|
};
|
|
2802
|
-
const recoveryMarker = recovery.timedOut ?
|
|
3404
|
+
const recoveryMarker = recovery.timedOut ? `\n${timeoutMarkerCode}` : "";
|
|
2803
3405
|
return {
|
|
2804
3406
|
...recovery,
|
|
2805
3407
|
stdout: `${initial.stdout}\n${recovery.stdout}`.slice(-runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS),
|
|
@@ -2811,6 +3413,12 @@ function mergeCreatorRecoveryResults(runtime, initial, recovery) {
|
|
|
2811
3413
|
providerResolvedModel: recovery.providerResolvedModel ?? initial.providerResolvedModel ?? null,
|
|
2812
3414
|
};
|
|
2813
3415
|
}
|
|
3416
|
+
function mergeCreatorRecoveryResults(runtime, initial, recovery) {
|
|
3417
|
+
return mergeAgentContinuationResults(runtime, initial, recovery, "agent_task_creation_recovery_timeout");
|
|
3418
|
+
}
|
|
3419
|
+
function mergeMissingAgentOutcomeRecoveryResults(runtime, initial, recovery) {
|
|
3420
|
+
return mergeAgentContinuationResults(runtime, initial, recovery, "agent_task_outcome_recovery_timeout");
|
|
3421
|
+
}
|
|
2814
3422
|
function buildAgentRunResult(result) {
|
|
2815
3423
|
return {
|
|
2816
3424
|
exitCode: result.exitCode,
|
|
@@ -2915,7 +3523,7 @@ async function recordAgentRunTelemetry(input) {
|
|
|
2915
3523
|
completedAt: input.completedAt.toISOString(),
|
|
2916
3524
|
...(input.status === "FAILED"
|
|
2917
3525
|
? {
|
|
2918
|
-
terminalReason: (0, types_1.classifyAgentTaskTerminalReason)(agentFailureRawError(input.result), "agent"),
|
|
3526
|
+
terminalReason: input.terminalReason ?? (0, types_1.classifyAgentTaskTerminalReason)(agentFailureRawError(input.result), "agent"),
|
|
2919
3527
|
}
|
|
2920
3528
|
: {}),
|
|
2921
3529
|
});
|
|
@@ -2970,9 +3578,7 @@ function buildAgentFailureCode(agent, result, request) {
|
|
|
2970
3578
|
return `agent_provider_overloaded:${provider}`;
|
|
2971
3579
|
}
|
|
2972
3580
|
if (providerStatus === 429 ||
|
|
2973
|
-
providerStatus
|
|
2974
|
-
providerStatus === 503 ||
|
|
2975
|
-
providerStatus === 504 ||
|
|
3581
|
+
(providerStatus !== null && providerStatus >= 500 && providerStatus <= 599) ||
|
|
2976
3582
|
(agent === "CLAUDE_CODE" &&
|
|
2977
3583
|
/\bResponse stalled mid-stream\.?(?:\s+The response above may be incomplete\.?)?/i.test(combinedOutput)) ||
|
|
2978
3584
|
/\b(?:service|provider|api)\s+(?:is\s+)?(?:temporarily\s+)?unavailable\b/i.test(combinedOutput) ||
|
|
@@ -2982,6 +3588,9 @@ function buildAgentFailureCode(agent, result, request) {
|
|
|
2982
3588
|
if (result.timedOut) {
|
|
2983
3589
|
return `agent_provider_timeout:${provider}`;
|
|
2984
3590
|
}
|
|
3591
|
+
if (/\b(?:econnreset|econnrefused|network error|socket hang up|connection reset|connection lost|fetch failed)\b/i.test(combinedOutput)) {
|
|
3592
|
+
return `agent_provider_connection:${provider}`;
|
|
3593
|
+
}
|
|
2985
3594
|
if (result.exitCode !== null) {
|
|
2986
3595
|
return `agent_unknown_exit:${provider}:${result.exitCode}`;
|
|
2987
3596
|
}
|
|
@@ -2990,7 +3599,7 @@ function buildAgentFailureCode(agent, result, request) {
|
|
|
2990
3599
|
}
|
|
2991
3600
|
return `agent_unknown_exit:${provider}`;
|
|
2992
3601
|
}
|
|
2993
|
-
function classifyAgentAvailabilityFailureReason(errorCode) {
|
|
3602
|
+
function classifyAgentAvailabilityFailureReason(errorCode, retryTransientProviderFailure = false) {
|
|
2994
3603
|
if (errorCode.startsWith("agent_provider_not_authenticated:")) {
|
|
2995
3604
|
return "agent_not_authenticated";
|
|
2996
3605
|
}
|
|
@@ -3001,6 +3610,10 @@ function classifyAgentAvailabilityFailureReason(errorCode) {
|
|
|
3001
3610
|
errorCode.startsWith("agent_provider_temporarily_unavailable:")) {
|
|
3002
3611
|
return "agent_temporarily_unavailable";
|
|
3003
3612
|
}
|
|
3613
|
+
if (retryTransientProviderFailure &&
|
|
3614
|
+
(errorCode.startsWith("agent_provider_connection:") || errorCode.startsWith("agent_provider_timeout:"))) {
|
|
3615
|
+
return "agent_temporarily_unavailable";
|
|
3616
|
+
}
|
|
3004
3617
|
if (errorCode.startsWith("requested_model_unavailable:")) {
|
|
3005
3618
|
return "agent_model_unsupported";
|
|
3006
3619
|
}
|
|
@@ -3031,6 +3644,9 @@ function describeAgentFailureForEvent(errorCode) {
|
|
|
3031
3644
|
if (code === "agent_provider_timeout") {
|
|
3032
3645
|
return `${providerLabel} timed out before completing the task. Retry this task when the provider is healthy.`;
|
|
3033
3646
|
}
|
|
3647
|
+
if (code === "agent_provider_connection") {
|
|
3648
|
+
return `${providerLabel} lost its connection before completing the task. Retry this task when the provider is healthy.`;
|
|
3649
|
+
}
|
|
3034
3650
|
if (code === "agent_task_resume_session_missing") {
|
|
3035
3651
|
return `${providerLabel} timed out, but the worker could not recover the same conversation. The task was stopped without starting over.`;
|
|
3036
3652
|
}
|
|
@@ -3076,6 +3692,7 @@ async function createLocalPlaydropShim(input) {
|
|
|
3076
3692
|
`set "PLAYDROP_WORKER_TASK_ATTEMPT=${input.attempt}"`,
|
|
3077
3693
|
`set "PLAYDROP_WORKER_TASK_DEV_PORT=${input.devPort}"`,
|
|
3078
3694
|
`set "PLAYDROP_DEV_ROUTER_PORT=${input.devPort}"`,
|
|
3695
|
+
...(input.gameSourceGit ? ['set "PLAYDROP_GAME_SOURCE_GIT_ROOT=1"'] : []),
|
|
3079
3696
|
`"${node_process_1.default.execPath}" "${cliEntrypoint}" %*`,
|
|
3080
3697
|
"exit /b %ERRORLEVEL%",
|
|
3081
3698
|
"",
|
|
@@ -3087,12 +3704,25 @@ async function createLocalPlaydropShim(input) {
|
|
|
3087
3704
|
`export PLAYDROP_WORKER_TASK_ATTEMPT=${JSON.stringify(String(input.attempt))}`,
|
|
3088
3705
|
`export PLAYDROP_WORKER_TASK_DEV_PORT=${JSON.stringify(String(input.devPort))}`,
|
|
3089
3706
|
`export PLAYDROP_DEV_ROUTER_PORT=${JSON.stringify(String(input.devPort))}`,
|
|
3707
|
+
...(input.gameSourceGit ? ["export PLAYDROP_GAME_SOURCE_GIT_ROOT=1"] : []),
|
|
3090
3708
|
`exec ${JSON.stringify(node_process_1.default.execPath)} ${JSON.stringify(cliEntrypoint)} "$@"`,
|
|
3091
3709
|
"",
|
|
3092
3710
|
];
|
|
3093
3711
|
await (0, promises_1.writeFile)(shimPath, shim.join(isWindows ? "\r\n" : "\n"), "utf8");
|
|
3094
3712
|
if (!isWindows) {
|
|
3095
3713
|
await (0, promises_1.chmod)(shimPath, 0o755);
|
|
3714
|
+
const npxShimPath = node_path_1.default.join(binDir, "npx");
|
|
3715
|
+
const npxShim = [
|
|
3716
|
+
"#!/bin/sh",
|
|
3717
|
+
'if [ "$1" = "playdrop" ]; then',
|
|
3718
|
+
" shift",
|
|
3719
|
+
` exec ${JSON.stringify(shimPath)} "$@"`,
|
|
3720
|
+
"fi",
|
|
3721
|
+
'PATH="${PATH#*:}" exec npx "$@"',
|
|
3722
|
+
"",
|
|
3723
|
+
];
|
|
3724
|
+
await (0, promises_1.writeFile)(npxShimPath, npxShim.join("\n"), "utf8");
|
|
3725
|
+
await (0, promises_1.chmod)(npxShimPath, 0o755);
|
|
3096
3726
|
}
|
|
3097
3727
|
return binDir;
|
|
3098
3728
|
}
|
|
@@ -3189,6 +3819,19 @@ function probeCodexModels() {
|
|
|
3189
3819
|
}
|
|
3190
3820
|
return parseCodexModelCatalog(result.output);
|
|
3191
3821
|
}
|
|
3822
|
+
function codexFeatureEnabled(output, feature) {
|
|
3823
|
+
const normalizedFeature = feature.trim();
|
|
3824
|
+
if (!normalizedFeature)
|
|
3825
|
+
return false;
|
|
3826
|
+
return output.split(/\r?\n/).some((line) => {
|
|
3827
|
+
const columns = line.trim().split(/\s+/);
|
|
3828
|
+
return columns[0] === normalizedFeature && columns[columns.length - 1] === "true";
|
|
3829
|
+
});
|
|
3830
|
+
}
|
|
3831
|
+
function probeCodexImageGeneration() {
|
|
3832
|
+
const result = (0, shellProbe_1.runShell)("codex features list");
|
|
3833
|
+
return result.exitCode === 0 && codexFeatureEnabled(result.output, "image_generation");
|
|
3834
|
+
}
|
|
3192
3835
|
function probeClaudeInstallation() {
|
|
3193
3836
|
const probe = (0, shellProbe_1.runShell)((0, shellProbe_1.buildCommandPathProbe)("claude"));
|
|
3194
3837
|
if (probe.exitCode !== 0 || !probe.output.trim()) {
|
|
@@ -3320,12 +3963,21 @@ function probeReadyWorkerCapabilities(input) {
|
|
|
3320
3963
|
const grokVersion = probeWorkerProviderIndependently(() => probeGrokInstallation()?.grokVersion ?? null, null);
|
|
3321
3964
|
const cursorVersion = probeWorkerProviderIndependently(() => probeCursorInstallation()?.cursorVersion ?? null, null);
|
|
3322
3965
|
const codexModels = codexVersion ? probeWorkerProviderIndependently(probeCodexModels, []) : [];
|
|
3966
|
+
const codexImageGenerationAvailable = codexVersion
|
|
3967
|
+
? probeWorkerProviderIndependently(probeCodexImageGeneration, false)
|
|
3968
|
+
: false;
|
|
3323
3969
|
const claudeModels = claudeVersion ? probeWorkerProviderIndependently(probeClaudeModels, []) : [];
|
|
3324
3970
|
const grokModels = grokVersion ? probeWorkerProviderIndependently(probeGrokModels, []) : [];
|
|
3971
|
+
const workerHomeDir = resolveWorkerHomeDir();
|
|
3972
|
+
const falConfigured = (0, generation_1.hasFalCredential)(workerHomeDir);
|
|
3973
|
+
const falProbe = probeFalAuthentication(workerHomeDir);
|
|
3325
3974
|
const capabilities = buildWorkerCapabilities({
|
|
3326
3975
|
codexVersion,
|
|
3327
3976
|
codexAuthenticated: codexVersion ? readCodexAuthenticated() : false,
|
|
3328
3977
|
codexModels,
|
|
3978
|
+
codexImageGenerationAvailable,
|
|
3979
|
+
falConfigured,
|
|
3980
|
+
falAuthenticated: falProbe.authenticated,
|
|
3329
3981
|
claudeVersion,
|
|
3330
3982
|
claudeAuthenticated: claudeVersion ? readClaudeAuthenticated() : false,
|
|
3331
3983
|
claudeModels,
|
|
@@ -3338,8 +3990,9 @@ function probeReadyWorkerCapabilities(input) {
|
|
|
3338
3990
|
playwright: input.playwright,
|
|
3339
3991
|
maxParallelTasks: input.maxParallelTasks,
|
|
3340
3992
|
runningTaskCount: 0,
|
|
3993
|
+
requireGenerationRuntime: input.requireGenerationRuntime,
|
|
3341
3994
|
});
|
|
3342
|
-
if (!capabilities.ready) {
|
|
3995
|
+
if (!capabilities.ready || (capabilities.degradedReasons?.length ?? 0) > 0) {
|
|
3343
3996
|
const reasons = Array.isArray(capabilities.degradedReasons) && capabilities.degradedReasons.length > 0
|
|
3344
3997
|
? capabilities.degradedReasons.join(", ")
|
|
3345
3998
|
: "no ready agent";
|
|
@@ -3446,11 +4099,18 @@ function describeWorkerPreflightReason(reason) {
|
|
|
3446
4099
|
worker_preflight_recorder_hash_mismatch: "The PlayDrop macOS recorder does not match this CLI release.",
|
|
3447
4100
|
worker_preflight_screen_recording_permission_missing: "Screen Recording permission is not granted to the PlayDrop recorder.",
|
|
3448
4101
|
codex_not_authenticated: "Codex is installed but not authenticated.",
|
|
4102
|
+
codex_not_installed: "Codex is required for generation work but is not installed.",
|
|
3449
4103
|
codex_models_unavailable: "Codex is authenticated but reported no usable models.",
|
|
4104
|
+
codex_image_generation_unavailable: "Codex built-in image generation is unavailable.",
|
|
3450
4105
|
claude_not_authenticated: "Claude Code is installed but not authenticated.",
|
|
3451
4106
|
claude_models_unavailable: "Claude Code is authenticated but reported no usable models.",
|
|
3452
4107
|
grok_not_authenticated: "Grok Build is installed but not authenticated with a subscription login.",
|
|
3453
4108
|
grok_models_unavailable: "Grok Build is authenticated but reported no usable models.",
|
|
4109
|
+
fal_not_configured: "The Fal credential file is missing.",
|
|
4110
|
+
fal_not_authenticated: "The Fal credential was rejected by the Fal queue API.",
|
|
4111
|
+
fal_api_unreachable: "The Fal queue API could not be reached during worker readiness checks.",
|
|
4112
|
+
ffmpeg_not_ready_for_generation: "ffmpeg or ffprobe is missing from PATH.",
|
|
4113
|
+
agent_cli_not_authenticated: "No installed agent CLI has a usable authenticated model.",
|
|
3454
4114
|
agent_cli_not_found: "No supported authenticated agent CLI is installed.",
|
|
3455
4115
|
};
|
|
3456
4116
|
return descriptions[reason] ?? reason;
|
|
@@ -3477,10 +4137,56 @@ function readWorkerTargetUpdateFailure() {
|
|
|
3477
4137
|
minimumCliVersion: typeof parsed.minimumCliVersion === "string" ? parsed.minimumCliVersion : null,
|
|
3478
4138
|
};
|
|
3479
4139
|
}
|
|
3480
|
-
function
|
|
4140
|
+
function falAuthenticationProbeExitCode(status) {
|
|
4141
|
+
if (status === 404)
|
|
4142
|
+
return 0;
|
|
4143
|
+
if (status === 401 || status === 403)
|
|
4144
|
+
return 2;
|
|
4145
|
+
return 3;
|
|
4146
|
+
}
|
|
4147
|
+
function probeFalAuthentication(workerHomeDir = resolveWorkerHomeDir(), deps = { spawnSync: node_child_process_1.spawnSync }) {
|
|
4148
|
+
const credentialFile = (0, generation_1.falCredentialPath)(workerHomeDir);
|
|
4149
|
+
if (!(0, node_fs_1.existsSync)(credentialFile)) {
|
|
4150
|
+
return { authenticated: false, error: "fal_not_configured" };
|
|
4151
|
+
}
|
|
4152
|
+
const credential = (0, node_fs_1.readFileSync)(credentialFile, "utf8").trim();
|
|
4153
|
+
if (!credential) {
|
|
4154
|
+
return { authenticated: false, error: "fal_not_authenticated" };
|
|
4155
|
+
}
|
|
4156
|
+
const probe = deps.spawnSync(node_process_1.default.execPath, [
|
|
4157
|
+
"-e",
|
|
4158
|
+
[
|
|
4159
|
+
"const chunks=[];",
|
|
4160
|
+
"process.stdin.on('data',(chunk)=>chunks.push(chunk));",
|
|
4161
|
+
"process.stdin.on('end',async()=>{",
|
|
4162
|
+
"const key=Buffer.concat(chunks).toString('utf8').trim();",
|
|
4163
|
+
"const controller=new AbortController();",
|
|
4164
|
+
"const timeout=setTimeout(()=>controller.abort(),10000);",
|
|
4165
|
+
"try{",
|
|
4166
|
+
"const response=await fetch('https://queue.fal.run/fal-ai/elevenlabs/requests/playdrop-auth-probe/status?logs=0',{headers:{Authorization:`Key ${key}`},signal:controller.signal});",
|
|
4167
|
+
"process.exitCode=response.status===404?0:response.status===401||response.status===403?2:3;",
|
|
4168
|
+
"}catch{process.exitCode=3;}finally{clearTimeout(timeout);}",
|
|
4169
|
+
"});",
|
|
4170
|
+
].join(""),
|
|
4171
|
+
], {
|
|
4172
|
+
input: `${credential}\n`,
|
|
4173
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
4174
|
+
timeout: 15000,
|
|
4175
|
+
});
|
|
4176
|
+
if (probe.status === 0) {
|
|
4177
|
+
return { authenticated: true, error: null };
|
|
4178
|
+
}
|
|
4179
|
+
if (probe.status === 2) {
|
|
4180
|
+
return { authenticated: false, error: "fal_not_authenticated" };
|
|
4181
|
+
}
|
|
4182
|
+
return { authenticated: false, error: "fal_api_unreachable" };
|
|
4183
|
+
}
|
|
4184
|
+
function collectWorkerLocalStatus(input = {}) {
|
|
4185
|
+
const requireGenerationRuntime = input.requireGenerationRuntime ?? (0, runtime_1.readEnvBoolean)("PLAYDROP_WORKER_REQUIRE_GENERATION_RUNTIME", false);
|
|
3481
4186
|
const degradedReasons = [];
|
|
3482
4187
|
let codexVersion = null;
|
|
3483
4188
|
let codexModels = [];
|
|
4189
|
+
let codexImageGenerationAvailable = false;
|
|
3484
4190
|
let claudeVersion = null;
|
|
3485
4191
|
let claudeModels = [];
|
|
3486
4192
|
let grokVersion = null;
|
|
@@ -3489,9 +4195,13 @@ function collectWorkerLocalStatus() {
|
|
|
3489
4195
|
const npmVersion = null;
|
|
3490
4196
|
let playwright = null;
|
|
3491
4197
|
let targetUpdateFailure = null;
|
|
4198
|
+
const workerHomeDir = resolveWorkerHomeDir();
|
|
4199
|
+
const falConfigured = (0, generation_1.hasFalCredential)(workerHomeDir);
|
|
4200
|
+
const falProbe = probeFalAuthentication(workerHomeDir);
|
|
3492
4201
|
try {
|
|
3493
4202
|
codexVersion = probeCodexInstallation()?.codexVersion ?? null;
|
|
3494
4203
|
codexModels = codexVersion ? probeCodexModels() : [];
|
|
4204
|
+
codexImageGenerationAvailable = codexVersion ? probeCodexImageGeneration() : false;
|
|
3495
4205
|
}
|
|
3496
4206
|
catch (error) {
|
|
3497
4207
|
degradedReasons.push(errorCode(error));
|
|
@@ -3500,22 +4210,16 @@ function collectWorkerLocalStatus() {
|
|
|
3500
4210
|
claudeVersion = probeClaudeInstallation()?.claudeVersion ?? null;
|
|
3501
4211
|
claudeModels = claudeVersion ? probeClaudeModels() : [];
|
|
3502
4212
|
}
|
|
3503
|
-
catch
|
|
3504
|
-
degradedReasons.push(errorCode(error));
|
|
3505
|
-
}
|
|
4213
|
+
catch { }
|
|
3506
4214
|
try {
|
|
3507
4215
|
grokVersion = probeGrokInstallation()?.grokVersion ?? null;
|
|
3508
4216
|
grokModels = grokVersion ? probeGrokModels() : [];
|
|
3509
4217
|
}
|
|
3510
|
-
catch
|
|
3511
|
-
degradedReasons.push(errorCode(error));
|
|
3512
|
-
}
|
|
4218
|
+
catch { }
|
|
3513
4219
|
try {
|
|
3514
4220
|
cursorVersion = probeCursorInstallation()?.cursorVersion ?? null;
|
|
3515
4221
|
}
|
|
3516
|
-
catch
|
|
3517
|
-
degradedReasons.push(errorCode(error));
|
|
3518
|
-
}
|
|
4222
|
+
catch { }
|
|
3519
4223
|
try {
|
|
3520
4224
|
playwright = probePlaywrightChromium();
|
|
3521
4225
|
if (!playwright.chromiumInstalled) {
|
|
@@ -3538,6 +4242,9 @@ function collectWorkerLocalStatus() {
|
|
|
3538
4242
|
codexVersion,
|
|
3539
4243
|
codexAuthenticated: codexVersion ? readCodexAuthenticated() : false,
|
|
3540
4244
|
codexModels,
|
|
4245
|
+
codexImageGenerationAvailable,
|
|
4246
|
+
falConfigured,
|
|
4247
|
+
falAuthenticated: falProbe.authenticated,
|
|
3541
4248
|
claudeVersion,
|
|
3542
4249
|
claudeAuthenticated: claudeVersion ? readClaudeAuthenticated() : false,
|
|
3543
4250
|
claudeModels,
|
|
@@ -3550,6 +4257,7 @@ function collectWorkerLocalStatus() {
|
|
|
3550
4257
|
playwright,
|
|
3551
4258
|
runningTaskCount: 0,
|
|
3552
4259
|
maxParallelTasks: (0, runtime_1.readPositiveEnvInt)("PLAYDROP_WORKER_MAX_PARALLEL_TASKS", DEFAULT_WORKER_MAX_PARALLEL_TASKS),
|
|
4260
|
+
requireGenerationRuntime,
|
|
3553
4261
|
});
|
|
3554
4262
|
if (targetUpdateFailure) {
|
|
3555
4263
|
capabilities.targetUpdateFailure = targetUpdateFailure;
|
|
@@ -3564,10 +4272,10 @@ function collectWorkerLocalStatus() {
|
|
|
3564
4272
|
const uniqueReasons = Array.from(new Set(degradedReasons));
|
|
3565
4273
|
const infrastructureFailures = degradedReasons.filter((reason) => reason.startsWith("worker_preflight_"));
|
|
3566
4274
|
const infrastructureReady = Boolean(playwright?.chromiumInstalled) && infrastructureFailures.length === 0;
|
|
3567
|
-
const ready = Boolean(capabilities?.ready) && infrastructureReady;
|
|
4275
|
+
const ready = Boolean(capabilities?.ready) && infrastructureReady && uniqueReasons.length === 0;
|
|
3568
4276
|
return {
|
|
3569
4277
|
ready,
|
|
3570
|
-
degradedReasons:
|
|
4278
|
+
degradedReasons: uniqueReasons,
|
|
3571
4279
|
cliVersion: (0, clientInfo_1.getCliVersion)(),
|
|
3572
4280
|
nodeVersion: node_process_1.default.versions.node,
|
|
3573
4281
|
npmVersion,
|
|
@@ -4431,6 +5139,13 @@ function printWorkerStatus(payload) {
|
|
|
4431
5139
|
else {
|
|
4432
5140
|
console.log(" Agents: none detected");
|
|
4433
5141
|
}
|
|
5142
|
+
const generationProviders = payload.local.capabilities?.generationProviders ?? [];
|
|
5143
|
+
const codexGenerationReady = generationProviders.some((entry) => entry.provider === "CODEX" && entry.ready);
|
|
5144
|
+
const codexImageGenerationReady = generationProviders.some((entry) => entry.provider === "CODEX" && entry.kind === "IMAGE" && entry.ready);
|
|
5145
|
+
const falGenerationReady = generationProviders.some((entry) => entry.provider === "FAL" && entry.ready);
|
|
5146
|
+
console.log(` Codex generation: ${codexGenerationReady ? "ready" : "unavailable"}`);
|
|
5147
|
+
console.log(` Codex image generation: ${codexImageGenerationReady ? "ready" : "unavailable"}`);
|
|
5148
|
+
console.log(` Fal generation: ${falGenerationReady ? "ready" : "unavailable"}`);
|
|
4434
5149
|
if (payload.local.degradedReasons.length > 0) {
|
|
4435
5150
|
console.log(` Reasons: ${payload.local.degradedReasons.join(", ")}`);
|
|
4436
5151
|
}
|
|
@@ -4487,11 +5202,16 @@ async function setupWorker(options = {}) {
|
|
|
4487
5202
|
return;
|
|
4488
5203
|
}
|
|
4489
5204
|
if (!options.env?.trim()) {
|
|
4490
|
-
throw new Error("worker_setup_requires_env:
|
|
5205
|
+
throw new Error("worker_setup_requires_env: cloud worker setup must pass --env <env> so the LaunchAgent cannot be installed for the wrong environment.");
|
|
4491
5206
|
}
|
|
4492
5207
|
if (!explicitWorkerName) {
|
|
4493
|
-
throw new Error("worker_setup_requires_name:
|
|
5208
|
+
throw new Error("worker_setup_requires_name: cloud worker setup must pass --name <worker name> so the LaunchAgent identity is deterministic.");
|
|
4494
5209
|
}
|
|
5210
|
+
const gameServerInfrastructure = await (0, game_server_infra_1.setupManagedWorkerGameServerInfrastructure)({
|
|
5211
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
5212
|
+
});
|
|
5213
|
+
node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_REDIS_ADMIN_URL = gameServerInfrastructure.redisAdminUrl;
|
|
5214
|
+
node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_MONGO_ADMIN_URL = gameServerInfrastructure.mongoAdminUrl;
|
|
4495
5215
|
verifyNpmGlobalInstallPreconditions();
|
|
4496
5216
|
const { wrapperPath, policyFile } = await writeManagedWorkerWrapper({ env, workerName, workerKey });
|
|
4497
5217
|
const { label, plistPath, registrationChanged } = await writeManagedWorkerLaunchAgent({
|
|
@@ -4523,7 +5243,7 @@ async function setupWorker(options = {}) {
|
|
|
4523
5243
|
`LaunchAgent: ${plistPath}`,
|
|
4524
5244
|
`Policy file: ${policyFile}`,
|
|
4525
5245
|
]);
|
|
4526
|
-
}, { env: options.env });
|
|
5246
|
+
}, { env: options.env, forceRefresh: true });
|
|
4527
5247
|
}
|
|
4528
5248
|
async function failTaskWithRetry(client, taskId, body) {
|
|
4529
5249
|
const terminalReason = body.terminalReason ?? (0, types_1.classifyAgentTaskTerminalReason)(body.error, "worker");
|
|
@@ -4553,12 +5273,69 @@ async function failTaskWithRetry(client, taskId, body) {
|
|
|
4553
5273
|
}
|
|
4554
5274
|
}
|
|
4555
5275
|
}
|
|
4556
|
-
async function
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
5276
|
+
async function completeWorkerAgentTaskWithRetry(input) {
|
|
5277
|
+
const sleepImpl = input.sleepImpl ?? sleep;
|
|
5278
|
+
const maxAttempts = input.maxAttempts ?? 3;
|
|
5279
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) {
|
|
5280
|
+
throw new Error("agent_task_completion_attempts_invalid");
|
|
4560
5281
|
}
|
|
4561
|
-
|
|
5282
|
+
let lastError = new Error("agent_task_completion_failed");
|
|
5283
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
5284
|
+
try {
|
|
5285
|
+
return await input.client.workerCompleteAgentTask(input.taskId, input.body);
|
|
5286
|
+
}
|
|
5287
|
+
catch (error) {
|
|
5288
|
+
if (error instanceof types_1.ApiError && error.status < 500)
|
|
5289
|
+
throw error;
|
|
5290
|
+
lastError = error;
|
|
5291
|
+
if (attempt < maxAttempts)
|
|
5292
|
+
await sleepImpl(500 * 2 ** (attempt - 1));
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
5295
|
+
throw lastError;
|
|
5296
|
+
}
|
|
5297
|
+
async function fetchTaskDetail(client, target, taskId, workerKey) {
|
|
5298
|
+
try {
|
|
5299
|
+
if (target === "FIRST_PARTY") {
|
|
5300
|
+
return await client.workerGetAgentTask(taskId, { workerKey });
|
|
5301
|
+
}
|
|
5302
|
+
return await client.getAgentTask(taskId);
|
|
5303
|
+
}
|
|
5304
|
+
catch (error) {
|
|
5305
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5306
|
+
throw new Error(`agent_task_worker_detail_failed:${detail}`);
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
function classifyWorkerSupervisorFailure(message, stage = "setup") {
|
|
5310
|
+
if (message.startsWith("agent_task_worker_detail_failed:")) {
|
|
5311
|
+
return {
|
|
5312
|
+
kind: "failure",
|
|
5313
|
+
category: "agent_failure",
|
|
5314
|
+
code: "agent_task_worker_detail_failed",
|
|
5315
|
+
message: "The worker could not refresh the task after the agent run. Retry after the platform is healthy.",
|
|
5316
|
+
retryable: true,
|
|
5317
|
+
source: "worker",
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
const classified = (0, types_1.classifyAgentTaskTerminalReason)(message, "worker");
|
|
5321
|
+
const stageFailureCodes = {
|
|
5322
|
+
post_agent_detail: "agent_task_post_agent_detail_failed",
|
|
5323
|
+
game_source_reconcile: "agent_task_game_source_reconcile_failed",
|
|
5324
|
+
game_publish: "agent_task_game_publish_failed",
|
|
5325
|
+
task_complete: "agent_task_completion_failed",
|
|
5326
|
+
};
|
|
5327
|
+
const stageFailureCode = stageFailureCodes[stage];
|
|
5328
|
+
if (stageFailureCode && classified.category === "agent_failure" && !classified.retryable) {
|
|
5329
|
+
return {
|
|
5330
|
+
kind: "failure",
|
|
5331
|
+
category: "agent_failure",
|
|
5332
|
+
code: stageFailureCode,
|
|
5333
|
+
message: "The worker failed after the agent finished. Retry after the platform is healthy.",
|
|
5334
|
+
retryable: true,
|
|
5335
|
+
source: "worker",
|
|
5336
|
+
};
|
|
5337
|
+
}
|
|
5338
|
+
return classified;
|
|
4562
5339
|
}
|
|
4563
5340
|
function assignmentRequiresRegisteredBundle(assignment) {
|
|
4564
5341
|
const playdropMetadata = assignment.task.metadata.playdrop;
|
|
@@ -4572,7 +5349,8 @@ function throwWorkerRuntimeError(error) {
|
|
|
4572
5349
|
}
|
|
4573
5350
|
async function startWorker(options = {}) {
|
|
4574
5351
|
loadWorkerEnvFile(options.env);
|
|
4575
|
-
await (0, commandContext_1.withEnvironment)("worker start", "Starting the PlayDrop worker", async (
|
|
5352
|
+
await (0, commandContext_1.withEnvironment)("worker start", "Starting the PlayDrop worker", async (ctx) => {
|
|
5353
|
+
const { client, env, envConfig } = ctx;
|
|
4576
5354
|
const devPluginWorkingTree = resolveDevPluginWorkingTree({
|
|
4577
5355
|
workingTree: options.devPluginWorkingTree,
|
|
4578
5356
|
env,
|
|
@@ -4596,6 +5374,16 @@ async function startWorker(options = {}) {
|
|
|
4596
5374
|
throw new Error('worker_session_invalid: the stored session did not resolve to a user. Run "playdrop auth login" and retry.');
|
|
4597
5375
|
}
|
|
4598
5376
|
const target = resolveWorkerExecutionTargetFromRole(me.user.role);
|
|
5377
|
+
if (target === "FIRST_PARTY" &&
|
|
5378
|
+
node_process_1.default.env.PLAYDROP_WORKER_WRAPPER_CONTRACT_VERSION &&
|
|
5379
|
+
node_process_1.default.platform === "darwin") {
|
|
5380
|
+
const gameServerInfrastructure = (0, game_server_infra_1.writeManagedWorkerGameServerConfiguration)({
|
|
5381
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
5382
|
+
});
|
|
5383
|
+
await (0, game_server_infra_1.probeManagedWorkerGameServerInfrastructure)(gameServerInfrastructure);
|
|
5384
|
+
node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_REDIS_ADMIN_URL = gameServerInfrastructure.redisAdminUrl;
|
|
5385
|
+
node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_MONGO_ADMIN_URL = gameServerInfrastructure.mongoAdminUrl;
|
|
5386
|
+
}
|
|
4599
5387
|
const initialWorkerPolicy = (await client.listMyAgentWorkers()).updatePolicy;
|
|
4600
5388
|
if (initialWorkerPolicy.executionTarget !== target) {
|
|
4601
5389
|
throw new Error(`worker_update_policy_target_mismatch: expected ${target}, received ${initialWorkerPolicy.executionTarget}.`);
|
|
@@ -4618,8 +5406,23 @@ async function startWorker(options = {}) {
|
|
|
4618
5406
|
const supervisorLock = acquireWorkerSupervisorLock({ supervisorType, workerName });
|
|
4619
5407
|
try {
|
|
4620
5408
|
await removeStaleAgentRunCredentials();
|
|
5409
|
+
await (0, game_source_git_1.maintainGameSourceCaches)(resolveWorkerHomeDir()).catch((error) => {
|
|
5410
|
+
console.error(`Game source cache maintenance failed at startup: ${error instanceof Error ? error.message : String(error)}`);
|
|
5411
|
+
});
|
|
5412
|
+
const workerRedisAdminUrl = node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_REDIS_ADMIN_URL?.trim() ?? "";
|
|
5413
|
+
const workerMongoAdminUrl = node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_MONGO_ADMIN_URL?.trim() ?? "";
|
|
5414
|
+
if (target === "FIRST_PARTY" && workerRedisAdminUrl && workerMongoAdminUrl) {
|
|
5415
|
+
const cleaned = await (0, game_server_infra_1.cleanupStaleWorkerGameServerDevResources)({
|
|
5416
|
+
redisAdminUrl: workerRedisAdminUrl,
|
|
5417
|
+
mongoAdminUrl: workerMongoAdminUrl,
|
|
5418
|
+
});
|
|
5419
|
+
if (cleaned.redisUsers > 0 || cleaned.redisKeys > 0 || cleaned.mongoDatabases > 0) {
|
|
5420
|
+
console.log(`Cleaned stale game-server development resources: Redis users=${cleaned.redisUsers}, ` +
|
|
5421
|
+
`keys=${cleaned.redisKeys}, MongoDB databases=${cleaned.mongoDatabases}.`);
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
4621
5424
|
const startupFailures = [];
|
|
4622
|
-
const local = collectWorkerLocalStatus();
|
|
5425
|
+
const local = collectWorkerLocalStatus({ requireGenerationRuntime: target === "FIRST_PARTY" });
|
|
4623
5426
|
startupFailures.push(...local.degradedReasons);
|
|
4624
5427
|
if (startupFailures.length > 0 || !local.ready || !local.capabilities || !local.playwright?.chromiumInstalled) {
|
|
4625
5428
|
throw formatWorkerPreflightFailure(startupFailures);
|
|
@@ -4632,6 +5435,14 @@ async function startWorker(options = {}) {
|
|
|
4632
5435
|
};
|
|
4633
5436
|
const maxParallelTasks = (0, runtime_1.readPositiveEnvInt)("PLAYDROP_WORKER_MAX_PARALLEL_TASKS", DEFAULT_WORKER_MAX_PARALLEL_TASKS);
|
|
4634
5437
|
let capabilities = local.capabilities;
|
|
5438
|
+
if (target === "FIRST_PARTY" && workerRedisAdminUrl && workerMongoAdminUrl) {
|
|
5439
|
+
capabilities.gameServerDevelopment = {
|
|
5440
|
+
ready: true,
|
|
5441
|
+
redisVersion: "7.4.5",
|
|
5442
|
+
mongoVersion: "8.0.26",
|
|
5443
|
+
maxParallelTasks,
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
4635
5446
|
let lastCapabilityRefreshAt = Date.now();
|
|
4636
5447
|
let lastProviderTelemetryRefreshAt = 0;
|
|
4637
5448
|
let providerTelemetryRefresh = null;
|
|
@@ -4652,6 +5463,8 @@ async function startWorker(options = {}) {
|
|
|
4652
5463
|
let shutdownSignal = null;
|
|
4653
5464
|
let fatalControlError = null;
|
|
4654
5465
|
let presenceTimer = null;
|
|
5466
|
+
let authRefreshTimer = null;
|
|
5467
|
+
let durableAuthInvalid = false;
|
|
4655
5468
|
let currentWorkerPolicy = initialWorkerPolicy;
|
|
4656
5469
|
const firstActiveTaskId = () => activeTaskIds.values().next().value ?? null;
|
|
4657
5470
|
const terminateActiveTasks = () => {
|
|
@@ -4665,7 +5478,7 @@ async function startWorker(options = {}) {
|
|
|
4665
5478
|
});
|
|
4666
5479
|
const beginProviderTelemetryRefresh = () => {
|
|
4667
5480
|
if (providerTelemetryRefresh)
|
|
4668
|
-
return;
|
|
5481
|
+
return providerTelemetryRefresh;
|
|
4669
5482
|
providerTelemetryRefresh = (0, provider_telemetry_1.probeWorkerProviderTelemetry)(capabilities)
|
|
4670
5483
|
.then((telemetry) => {
|
|
4671
5484
|
capabilities = (0, provider_telemetry_1.mergeWorkerProviderTelemetry)(capabilities, telemetry);
|
|
@@ -4678,6 +5491,7 @@ async function startWorker(options = {}) {
|
|
|
4678
5491
|
lastProviderTelemetryRefreshAt = Date.now();
|
|
4679
5492
|
providerTelemetryRefresh = null;
|
|
4680
5493
|
});
|
|
5494
|
+
return providerTelemetryRefresh;
|
|
4681
5495
|
};
|
|
4682
5496
|
const presenceBody = () => ({
|
|
4683
5497
|
workerKey,
|
|
@@ -4747,7 +5561,7 @@ async function startWorker(options = {}) {
|
|
|
4747
5561
|
node_process_1.default.stdin.resume();
|
|
4748
5562
|
}
|
|
4749
5563
|
console.log(`PlayDrop Worker started as "${workerName}" (${target}).`);
|
|
4750
|
-
beginProviderTelemetryRefresh();
|
|
5564
|
+
await beginProviderTelemetryRefresh();
|
|
4751
5565
|
// Marks the session expired, kills any in-flight agent child, and lets the
|
|
4752
5566
|
// run loop finish the task lifecycle before the worker stops non-zero.
|
|
4753
5567
|
const handleSessionExpiry = () => {
|
|
@@ -4758,6 +5572,17 @@ async function startWorker(options = {}) {
|
|
|
4758
5572
|
console.error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
|
|
4759
5573
|
terminateActiveTasks();
|
|
4760
5574
|
};
|
|
5575
|
+
const quiesceForDurableAuthFailure = (error) => {
|
|
5576
|
+
if (durableAuthInvalid)
|
|
5577
|
+
return;
|
|
5578
|
+
durableAuthInvalid = true;
|
|
5579
|
+
quiescingForUpdate = true;
|
|
5580
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
5581
|
+
console.error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
|
|
5582
|
+
};
|
|
5583
|
+
authRefreshTimer = setInterval(() => {
|
|
5584
|
+
ctx.refreshToken(false).catch(quiesceForDurableAuthFailure);
|
|
5585
|
+
}, 60000);
|
|
4761
5586
|
try {
|
|
4762
5587
|
handleWorkerAction(await client.workerPresence(presenceBody()));
|
|
4763
5588
|
}
|
|
@@ -4809,7 +5634,14 @@ async function startWorker(options = {}) {
|
|
|
4809
5634
|
let agentCompletedAt = null;
|
|
4810
5635
|
let agentResult = null;
|
|
4811
5636
|
let telemetryReported = false;
|
|
5637
|
+
let gameServerDevLease = null;
|
|
5638
|
+
let gameSourceWorkspace = null;
|
|
5639
|
+
let transcriptSpoolPath = null;
|
|
5640
|
+
let transcriptSpoolSequence = 0;
|
|
4812
5641
|
let heartbeatTransientFailures = 0;
|
|
5642
|
+
let automaticContinuationKind = null;
|
|
5643
|
+
let automaticContinuationAttempted = false;
|
|
5644
|
+
let workerFailureStage = "setup";
|
|
4813
5645
|
const sendTaskHeartbeat = async () => {
|
|
4814
5646
|
try {
|
|
4815
5647
|
const heartbeat = await client.workerHeartbeatAgentTask(task.id, { workerKey, leaseToken });
|
|
@@ -4830,7 +5662,7 @@ async function startWorker(options = {}) {
|
|
|
4830
5662
|
clearInterval(heartbeatTimer);
|
|
4831
5663
|
heartbeatTimer = null;
|
|
4832
5664
|
}
|
|
4833
|
-
const refreshed = await fetchTaskDetail(client, target, task.id).catch(() => null);
|
|
5665
|
+
const refreshed = await fetchTaskDetail(client, target, task.id, workerKey).catch(() => null);
|
|
4834
5666
|
if (refreshed?.task.status === "DONE") {
|
|
4835
5667
|
fenced = true;
|
|
4836
5668
|
if (!heartbeatObservedFinalizedTask) {
|
|
@@ -4859,7 +5691,7 @@ async function startWorker(options = {}) {
|
|
|
4859
5691
|
console.error(`Agent task heartbeat failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4860
5692
|
}
|
|
4861
5693
|
};
|
|
4862
|
-
const reportTelemetry = async (status, setupError) => {
|
|
5694
|
+
const reportTelemetry = async (status, setupError, terminalReason) => {
|
|
4863
5695
|
const completedAt = agentCompletedAt ?? new Date();
|
|
4864
5696
|
await recordAgentRunTelemetry({
|
|
4865
5697
|
client,
|
|
@@ -4875,11 +5707,20 @@ async function startWorker(options = {}) {
|
|
|
4875
5707
|
observedSkillPaths: [...observedSkillPaths],
|
|
4876
5708
|
startedAt: agentStartedAt ?? completedAt,
|
|
4877
5709
|
completedAt,
|
|
5710
|
+
...(terminalReason ? { terminalReason } : {}),
|
|
4878
5711
|
});
|
|
4879
5712
|
telemetryReported = true;
|
|
4880
5713
|
};
|
|
4881
5714
|
const failUploadedArtifactWithoutTaskDone = async () => {
|
|
4882
5715
|
const failureCode = "agent_exited_after_upload_without_task_done";
|
|
5716
|
+
const terminalReason = {
|
|
5717
|
+
kind: "failure",
|
|
5718
|
+
category: "agent_failure",
|
|
5719
|
+
code: failureCode,
|
|
5720
|
+
message: "The game was uploaded, but the task could not be finalized. Clear this error to continue.",
|
|
5721
|
+
retryable: false,
|
|
5722
|
+
source: "worker",
|
|
5723
|
+
};
|
|
4883
5724
|
retainWorkspace = true;
|
|
4884
5725
|
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4885
5726
|
workerKey,
|
|
@@ -4894,21 +5735,73 @@ async function startWorker(options = {}) {
|
|
|
4894
5735
|
workerKey,
|
|
4895
5736
|
leaseToken,
|
|
4896
5737
|
error: failureCode,
|
|
4897
|
-
terminalReason
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
5738
|
+
terminalReason,
|
|
5739
|
+
});
|
|
5740
|
+
await reportTelemetry("FAILED", undefined, terminalReason);
|
|
5741
|
+
};
|
|
5742
|
+
const readRecordedTaskUploadFailure = () => {
|
|
5743
|
+
if (!workspaceDir)
|
|
5744
|
+
return null;
|
|
5745
|
+
const failure = readTaskUploadFailureFile(workspaceDir);
|
|
5746
|
+
if (!failure)
|
|
5747
|
+
return null;
|
|
5748
|
+
if (failure.taskId !== task.id || failure.attempt !== taskContext.attempt) {
|
|
5749
|
+
throw new Error("task_upload_failure_context_mismatch");
|
|
5750
|
+
}
|
|
5751
|
+
return failure;
|
|
5752
|
+
};
|
|
5753
|
+
const hasReportedGameSourceCompletion = () => {
|
|
5754
|
+
if (!assignment.gameSource || task.kind === "STATIC_GAME" || !workspaceDir) {
|
|
5755
|
+
return false;
|
|
5756
|
+
}
|
|
5757
|
+
const intent = readWorkerGameSourceCompletion(workspaceDir);
|
|
5758
|
+
return intent?.taskId === task.id && intent.attempt === taskContext.attempt;
|
|
5759
|
+
};
|
|
5760
|
+
const taskStillMissingAgentOutcome = (detail) => detail.task.status === "RUNNING" &&
|
|
5761
|
+
task.kind !== "STATIC_GAME" &&
|
|
5762
|
+
task.kind !== "RELEASE_SMOKE" &&
|
|
5763
|
+
!hasReportedGameSourceCompletion();
|
|
5764
|
+
const failMissingAgentOutcome = async (runResult) => {
|
|
5765
|
+
const terminalReason = missingAgentOutcomeTerminalReason();
|
|
5766
|
+
retainWorkspace = true;
|
|
5767
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5768
|
+
workerKey,
|
|
5769
|
+
leaseToken,
|
|
5770
|
+
kind: "progress",
|
|
5771
|
+
phase: "failed",
|
|
5772
|
+
message: terminalReason.message,
|
|
5773
|
+
pct: null,
|
|
5774
|
+
payload: { error: terminalReason.code },
|
|
5775
|
+
});
|
|
5776
|
+
await failTaskWithRetry(client, task.id, {
|
|
5777
|
+
workerKey,
|
|
5778
|
+
leaseToken,
|
|
5779
|
+
error: terminalReason.code,
|
|
5780
|
+
terminalReason,
|
|
5781
|
+
result: runResult,
|
|
5782
|
+
});
|
|
5783
|
+
await reportTelemetry("FAILED", undefined, terminalReason);
|
|
5784
|
+
};
|
|
5785
|
+
const failCreationTimeout = async (runResult) => {
|
|
5786
|
+
const terminalReason = creationTimeoutTerminalReason();
|
|
5787
|
+
retainWorkspace = true;
|
|
5788
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5789
|
+
workerKey,
|
|
5790
|
+
leaseToken,
|
|
5791
|
+
kind: "progress",
|
|
5792
|
+
phase: "failed",
|
|
5793
|
+
message: terminalReason.message,
|
|
5794
|
+
pct: null,
|
|
5795
|
+
payload: { error: terminalReason.code },
|
|
5796
|
+
});
|
|
5797
|
+
await failTaskWithRetry(client, task.id, {
|
|
5798
|
+
workerKey,
|
|
5799
|
+
leaseToken,
|
|
5800
|
+
error: terminalReason.code,
|
|
5801
|
+
terminalReason,
|
|
5802
|
+
result: runResult,
|
|
4910
5803
|
});
|
|
4911
|
-
await reportTelemetry("FAILED");
|
|
5804
|
+
await reportTelemetry("FAILED", undefined, terminalReason);
|
|
4912
5805
|
};
|
|
4913
5806
|
try {
|
|
4914
5807
|
await sendTaskHeartbeat();
|
|
@@ -4936,6 +5829,25 @@ async function startWorker(options = {}) {
|
|
|
4936
5829
|
codexModel?.model ?? claudeModel?.model ?? grokModel?.model ?? assignment.agent.model;
|
|
4937
5830
|
reasoningEffort = codexModel?.reasoningEffort ?? claudeModel?.effort ?? grokModel?.reasoningEffort ?? null;
|
|
4938
5831
|
const cleanRoom = task.kind === "STATIC_GAME";
|
|
5832
|
+
if (task.kind === "NEW_GAME" || task.kind === "REMIX_GAME" || task.kind === "GAME_UPDATE") {
|
|
5833
|
+
const managedRedisAdminUrl = node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_REDIS_ADMIN_URL?.trim() ?? "";
|
|
5834
|
+
const managedMongoAdminUrl = node_process_1.default.env.PLAYDROP_WORKER_GAME_SERVER_MONGO_ADMIN_URL?.trim() ?? "";
|
|
5835
|
+
const redisAdminUrl = managedRedisAdminUrl || node_process_1.default.env.PLAYDROP_DEV_REDIS_URL?.trim() || "";
|
|
5836
|
+
const mongoAdminUrl = managedMongoAdminUrl || node_process_1.default.env.PLAYDROP_DEV_MONGO_URL?.trim() || "";
|
|
5837
|
+
if (target === "FIRST_PARTY" && (!redisAdminUrl || !mongoAdminUrl)) {
|
|
5838
|
+
throw new Error("worker_game_server_dev_infrastructure_missing");
|
|
5839
|
+
}
|
|
5840
|
+
if (redisAdminUrl && mongoAdminUrl) {
|
|
5841
|
+
gameServerDevLease = await (0, game_server_infra_1.provisionWorkerGameServerDevLease)({
|
|
5842
|
+
taskId: task.id,
|
|
5843
|
+
attempt: taskContext.attempt,
|
|
5844
|
+
environment: env,
|
|
5845
|
+
redisAdminUrl,
|
|
5846
|
+
mongoAdminUrl,
|
|
5847
|
+
requireRedisAcl: Boolean(managedRedisAdminUrl),
|
|
5848
|
+
});
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
4939
5851
|
workspaceDir = workerTaskWorkspaceDirFromAssignment(assignment);
|
|
4940
5852
|
const preparedWorkspace = await prepareClaimedTaskWorkspace({
|
|
4941
5853
|
workspaceDir,
|
|
@@ -4944,11 +5856,30 @@ async function startWorker(options = {}) {
|
|
|
4944
5856
|
if (preparedWorkspace.resumed) {
|
|
4945
5857
|
console.log(`Agent task ${task.id} retry resumed retained workspace attempt ${task.attempts}.`);
|
|
4946
5858
|
}
|
|
4947
|
-
|
|
5859
|
+
if (assignment.gameSource) {
|
|
5860
|
+
gameSourceWorkspace = await (0, game_source_git_1.setupGameSourceWorkspace)({
|
|
5861
|
+
assignment,
|
|
5862
|
+
workspaceDir,
|
|
5863
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
5864
|
+
client,
|
|
5865
|
+
workerKey,
|
|
5866
|
+
leaseToken,
|
|
5867
|
+
});
|
|
5868
|
+
}
|
|
5869
|
+
agentWorkspaceDir = gameSourceWorkspace?.projectDir ?? (cleanRoom ? node_path_1.default.join(workspaceDir, "project") : workspaceDir);
|
|
4948
5870
|
await (0, promises_1.mkdir)(agentWorkspaceDir, { recursive: true });
|
|
4949
5871
|
const eventDir = node_path_1.default.join(workspaceDir, ".playdrop", "task-events");
|
|
4950
5872
|
await (0, promises_1.mkdir)(eventDir, { recursive: true });
|
|
4951
|
-
|
|
5873
|
+
transcriptSpoolPath = node_path_1.default.join(workspaceDir, ".playdrop", `transcript-attempt-${taskContext.attempt}.jsonl`);
|
|
5874
|
+
if (!(0, node_fs_1.existsSync)(transcriptSpoolPath)) {
|
|
5875
|
+
await (0, promises_1.writeFile)(transcriptSpoolPath, `${JSON.stringify({
|
|
5876
|
+
version: 1,
|
|
5877
|
+
taskId: task.id,
|
|
5878
|
+
attempt: taskContext.attempt,
|
|
5879
|
+
createdAt: new Date().toISOString(),
|
|
5880
|
+
})}\n`, "utf8");
|
|
5881
|
+
}
|
|
5882
|
+
if (!cleanRoom || assignment.gameSource) {
|
|
4952
5883
|
await stageWorkerTaskContext({
|
|
4953
5884
|
workspaceDir,
|
|
4954
5885
|
env,
|
|
@@ -4963,6 +5894,7 @@ async function startWorker(options = {}) {
|
|
|
4963
5894
|
taskId: taskContext.taskId,
|
|
4964
5895
|
attempt: taskContext.attempt,
|
|
4965
5896
|
devPort,
|
|
5897
|
+
gameSourceGit: Boolean(assignment.gameSource),
|
|
4966
5898
|
});
|
|
4967
5899
|
const handleEventDrainFailure = (error) => {
|
|
4968
5900
|
const classification = classifyWorkerEventDrainError(error);
|
|
@@ -4991,11 +5923,20 @@ async function startWorker(options = {}) {
|
|
|
4991
5923
|
}
|
|
4992
5924
|
}
|
|
4993
5925
|
};
|
|
4994
|
-
const
|
|
5926
|
+
const appendTranscriptSpool = async (chunks) => {
|
|
4995
5927
|
appendObservedSkillPathsFromTranscript(chunks);
|
|
4996
|
-
|
|
5928
|
+
if (transcriptSpoolPath) {
|
|
5929
|
+
const lines = chunks.map((chunk) => JSON.stringify({
|
|
5930
|
+
sequence: ++transcriptSpoolSequence,
|
|
5931
|
+
stream: chunk.stream,
|
|
5932
|
+
content: chunk.content,
|
|
5933
|
+
createdAt: new Date().toISOString(),
|
|
5934
|
+
})).join("\n");
|
|
5935
|
+
if (lines)
|
|
5936
|
+
await (0, promises_1.appendFile)(transcriptSpoolPath, `${lines}\n`, "utf8");
|
|
5937
|
+
}
|
|
4997
5938
|
};
|
|
4998
|
-
await
|
|
5939
|
+
await stageAssignmentWorkspace(agentWorkspaceDir, assignment.workspace);
|
|
4999
5940
|
if (fenced) {
|
|
5000
5941
|
throw new Error("agent_task_lease_invalid");
|
|
5001
5942
|
}
|
|
@@ -5030,7 +5971,7 @@ async function startWorker(options = {}) {
|
|
|
5030
5971
|
}
|
|
5031
5972
|
const taskClient = (0, apiClient_1.createCliApiClient)({
|
|
5032
5973
|
baseUrl: envConfig.apiBase,
|
|
5033
|
-
|
|
5974
|
+
tokenProvider: ctx.getToken,
|
|
5034
5975
|
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
5035
5976
|
agentTaskToken: assignment.task.token,
|
|
5036
5977
|
agentTaskId: task.id,
|
|
@@ -5047,7 +5988,7 @@ async function startWorker(options = {}) {
|
|
|
5047
5988
|
webBase: envConfig.webBase ?? null,
|
|
5048
5989
|
cdnBase: envConfig.cdnBase,
|
|
5049
5990
|
devRouterPort: devPort,
|
|
5050
|
-
token,
|
|
5991
|
+
token: ctx.getToken(),
|
|
5051
5992
|
user: me.user,
|
|
5052
5993
|
skipReview: true,
|
|
5053
5994
|
});
|
|
@@ -5105,7 +6046,7 @@ async function startWorker(options = {}) {
|
|
|
5105
6046
|
}, 1000);
|
|
5106
6047
|
const gameCreationTask = usesCreatorRecovery(task.kind);
|
|
5107
6048
|
const startsInCreationRecovery = gameCreationTask && preparedWorkspace.resumed;
|
|
5108
|
-
const
|
|
6049
|
+
const basePrompt = startsInCreationRecovery
|
|
5109
6050
|
? buildCreatorRecoveryPrompt({
|
|
5110
6051
|
prompt: assignment.request.prompt,
|
|
5111
6052
|
kind: task.kind,
|
|
@@ -5113,6 +6054,7 @@ async function startWorker(options = {}) {
|
|
|
5113
6054
|
: task.kind === "GAME_EVAL" && preparedWorkspace.resumed
|
|
5114
6055
|
? buildGameEvalRetryPrompt(assignment.request.prompt)
|
|
5115
6056
|
: assignment.request.prompt;
|
|
6057
|
+
const prompt = basePrompt;
|
|
5116
6058
|
const imageAttachments = assignment.request.attachments
|
|
5117
6059
|
.filter((attachment) => attachment.contentType.startsWith("image/"))
|
|
5118
6060
|
.map((attachment) => ({
|
|
@@ -5144,6 +6086,28 @@ async function startWorker(options = {}) {
|
|
|
5144
6086
|
}
|
|
5145
6087
|
};
|
|
5146
6088
|
const runAssignedAgent = async (input) => {
|
|
6089
|
+
const fixtureKind = (0, e2e_fixtures_1.detectPlaydropE2EFixture)(assignment.request.prompt);
|
|
6090
|
+
if (fixtureKind) {
|
|
6091
|
+
if (!(0, e2e_fixtures_1.isPlaydropE2EGameKind)(task.kind) || fixtureKind !== task.kind) {
|
|
6092
|
+
throw new Error(`playdrop_e2e_marker_kind_mismatch:expected_${task.kind}:received_${fixtureKind}`);
|
|
6093
|
+
}
|
|
6094
|
+
if (!assignment.gameSource || !gameSourceWorkspace || !workspaceDir || !agentWorkspaceDir) {
|
|
6095
|
+
throw new Error("playdrop_e2e_game_source_git_required");
|
|
6096
|
+
}
|
|
6097
|
+
return runPlaydropE2EGameFixture({
|
|
6098
|
+
projectDir: agentWorkspaceDir,
|
|
6099
|
+
workspaceDir,
|
|
6100
|
+
binDir,
|
|
6101
|
+
eventDir,
|
|
6102
|
+
envName: env,
|
|
6103
|
+
devPort,
|
|
6104
|
+
workerUsername: username,
|
|
6105
|
+
taskContext,
|
|
6106
|
+
gameServerDev: gameServerDevLease,
|
|
6107
|
+
onTranscriptChunks: appendTranscriptSpool,
|
|
6108
|
+
onChild: handleAgentChild,
|
|
6109
|
+
});
|
|
6110
|
+
}
|
|
5147
6111
|
if (assignment.agent.runtime === "CODEX") {
|
|
5148
6112
|
return runCodex({
|
|
5149
6113
|
workspaceDir: agentWorkspaceDir,
|
|
@@ -5155,18 +6119,21 @@ async function startWorker(options = {}) {
|
|
|
5155
6119
|
workerUsername: username,
|
|
5156
6120
|
taskId: task.id,
|
|
5157
6121
|
taskToken: assignment.task.token,
|
|
6122
|
+
taskAccessToken: assignment.task.accessToken ?? null,
|
|
5158
6123
|
attempt: taskContext.attempt,
|
|
5159
6124
|
devPort,
|
|
6125
|
+
gameServerDev: gameServerDevLease,
|
|
5160
6126
|
codexModel: codexModel ?? resolveCodexModel(assignment.agent.model),
|
|
5161
6127
|
taskPluginRoot: assignmentPluginRoot,
|
|
5162
6128
|
enablePlaywrightMcp: assignment.agent.browser === "PLAYWRIGHT",
|
|
5163
6129
|
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
5164
6130
|
imagePaths: imageAttachments.map((attachment) => attachment.path),
|
|
5165
6131
|
onTranscriptChunks: async (chunks) => {
|
|
5166
|
-
await
|
|
6132
|
+
await appendTranscriptSpool(chunks);
|
|
5167
6133
|
},
|
|
5168
6134
|
onChild: handleAgentChild,
|
|
5169
6135
|
cleanRoom,
|
|
6136
|
+
allowGit: Boolean(assignment.gameSource),
|
|
5170
6137
|
timeoutMs: input.timeoutMs,
|
|
5171
6138
|
resumeSessionId: input.resumeSessionId,
|
|
5172
6139
|
});
|
|
@@ -5185,16 +6152,19 @@ async function startWorker(options = {}) {
|
|
|
5185
6152
|
workerUsername: username,
|
|
5186
6153
|
taskId: task.id,
|
|
5187
6154
|
taskToken: assignment.task.token,
|
|
6155
|
+
taskAccessToken: assignment.task.accessToken ?? null,
|
|
5188
6156
|
attempt: taskContext.attempt,
|
|
5189
6157
|
devPort,
|
|
6158
|
+
gameServerDev: gameServerDevLease,
|
|
5190
6159
|
claudeModel: claudeModel ?? resolveClaudeModel(assignment.agent.model),
|
|
5191
6160
|
taskPluginRoot: assignmentPluginRoot,
|
|
5192
6161
|
imageAttachments,
|
|
5193
6162
|
onTranscriptChunks: async (chunks) => {
|
|
5194
|
-
await
|
|
6163
|
+
await appendTranscriptSpool(chunks);
|
|
5195
6164
|
},
|
|
5196
6165
|
onChild: handleAgentChild,
|
|
5197
6166
|
cleanRoom,
|
|
6167
|
+
allowGit: Boolean(assignment.gameSource),
|
|
5198
6168
|
timeoutMs: input.timeoutMs,
|
|
5199
6169
|
resumeSessionId: input.resumeSessionId,
|
|
5200
6170
|
});
|
|
@@ -5213,15 +6183,17 @@ async function startWorker(options = {}) {
|
|
|
5213
6183
|
workerUsername: username,
|
|
5214
6184
|
taskId: task.id,
|
|
5215
6185
|
taskToken: assignment.task.token,
|
|
6186
|
+
taskAccessToken: assignment.task.accessToken ?? null,
|
|
5216
6187
|
attempt: taskContext.attempt,
|
|
5217
6188
|
devPort,
|
|
6189
|
+
gameServerDev: gameServerDevLease,
|
|
5218
6190
|
grokModel: grokModel ?? resolveGrokModel(assignment.agent.model),
|
|
5219
6191
|
taskPluginRoot: assignmentPluginRoot,
|
|
5220
6192
|
imageAttachments,
|
|
5221
6193
|
enablePlaywrightMcp: assignment.agent.browser === "PLAYWRIGHT",
|
|
5222
6194
|
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
5223
6195
|
onTranscriptChunks: async (chunks) => {
|
|
5224
|
-
await
|
|
6196
|
+
await appendTranscriptSpool(chunks);
|
|
5225
6197
|
},
|
|
5226
6198
|
onChild: handleAgentChild,
|
|
5227
6199
|
cleanRoom,
|
|
@@ -5251,6 +6223,7 @@ async function startWorker(options = {}) {
|
|
|
5251
6223
|
message: "Resumed the retained game workspace with a focused creation recovery window.",
|
|
5252
6224
|
});
|
|
5253
6225
|
}
|
|
6226
|
+
workerFailureStage = "agent";
|
|
5254
6227
|
agentResult = await runAssignedAgent({
|
|
5255
6228
|
prompt,
|
|
5256
6229
|
timeoutMs: startsInCreationRecovery
|
|
@@ -5259,6 +6232,7 @@ async function startWorker(options = {}) {
|
|
|
5259
6232
|
? runtime_1.DEFAULT_CREATOR_AGENT_TIMEOUT_MS
|
|
5260
6233
|
: undefined,
|
|
5261
6234
|
});
|
|
6235
|
+
workerFailureStage = "post_agent_detail";
|
|
5262
6236
|
if (startsInCreationRecovery && agentResult.timedOut) {
|
|
5263
6237
|
const marker = "agent_task_creation_recovery_timeout";
|
|
5264
6238
|
agentResult = {
|
|
@@ -5269,9 +6243,10 @@ async function startWorker(options = {}) {
|
|
|
5269
6243
|
}
|
|
5270
6244
|
if (gameCreationTask && !startsInCreationRecovery && agentResult.timedOut && !fenced && !shuttingDown) {
|
|
5271
6245
|
const timedOutResult = agentResult;
|
|
5272
|
-
const refreshedAfterTimeout = await fetchTaskDetail(client, target, task.id);
|
|
6246
|
+
const refreshedAfterTimeout = await fetchTaskDetail(client, target, task.id, workerKey);
|
|
5273
6247
|
if (refreshedAfterTimeout.task.status === "RUNNING" &&
|
|
5274
6248
|
!hasAgentTaskUploadedArtifact(refreshedAfterTimeout.task)) {
|
|
6249
|
+
automaticContinuationKind = "timeout";
|
|
5275
6250
|
const providerSessionId = timedOutResult.providerSessionId?.trim() ?? "";
|
|
5276
6251
|
if (!providerSessionId) {
|
|
5277
6252
|
const marker = "agent_task_resume_session_missing";
|
|
@@ -5282,6 +6257,7 @@ async function startWorker(options = {}) {
|
|
|
5282
6257
|
};
|
|
5283
6258
|
}
|
|
5284
6259
|
else {
|
|
6260
|
+
automaticContinuationAttempted = true;
|
|
5285
6261
|
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5286
6262
|
workerKey,
|
|
5287
6263
|
leaseToken,
|
|
@@ -5301,15 +6277,67 @@ async function startWorker(options = {}) {
|
|
|
5301
6277
|
}
|
|
5302
6278
|
}
|
|
5303
6279
|
}
|
|
6280
|
+
if (!automaticContinuationKind) {
|
|
6281
|
+
const refreshedAfterExit = await fetchTaskDetail(client, target, task.id, workerKey);
|
|
6282
|
+
if (shouldRecoverMissingAgentOutcome({
|
|
6283
|
+
kind: task.kind,
|
|
6284
|
+
taskStatus: refreshedAfterExit.task.status,
|
|
6285
|
+
completionReported: hasReportedGameSourceCompletion(),
|
|
6286
|
+
result: agentResult,
|
|
6287
|
+
})) {
|
|
6288
|
+
automaticContinuationKind = "missing_outcome";
|
|
6289
|
+
const initialResult = agentResult;
|
|
6290
|
+
const providerSessionId = initialResult.providerSessionId?.trim() ?? "";
|
|
6291
|
+
if (!providerSessionId) {
|
|
6292
|
+
const marker = "agent_task_outcome_recovery_session_missing";
|
|
6293
|
+
agentResult = {
|
|
6294
|
+
...initialResult,
|
|
6295
|
+
stderr: `${initialResult.stderr}\n${marker}`.trim(),
|
|
6296
|
+
outputTail: `${initialResult.outputTail}\n${marker}`.trim(),
|
|
6297
|
+
};
|
|
6298
|
+
}
|
|
6299
|
+
else {
|
|
6300
|
+
automaticContinuationAttempted = true;
|
|
6301
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
6302
|
+
workerKey,
|
|
6303
|
+
leaseToken,
|
|
6304
|
+
kind: "progress",
|
|
6305
|
+
phase: "outcome_recovery",
|
|
6306
|
+
message: "The agent stopped before confirming the result. Asking it to resume and finish the task.",
|
|
6307
|
+
});
|
|
6308
|
+
const recoveryResult = await runAssignedAgent({
|
|
6309
|
+
prompt: buildMissingAgentOutcomeRecoveryPrompt(task.kind),
|
|
6310
|
+
timeoutMs: runtime_1.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS,
|
|
6311
|
+
resumeSessionId: providerSessionId,
|
|
6312
|
+
});
|
|
6313
|
+
agentResult = mergeMissingAgentOutcomeRecoveryResults(assignment.agent.runtime, initialResult, recoveryResult);
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
5304
6317
|
resolvedRuntimeModel = agentResult.providerResolvedModel?.trim() || resolvedRuntimeModel;
|
|
5305
6318
|
agentCompletedAt = new Date();
|
|
5306
6319
|
await queueEventDrain().catch((error) => {
|
|
5307
6320
|
handleEventDrainFailure(error);
|
|
5308
6321
|
});
|
|
6322
|
+
void archiveWorkerTranscriptBestEffort({
|
|
6323
|
+
client,
|
|
6324
|
+
taskId: task.id,
|
|
6325
|
+
attempt: taskContext.attempt,
|
|
6326
|
+
workerKey,
|
|
6327
|
+
leaseToken,
|
|
6328
|
+
transcriptPath: transcriptSpoolPath,
|
|
6329
|
+
});
|
|
5309
6330
|
const completedAgentResult = agentResult;
|
|
5310
6331
|
const agentRunResult = buildAgentRunResult(completedAgentResult);
|
|
6332
|
+
if (assignment.gameSource &&
|
|
6333
|
+
gameSourceWorkspace &&
|
|
6334
|
+
(completedAgentResult.timedOut || completedAgentResult.exitCode !== 0 || completedAgentResult.signal)) {
|
|
6335
|
+
await (0, game_source_git_1.checkpointFailedGameSourceWorkspace)({ workspace: gameSourceWorkspace, taskId: task.id }).catch((error) => {
|
|
6336
|
+
console.error(`Failed Git workspace checkpoint did not complete: ${error instanceof Error ? error.message : String(error)}`);
|
|
6337
|
+
});
|
|
6338
|
+
}
|
|
5311
6339
|
if (fenced) {
|
|
5312
|
-
const refreshed = await fetchTaskDetail(client, target, task.id).catch(() => null);
|
|
6340
|
+
const refreshed = await fetchTaskDetail(client, target, task.id, workerKey).catch(() => null);
|
|
5313
6341
|
if (refreshed && refreshed.task.status !== "RUNNING" && refreshed.task.status !== "QUEUED") {
|
|
5314
6342
|
retainWorkspace = refreshed.task.status === "FAILED";
|
|
5315
6343
|
console.error(`Agent task ${task.id} was finalized by the server as ${refreshed.task.status}.`);
|
|
@@ -5330,18 +6358,20 @@ async function startWorker(options = {}) {
|
|
|
5330
6358
|
});
|
|
5331
6359
|
}
|
|
5332
6360
|
else if (shuttingDown) {
|
|
6361
|
+
const terminalReason = (0, types_1.classifyAgentTaskTerminalReason)("worker_shutdown", "worker");
|
|
5333
6362
|
await failTaskWithRetry(client, task.id, {
|
|
5334
6363
|
workerKey,
|
|
5335
6364
|
leaseToken,
|
|
5336
6365
|
error: "worker_shutdown",
|
|
6366
|
+
terminalReason,
|
|
5337
6367
|
result: agentRunResult,
|
|
5338
6368
|
});
|
|
5339
|
-
await reportTelemetry("FAILED");
|
|
6369
|
+
await reportTelemetry("FAILED", undefined, terminalReason);
|
|
5340
6370
|
}
|
|
5341
6371
|
else if (completedAgentResult.timedOut ||
|
|
5342
6372
|
completedAgentResult.exitCode !== 0 ||
|
|
5343
6373
|
completedAgentResult.signal) {
|
|
5344
|
-
const refreshed = await fetchTaskDetail(client, target, task.id);
|
|
6374
|
+
const refreshed = await fetchTaskDetail(client, target, task.id, workerKey);
|
|
5345
6375
|
if (refreshed.task.status !== "RUNNING") {
|
|
5346
6376
|
retainWorkspace = refreshed.task.status === "FAILED";
|
|
5347
6377
|
console.error(`Agent task ${task.id} was finalized by the agent as ${refreshed.task.status}.`);
|
|
@@ -5351,19 +6381,32 @@ async function startWorker(options = {}) {
|
|
|
5351
6381
|
console.error(`Agent task ${task.id} uploaded an artifact but exited before task completion; failing it explicitly.`);
|
|
5352
6382
|
await failUploadedArtifactWithoutTaskDone();
|
|
5353
6383
|
}
|
|
6384
|
+
else if (automaticContinuationKind === "missing_outcome" &&
|
|
6385
|
+
taskStillMissingAgentOutcome(refreshed)) {
|
|
6386
|
+
await failMissingAgentOutcome(agentRunResult);
|
|
6387
|
+
}
|
|
6388
|
+
else if (automaticContinuationKind === "timeout" &&
|
|
6389
|
+
taskStillMissingAgentOutcome(refreshed) &&
|
|
6390
|
+
!automaticContinuationAttempted) {
|
|
6391
|
+
await failCreationTimeout(agentRunResult);
|
|
6392
|
+
}
|
|
5354
6393
|
else {
|
|
5355
6394
|
retainWorkspace = true;
|
|
5356
|
-
const
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
6395
|
+
const uploadFailure = readRecordedTaskUploadFailure();
|
|
6396
|
+
const failureCode = uploadFailure?.terminalReason.code ??
|
|
6397
|
+
buildAgentFailureCode(assignment.agent.runtime, completedAgentResult, {
|
|
6398
|
+
model: assignment.agent.model,
|
|
6399
|
+
reasoningEffort,
|
|
6400
|
+
});
|
|
6401
|
+
const availabilityReason = uploadFailure
|
|
6402
|
+
? null
|
|
6403
|
+
: classifyAgentAvailabilityFailureReason(failureCode, task.kind === "GAME_REVIEW");
|
|
5361
6404
|
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5362
6405
|
workerKey,
|
|
5363
6406
|
leaseToken,
|
|
5364
6407
|
kind: "progress",
|
|
5365
6408
|
phase: "failed",
|
|
5366
|
-
message: describeAgentFailureForEvent(failureCode),
|
|
6409
|
+
message: uploadFailure?.terminalReason.message ?? describeAgentFailureForEvent(failureCode),
|
|
5367
6410
|
pct: null,
|
|
5368
6411
|
payload: { error: failureCode },
|
|
5369
6412
|
});
|
|
@@ -5388,7 +6431,9 @@ async function startWorker(options = {}) {
|
|
|
5388
6431
|
workerKey,
|
|
5389
6432
|
leaseToken,
|
|
5390
6433
|
error: failureCode,
|
|
5391
|
-
terminalReason:
|
|
6434
|
+
terminalReason: uploadFailure
|
|
6435
|
+
? uploadFailure.terminalReason
|
|
6436
|
+
: (0, types_1.classifyAgentTaskTerminalReason)(failureCode, "agent"),
|
|
5392
6437
|
result: agentRunResult,
|
|
5393
6438
|
});
|
|
5394
6439
|
await reportTelemetry("FAILED");
|
|
@@ -5396,18 +6441,237 @@ async function startWorker(options = {}) {
|
|
|
5396
6441
|
}
|
|
5397
6442
|
}
|
|
5398
6443
|
else {
|
|
5399
|
-
const refreshed = await fetchTaskDetail(client, target, task.id);
|
|
6444
|
+
const refreshed = await fetchTaskDetail(client, target, task.id, workerKey);
|
|
5400
6445
|
if (refreshed.task.status !== "RUNNING") {
|
|
5401
6446
|
retainWorkspace = refreshed.task.status === "FAILED";
|
|
5402
6447
|
console.error(`Agent task ${task.id} was finalized by the agent as ${refreshed.task.status}.`);
|
|
5403
6448
|
await reportTelemetry(refreshed.task.status);
|
|
5404
6449
|
}
|
|
5405
|
-
else if (
|
|
5406
|
-
|
|
5407
|
-
(
|
|
5408
|
-
|
|
5409
|
-
|
|
6450
|
+
else if (taskStillMissingAgentOutcome(refreshed) &&
|
|
6451
|
+
hasAgentTaskUploadedArtifact(refreshed.task)) {
|
|
6452
|
+
console.error(`Agent task ${task.id} uploaded an artifact but exited before task completion; failing it explicitly.`);
|
|
6453
|
+
await failUploadedArtifactWithoutTaskDone();
|
|
6454
|
+
}
|
|
6455
|
+
else if (automaticContinuationKind === "timeout" &&
|
|
6456
|
+
taskStillMissingAgentOutcome(refreshed)) {
|
|
6457
|
+
await failCreationTimeout(agentRunResult);
|
|
6458
|
+
}
|
|
6459
|
+
else if (automaticContinuationKind === "missing_outcome" &&
|
|
6460
|
+
taskStillMissingAgentOutcome(refreshed)) {
|
|
6461
|
+
await failMissingAgentOutcome(agentRunResult);
|
|
6462
|
+
}
|
|
6463
|
+
else if (assignment.gameSource) {
|
|
6464
|
+
if (!gameSourceWorkspace || !workspaceDir || !agentWorkspaceDir) {
|
|
6465
|
+
throw new Error("game_source_workspace_missing");
|
|
6466
|
+
}
|
|
6467
|
+
const intent = task.kind === "STATIC_GAME"
|
|
6468
|
+
? {
|
|
6469
|
+
taskId: task.id,
|
|
6470
|
+
attempt: taskContext.attempt,
|
|
6471
|
+
summary: "Created and privately hosted a standalone static web game.",
|
|
6472
|
+
}
|
|
6473
|
+
: readWorkerGameSourceCompletion(workspaceDir);
|
|
6474
|
+
if (!intent || intent.taskId !== task.id || intent.attempt !== taskContext.attempt) {
|
|
6475
|
+
throw new Error("agent_exited_without_task_done");
|
|
6476
|
+
}
|
|
6477
|
+
if (task.kind !== "STATIC_GAME" && !(0, node_fs_1.existsSync)(workerGameSourceReadinessPath(workspaceDir))) {
|
|
6478
|
+
throw new Error("agent_exited_without_task_upload");
|
|
6479
|
+
}
|
|
6480
|
+
if (task.kind === "STATIC_GAME" && !(0, node_fs_1.existsSync)(node_path_1.default.join(agentWorkspaceDir, "catalogue.json"))) {
|
|
6481
|
+
const primarySurface = resolveWorkerInstrumentSurface(taskContext);
|
|
6482
|
+
if (!primarySurface)
|
|
6483
|
+
throw new Error("static_game_artifact_primary_surface_missing");
|
|
6484
|
+
if (!envConfig.cdnBase)
|
|
6485
|
+
throw new Error("static_game_publish_cdn_base_missing");
|
|
6486
|
+
const preparedStatic = await (0, staticHtml_1.prepareStaticHtmlProject)({
|
|
6487
|
+
projectDir: agentWorkspaceDir,
|
|
6488
|
+
taskId: task.id,
|
|
6489
|
+
appName: refreshed.task.claimedAppName ?? undefined,
|
|
6490
|
+
primarySurface,
|
|
6491
|
+
cdnBase: envConfig.cdnBase,
|
|
6492
|
+
});
|
|
6493
|
+
try {
|
|
6494
|
+
const sourceTask = preparedStatic.sourceTask;
|
|
6495
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(agentWorkspaceDir, "catalogue.json"), `${JSON.stringify({
|
|
6496
|
+
apps: [{
|
|
6497
|
+
name: sourceTask.name,
|
|
6498
|
+
version: sourceTask.version,
|
|
6499
|
+
displayName: sourceTask.displayName,
|
|
6500
|
+
subtitle: sourceTask.subtitle,
|
|
6501
|
+
description: sourceTask.description,
|
|
6502
|
+
type: "GAME",
|
|
6503
|
+
authMode: "NONE",
|
|
6504
|
+
controllerMode: "UNSUPPORTED",
|
|
6505
|
+
previewable: true,
|
|
6506
|
+
file: "index.html",
|
|
6507
|
+
primarySurface,
|
|
6508
|
+
surfaceTargets: {
|
|
6509
|
+
desktop: sourceTask.surfaceTargets.includes("DESKTOP"),
|
|
6510
|
+
mobileLandscape: sourceTask.surfaceTargets.includes("MOBILE_LANDSCAPE"),
|
|
6511
|
+
mobilePortrait: sourceTask.surfaceTargets.includes("MOBILE_PORTRAIT"),
|
|
6512
|
+
},
|
|
6513
|
+
releaseNotes: sourceTask.releaseNotes,
|
|
6514
|
+
hostingMode: "HOSTED",
|
|
6515
|
+
license: "CLOSED",
|
|
6516
|
+
}],
|
|
6517
|
+
}, null, 2)}\n`, "utf8");
|
|
6518
|
+
}
|
|
6519
|
+
finally {
|
|
6520
|
+
await preparedStatic.cleanup();
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6523
|
+
const outputProjectDir = discoverWorkerProjectRoot(agentWorkspaceDir);
|
|
6524
|
+
if (node_path_1.default.resolve(outputProjectDir) !== node_path_1.default.resolve(gameSourceWorkspace.projectDir)) {
|
|
6525
|
+
throw new Error("game_source_output_must_be_repository_root");
|
|
6526
|
+
}
|
|
6527
|
+
workerFailureStage = "game_source_reconcile";
|
|
6528
|
+
const reconciled = await (0, game_source_git_1.reconcileGameSourceWorkspace)({
|
|
6529
|
+
workspace: gameSourceWorkspace,
|
|
6530
|
+
taskId: task.id,
|
|
6531
|
+
summary: intent.summary,
|
|
6532
|
+
});
|
|
6533
|
+
const taskClient = (0, apiClient_1.createCliApiClient)({
|
|
6534
|
+
baseUrl: envConfig.apiBase,
|
|
6535
|
+
tokenProvider: ctx.getToken,
|
|
6536
|
+
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
6537
|
+
agentTaskToken: assignment.task.token,
|
|
6538
|
+
agentTaskId: task.id,
|
|
6539
|
+
agentTaskAttempt: taskContext.attempt,
|
|
6540
|
+
});
|
|
6541
|
+
let published;
|
|
6542
|
+
let finalizedGameSource = null;
|
|
6543
|
+
let preparedGameSource = null;
|
|
6544
|
+
const prepareGitTransition = async (input) => {
|
|
6545
|
+
const versionParts = input.version.split(".").map((part) => Number(part));
|
|
6546
|
+
if (versionParts.length !== 3 ||
|
|
6547
|
+
versionParts.some((part) => !Number.isSafeInteger(part) || part < 0)) {
|
|
6548
|
+
throw new Error("game_source_version_invalid");
|
|
6549
|
+
}
|
|
6550
|
+
finalizedGameSource = await (0, game_source_git_1.finalizeGameSourceRepository)({
|
|
6551
|
+
reconciled,
|
|
6552
|
+
appVersionId: 0,
|
|
6553
|
+
version: { major: versionParts[0], minor: versionParts[1], patch: versionParts[2] },
|
|
6554
|
+
});
|
|
6555
|
+
preparedGameSource = await (0, game_source_git_1.uploadFinalizedGameSource)({
|
|
6556
|
+
client,
|
|
6557
|
+
taskId: task.id,
|
|
6558
|
+
attempt: taskContext.attempt,
|
|
6559
|
+
workerKey,
|
|
6560
|
+
leaseToken,
|
|
6561
|
+
appId: input.appId,
|
|
6562
|
+
appUploadSessionId: input.sessionId,
|
|
6563
|
+
source: finalizedGameSource,
|
|
6564
|
+
});
|
|
6565
|
+
return {
|
|
6566
|
+
commitSha: preparedGameSource.commitSha,
|
|
6567
|
+
headSha: preparedGameSource.headSha,
|
|
6568
|
+
baseGeneration: preparedGameSource.baseGeneration,
|
|
6569
|
+
bundleStorageKey: preparedGameSource.bundleStorageKey,
|
|
6570
|
+
bundleSha256: preparedGameSource.bundleSha256,
|
|
6571
|
+
bundleSizeBytes: preparedGameSource.bundleBytes,
|
|
6572
|
+
};
|
|
6573
|
+
};
|
|
6574
|
+
workerFailureStage = "game_publish";
|
|
6575
|
+
if (task.kind === "STATIC_GAME") {
|
|
6576
|
+
const primarySurface = resolveWorkerInstrumentSurface(taskContext);
|
|
6577
|
+
if (!primarySurface)
|
|
6578
|
+
throw new Error("static_game_artifact_primary_surface_missing");
|
|
6579
|
+
if (!envConfig.cdnBase)
|
|
6580
|
+
throw new Error("static_game_publish_cdn_base_missing");
|
|
6581
|
+
const result = await (0, upload_1.publishStaticHtmlProject)({
|
|
6582
|
+
client: taskClient,
|
|
6583
|
+
taskId: task.id,
|
|
6584
|
+
taskToken: assignment.task.token,
|
|
6585
|
+
projectDir: agentWorkspaceDir,
|
|
6586
|
+
creatorUsername: taskContext.creatorUsername,
|
|
6587
|
+
primarySurface,
|
|
6588
|
+
apiBase: envConfig.apiBase,
|
|
6589
|
+
webBase: envConfig.webBase ?? null,
|
|
6590
|
+
cdnBase: envConfig.cdnBase,
|
|
6591
|
+
claimedAppName: refreshed.task.claimedAppName,
|
|
6592
|
+
devRouterPort: devPort,
|
|
6593
|
+
token: ctx.getToken(),
|
|
6594
|
+
user: me.user,
|
|
6595
|
+
prepareGitTransition,
|
|
6596
|
+
});
|
|
6597
|
+
published = result;
|
|
6598
|
+
}
|
|
6599
|
+
else {
|
|
6600
|
+
const fixtureKind = (0, e2e_fixtures_1.detectPlaydropE2EFixture)(assignment.request.prompt);
|
|
6601
|
+
const result = await (0, upload_1.publishWorkerAppProject)({
|
|
6602
|
+
client: taskClient,
|
|
6603
|
+
taskId: task.id,
|
|
6604
|
+
taskToken: assignment.task.token,
|
|
6605
|
+
kind: task.kind,
|
|
6606
|
+
executionTarget: task.executionTarget,
|
|
6607
|
+
expectedAppName: taskContext.outputAppName ?? undefined,
|
|
6608
|
+
expectedDisplayName: taskContext.outputDisplayName,
|
|
6609
|
+
expectedSubtitle: taskContext.outputSubtitle,
|
|
6610
|
+
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
6611
|
+
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
6612
|
+
projectDir: agentWorkspaceDir,
|
|
6613
|
+
creatorUsername: taskContext.creatorUsername,
|
|
6614
|
+
apiBase: envConfig.apiBase,
|
|
6615
|
+
webBase: envConfig.webBase ?? null,
|
|
6616
|
+
token: ctx.getToken(),
|
|
6617
|
+
user: me.user,
|
|
6618
|
+
deterministicE2EFixture: fixtureKind === task.kind,
|
|
6619
|
+
prepareGitTransition,
|
|
6620
|
+
});
|
|
6621
|
+
published = result;
|
|
6622
|
+
}
|
|
6623
|
+
if (!finalizedGameSource || !preparedGameSource) {
|
|
6624
|
+
throw new Error("game_source_transition_not_prepared");
|
|
6625
|
+
}
|
|
6626
|
+
const finalized = finalizedGameSource;
|
|
6627
|
+
const gameSource = preparedGameSource;
|
|
6628
|
+
workerFailureStage = "task_complete";
|
|
6629
|
+
const completion = await completeWorkerAgentTaskWithRetry({
|
|
6630
|
+
client,
|
|
6631
|
+
taskId: task.id,
|
|
6632
|
+
body: {
|
|
6633
|
+
workerKey,
|
|
6634
|
+
leaseToken,
|
|
6635
|
+
appId: published.appId,
|
|
6636
|
+
appVersionId: published.appVersionId,
|
|
6637
|
+
result: {
|
|
6638
|
+
appId: published.appId,
|
|
6639
|
+
appVersionId: published.appVersionId,
|
|
6640
|
+
appName: published.appName,
|
|
6641
|
+
version: published.version,
|
|
6642
|
+
versionNodeId: published.versionNodeId,
|
|
6643
|
+
completedBy: "worker_game_source_git",
|
|
6644
|
+
},
|
|
6645
|
+
summary: intent.summary,
|
|
6646
|
+
...(intent.nextSteps ? { nextSteps: intent.nextSteps } : {}),
|
|
6647
|
+
...(intent.platformFeedback ? { platformFeedback: intent.platformFeedback } : {}),
|
|
6648
|
+
tokensUsed: agentResult.tokensUsed,
|
|
6649
|
+
gameSource,
|
|
6650
|
+
},
|
|
6651
|
+
});
|
|
6652
|
+
if (!completion.gameSource)
|
|
6653
|
+
throw new Error("game_source_completion_metadata_missing");
|
|
6654
|
+
(0, game_source_git_1.emitGameSourceWorkerEvent)("playdrop.game_source.completed", {
|
|
6655
|
+
taskId: task.id,
|
|
6656
|
+
appId: published.appId,
|
|
6657
|
+
repoGeneration: completion.gameSource.repoGeneration,
|
|
6658
|
+
bundleBytes: finalized.bundle.length,
|
|
6659
|
+
materialCount: finalized.materials.length,
|
|
6660
|
+
outcome: "DONE",
|
|
5410
6661
|
});
|
|
6662
|
+
await reportTelemetry("DONE");
|
|
6663
|
+
await (0, game_source_git_1.cleanupCompletedGameSourceWorkspace)({
|
|
6664
|
+
workspace: gameSourceWorkspace,
|
|
6665
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
6666
|
+
appId: published.appId,
|
|
6667
|
+
repoGeneration: completion.gameSource.repoGeneration,
|
|
6668
|
+
headSha: finalized.commitSha,
|
|
6669
|
+
versions: finalized.versions,
|
|
6670
|
+
});
|
|
6671
|
+
retainWorkspace = false;
|
|
6672
|
+
console.log(`Agent task ${task.id} reconciled, published, and completed with canonical Git source.`);
|
|
6673
|
+
}
|
|
6674
|
+
else if (task.kind === "STATIC_GAME") {
|
|
5411
6675
|
await reportTelemetry("DONE");
|
|
5412
6676
|
try {
|
|
5413
6677
|
let appId = refreshed.task.appId;
|
|
@@ -5452,7 +6716,7 @@ async function startWorker(options = {}) {
|
|
|
5452
6716
|
});
|
|
5453
6717
|
const taskClient = (0, apiClient_1.createCliApiClient)({
|
|
5454
6718
|
baseUrl: envConfig.apiBase,
|
|
5455
|
-
|
|
6719
|
+
tokenProvider: ctx.getToken,
|
|
5456
6720
|
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
5457
6721
|
agentTaskToken: assignment.task.token,
|
|
5458
6722
|
agentTaskId: task.id,
|
|
@@ -5470,7 +6734,7 @@ async function startWorker(options = {}) {
|
|
|
5470
6734
|
cdnBase: envConfig.cdnBase,
|
|
5471
6735
|
claimedAppName: refreshed.task.claimedAppName,
|
|
5472
6736
|
devRouterPort: devPort,
|
|
5473
|
-
token,
|
|
6737
|
+
token: ctx.getToken(),
|
|
5474
6738
|
user: me.user,
|
|
5475
6739
|
});
|
|
5476
6740
|
appId = published.appId;
|
|
@@ -5521,9 +6785,9 @@ async function startWorker(options = {}) {
|
|
|
5521
6785
|
kind: "system",
|
|
5522
6786
|
phase: artifactFailure ? "static_game_artifact_failed" : "static_game_publish_failed",
|
|
5523
6787
|
message: artifactFailure
|
|
5524
|
-
?
|
|
5525
|
-
:
|
|
5526
|
-
payload: { error: failureCode
|
|
6788
|
+
? "Static game output did not pass validation."
|
|
6789
|
+
: "Static game publishing failed.",
|
|
6790
|
+
payload: { error: failureCode },
|
|
5527
6791
|
});
|
|
5528
6792
|
retainWorkspace = true;
|
|
5529
6793
|
const terminalReason = artifactFailure
|
|
@@ -5533,7 +6797,6 @@ async function startWorker(options = {}) {
|
|
|
5533
6797
|
code: failureCode,
|
|
5534
6798
|
message: "The static game output did not pass validation.",
|
|
5535
6799
|
retryable: false,
|
|
5536
|
-
rawError: rawMessage,
|
|
5537
6800
|
source: "worker",
|
|
5538
6801
|
}
|
|
5539
6802
|
: {
|
|
@@ -5542,7 +6805,6 @@ async function startWorker(options = {}) {
|
|
|
5542
6805
|
code: failureCode,
|
|
5543
6806
|
message: "The platform failed to publish the static game.",
|
|
5544
6807
|
retryable: true,
|
|
5545
|
-
rawError: rawMessage,
|
|
5546
6808
|
source: "worker",
|
|
5547
6809
|
};
|
|
5548
6810
|
await failTaskWithRetry(client, task.id, {
|
|
@@ -5550,11 +6812,6 @@ async function startWorker(options = {}) {
|
|
|
5550
6812
|
leaseToken,
|
|
5551
6813
|
error: failureCode,
|
|
5552
6814
|
terminalReason,
|
|
5553
|
-
result: {
|
|
5554
|
-
stage: artifactFailure ? "artifact" : "publish",
|
|
5555
|
-
error: rawMessage,
|
|
5556
|
-
agentRunSucceeded: true,
|
|
5557
|
-
},
|
|
5558
6815
|
});
|
|
5559
6816
|
}
|
|
5560
6817
|
}
|
|
@@ -5563,44 +6820,20 @@ async function startWorker(options = {}) {
|
|
|
5563
6820
|
await failUploadedArtifactWithoutTaskDone();
|
|
5564
6821
|
}
|
|
5565
6822
|
else {
|
|
5566
|
-
const
|
|
5567
|
-
let tokenUsageKnown = true;
|
|
5568
|
-
try {
|
|
5569
|
-
tokenUsageKnown = (0, runtime_1.assertWorkerTokenUsageWithinCap)({
|
|
5570
|
-
tokensUsed: agentResult.tokensUsed,
|
|
5571
|
-
tokenCap,
|
|
5572
|
-
});
|
|
5573
|
-
}
|
|
5574
|
-
catch (error) {
|
|
5575
|
-
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5576
|
-
workerKey,
|
|
5577
|
-
leaseToken,
|
|
5578
|
-
kind: "progress",
|
|
5579
|
-
message: `Token usage ${completedAgentResult.tokensUsed} exceeded the worker token cap of ${tokenCap}.`,
|
|
5580
|
-
});
|
|
5581
|
-
await reportTelemetry("FAILED");
|
|
5582
|
-
throw error;
|
|
5583
|
-
}
|
|
5584
|
-
if (!tokenUsageKnown) {
|
|
5585
|
-
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5586
|
-
workerKey,
|
|
5587
|
-
leaseToken,
|
|
5588
|
-
kind: "progress",
|
|
5589
|
-
message: "Token usage parse failed: the agent output did not contain a recognizable total token count.",
|
|
5590
|
-
});
|
|
5591
|
-
}
|
|
6823
|
+
const uploadFailure = readRecordedTaskUploadFailure();
|
|
5592
6824
|
await client.workerCreateAgentTaskEvent(task.id, {
|
|
5593
6825
|
workerKey,
|
|
5594
6826
|
leaseToken,
|
|
5595
6827
|
kind: "progress",
|
|
5596
6828
|
phase: "failed",
|
|
5597
|
-
message: "Agent exited before running playdrop task done",
|
|
6829
|
+
message: uploadFailure?.terminalReason.message ?? "Agent exited before running playdrop task done",
|
|
5598
6830
|
});
|
|
5599
6831
|
retainWorkspace = true;
|
|
5600
6832
|
await failTaskWithRetry(client, task.id, {
|
|
5601
6833
|
workerKey,
|
|
5602
6834
|
leaseToken,
|
|
5603
|
-
error: "agent_exited_without_task_done",
|
|
6835
|
+
error: uploadFailure?.terminalReason.code ?? "agent_exited_without_task_done",
|
|
6836
|
+
...(uploadFailure ? { terminalReason: uploadFailure.terminalReason } : {}),
|
|
5604
6837
|
result: agentRunResult,
|
|
5605
6838
|
});
|
|
5606
6839
|
await reportTelemetry("FAILED");
|
|
@@ -5609,23 +6842,45 @@ async function startWorker(options = {}) {
|
|
|
5609
6842
|
}
|
|
5610
6843
|
catch (error) {
|
|
5611
6844
|
const message = error instanceof Error ? error.message : String(error);
|
|
6845
|
+
if (gameSourceWorkspace) {
|
|
6846
|
+
await (0, game_source_git_1.checkpointFailedGameSourceWorkspace)({ workspace: gameSourceWorkspace, taskId: task.id }).catch((checkpointError) => {
|
|
6847
|
+
console.error(`Failed Git workspace checkpoint did not complete: ${checkpointError instanceof Error ? checkpointError.message : String(checkpointError)}`);
|
|
6848
|
+
});
|
|
6849
|
+
}
|
|
5612
6850
|
if (isAgentTaskNotRunningError(error)) {
|
|
5613
|
-
const refreshed = await fetchTaskDetail(client, target, task.id).catch(() => null);
|
|
6851
|
+
const refreshed = await fetchTaskDetail(client, target, task.id, workerKey).catch(() => null);
|
|
5614
6852
|
retainWorkspace = refreshed?.task.status === "FAILED";
|
|
5615
6853
|
console.error(`Agent task ${task.id} was already finalized${refreshed ? ` as ${refreshed.task.status}` : ""}.`);
|
|
5616
6854
|
}
|
|
5617
6855
|
else {
|
|
5618
6856
|
retainWorkspace = true;
|
|
6857
|
+
const failureCode = normalizeWorkerFailureErrorCode(message);
|
|
6858
|
+
const terminalReason = classifyWorkerSupervisorFailure(message, workerFailureStage);
|
|
5619
6859
|
console.error(`Agent task ${task.id} failed in the worker: ${message}`);
|
|
6860
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
6861
|
+
workerKey,
|
|
6862
|
+
leaseToken,
|
|
6863
|
+
kind: "system",
|
|
6864
|
+
phase: "worker_failed",
|
|
6865
|
+
message: terminalReason.message,
|
|
6866
|
+
pct: null,
|
|
6867
|
+
payload: {
|
|
6868
|
+
stage: workerFailureStage,
|
|
6869
|
+
error: failureCode.slice(0, 160),
|
|
6870
|
+
},
|
|
6871
|
+
}).catch((eventError) => {
|
|
6872
|
+
console.error(`Agent task ${task.id} worker failure event did not record: ${eventError instanceof Error ? eventError.message : String(eventError)}`);
|
|
6873
|
+
});
|
|
5620
6874
|
}
|
|
5621
6875
|
if (!fenced && !isAgentTaskNotRunningError(error)) {
|
|
5622
6876
|
try {
|
|
6877
|
+
const failureCode = normalizeWorkerFailureErrorCode(message);
|
|
6878
|
+
const terminalReason = classifyWorkerSupervisorFailure(message, workerFailureStage);
|
|
5623
6879
|
await failTaskWithRetry(client, task.id, {
|
|
5624
6880
|
workerKey,
|
|
5625
6881
|
leaseToken,
|
|
5626
|
-
error:
|
|
5627
|
-
terminalReason
|
|
5628
|
-
result: { workerError: message },
|
|
6882
|
+
error: failureCode,
|
|
6883
|
+
terminalReason,
|
|
5629
6884
|
});
|
|
5630
6885
|
}
|
|
5631
6886
|
catch (reportError) {
|
|
@@ -5634,7 +6889,7 @@ async function startWorker(options = {}) {
|
|
|
5634
6889
|
finally {
|
|
5635
6890
|
if (!telemetryReported) {
|
|
5636
6891
|
try {
|
|
5637
|
-
await reportTelemetry("FAILED", message);
|
|
6892
|
+
await reportTelemetry("FAILED", undefined, classifyWorkerSupervisorFailure(message, workerFailureStage));
|
|
5638
6893
|
}
|
|
5639
6894
|
catch (telemetryError) {
|
|
5640
6895
|
console.error(`Agent task telemetry failed: ${telemetryError instanceof Error ? telemetryError.message : String(telemetryError)}`);
|
|
@@ -5651,6 +6906,7 @@ async function startWorker(options = {}) {
|
|
|
5651
6906
|
clearInterval(eventDrainTimer);
|
|
5652
6907
|
}
|
|
5653
6908
|
activeTerminators.delete(task.id);
|
|
6909
|
+
await gameServerDevLease?.cleanup();
|
|
5654
6910
|
if (workspaceDir) {
|
|
5655
6911
|
await removeAgentRunCredentials(workspaceDir);
|
|
5656
6912
|
}
|
|
@@ -5660,6 +6916,9 @@ async function startWorker(options = {}) {
|
|
|
5660
6916
|
else if (workspaceDir) {
|
|
5661
6917
|
await (0, promises_1.rm)(workspaceDir, { recursive: true, force: true });
|
|
5662
6918
|
}
|
|
6919
|
+
await (0, game_source_git_1.maintainGameSourceCaches)(resolveWorkerHomeDir()).catch((error) => {
|
|
6920
|
+
console.error(`Game source cache maintenance failed after task ${task.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
6921
|
+
});
|
|
5663
6922
|
}
|
|
5664
6923
|
if (sessionExpired) {
|
|
5665
6924
|
throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
|
|
@@ -5684,6 +6943,7 @@ async function startWorker(options = {}) {
|
|
|
5684
6943
|
npmVersion,
|
|
5685
6944
|
playwright,
|
|
5686
6945
|
maxParallelTasks,
|
|
6946
|
+
requireGenerationRuntime: target === "FIRST_PARTY",
|
|
5687
6947
|
}), capabilities);
|
|
5688
6948
|
lastCapabilityRefreshAt = Date.now();
|
|
5689
6949
|
console.log("Worker refreshed installed agent CLI versions and model catalogs.");
|
|
@@ -5699,7 +6959,7 @@ async function startWorker(options = {}) {
|
|
|
5699
6959
|
});
|
|
5700
6960
|
let claim;
|
|
5701
6961
|
try {
|
|
5702
|
-
claim =
|
|
6962
|
+
claim = normalizeWorkerClaimResponseV5(await client.workerClaimAgentTask(buildWorkerClaimBody({
|
|
5703
6963
|
workerKey,
|
|
5704
6964
|
capabilities,
|
|
5705
6965
|
runningTaskCount: activeTaskRuns.size,
|
|
@@ -5744,10 +7004,29 @@ async function startWorker(options = {}) {
|
|
|
5744
7004
|
continue;
|
|
5745
7005
|
}
|
|
5746
7006
|
activeTaskIds.add(taskId);
|
|
5747
|
-
|
|
7007
|
+
if (claim.assignment.operation === "GAME")
|
|
7008
|
+
activeDevPorts.add(devPort);
|
|
7009
|
+
const run = claim.assignment.operation === "GENERATION"
|
|
7010
|
+
? (0, generation_1.runWorkerGenerationTask)({
|
|
7011
|
+
client,
|
|
7012
|
+
assignment: claim.assignment,
|
|
7013
|
+
workerKey,
|
|
7014
|
+
leaseToken: acknowledgedLease.token,
|
|
7015
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
7016
|
+
})
|
|
7017
|
+
: claim.assignment.operation === "SCREENING"
|
|
7018
|
+
? (0, generation_1.runWorkerScreeningTask)({
|
|
7019
|
+
client,
|
|
7020
|
+
assignment: claim.assignment,
|
|
7021
|
+
workerKey,
|
|
7022
|
+
leaseToken: acknowledgedLease.token,
|
|
7023
|
+
})
|
|
7024
|
+
: claim.assignment.operation === "GAME"
|
|
7025
|
+
? runClaimedTask(claim.assignment, acknowledgedLease.token, devPort)
|
|
7026
|
+
: Promise.reject(new Error("worker_assignment_operation_invalid"));
|
|
5748
7027
|
const taskRun = containWorkerTaskRun({
|
|
5749
7028
|
taskId,
|
|
5750
|
-
run
|
|
7029
|
+
run,
|
|
5751
7030
|
})
|
|
5752
7031
|
.finally(() => {
|
|
5753
7032
|
activeTaskRuns.delete(taskId);
|
|
@@ -5772,6 +7051,9 @@ async function startWorker(options = {}) {
|
|
|
5772
7051
|
if (sessionExpired) {
|
|
5773
7052
|
throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
|
|
5774
7053
|
}
|
|
7054
|
+
if (durableAuthInvalid) {
|
|
7055
|
+
throw new Error(exports.WORKER_SESSION_EXPIRED_MESSAGE);
|
|
7056
|
+
}
|
|
5775
7057
|
if (quiescingForUpdate) {
|
|
5776
7058
|
console.log("Worker quiesced for managed update. Exiting for supervisor restart.");
|
|
5777
7059
|
return;
|
|
@@ -5788,6 +7070,9 @@ async function startWorker(options = {}) {
|
|
|
5788
7070
|
if (presenceTimer) {
|
|
5789
7071
|
clearInterval(presenceTimer);
|
|
5790
7072
|
}
|
|
7073
|
+
if (authRefreshTimer) {
|
|
7074
|
+
clearInterval(authRefreshTimer);
|
|
7075
|
+
}
|
|
5791
7076
|
node_process_1.default.off("SIGINT", signalHandler);
|
|
5792
7077
|
node_process_1.default.off("SIGTERM", signalHandler);
|
|
5793
7078
|
if (options.superviseStdin) {
|
|
@@ -5800,7 +7085,7 @@ async function startWorker(options = {}) {
|
|
|
5800
7085
|
finally {
|
|
5801
7086
|
releaseWorkerSupervisorLock(supervisorLock);
|
|
5802
7087
|
}
|
|
5803
|
-
}, { env: options.env });
|
|
7088
|
+
}, { env: options.env, forceRefresh: true });
|
|
5804
7089
|
}
|
|
5805
7090
|
async function reportTask(options) {
|
|
5806
7091
|
const message = options.message?.trim() || null;
|
|
@@ -5852,11 +7137,11 @@ async function prepareAssetRuntimeTask() {
|
|
|
5852
7137
|
}
|
|
5853
7138
|
const workspaceDir = resolveTaskWorkspaceDir();
|
|
5854
7139
|
const relativeSetupPath = node_path_1.default.join("skills", "make-2d-asset-pack", "scripts", "setup_runtime.py");
|
|
5855
|
-
const
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
if (!setupScript) {
|
|
7140
|
+
const assignmentSetupScript = node_path_1.default.join(workspaceDir, ".playdrop", "task-plugin", relativeSetupPath);
|
|
7141
|
+
const setupScript = isFile(assignmentSetupScript)
|
|
7142
|
+
? assignmentSetupScript
|
|
7143
|
+
: node_path_1.default.join(discoverWorkerProjectRoot(node_process_1.default.cwd()), ".playdrop", "dev-task-plugin", relativeSetupPath);
|
|
7144
|
+
if (!isFile(setupScript)) {
|
|
5860
7145
|
throw new Error("task_asset_runtime_setup_script_missing");
|
|
5861
7146
|
}
|
|
5862
7147
|
const bootstrapPython = node_process_1.default.env.PLAYDROP_ASSET_PACK_BOOTSTRAP_PYTHON?.trim() || "python3";
|
|
@@ -6049,8 +7334,11 @@ async function resolveTaskCommandContext(command, optionsEnv, taskContext) {
|
|
|
6049
7334
|
if (!ctx) {
|
|
6050
7335
|
return null;
|
|
6051
7336
|
}
|
|
7337
|
+
const accessToken = taskContext.target === "FIRST_PARTY" ? taskContext.taskAccessToken?.trim() ?? "" : ctx.token;
|
|
7338
|
+
if (!accessToken)
|
|
7339
|
+
throw new Error("agent_task_access_token_missing");
|
|
6052
7340
|
const taskClientOptions = {
|
|
6053
|
-
token:
|
|
7341
|
+
token: accessToken,
|
|
6054
7342
|
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
6055
7343
|
agentTaskToken: taskContext.taskToken,
|
|
6056
7344
|
agentTaskId: taskContext.taskId,
|
|
@@ -6060,21 +7348,42 @@ async function resolveTaskCommandContext(command, optionsEnv, taskContext) {
|
|
|
6060
7348
|
...taskClientOptions,
|
|
6061
7349
|
baseUrl: ctx.envConfig.apiBase,
|
|
6062
7350
|
});
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
const me = await client.me();
|
|
6068
|
-
if (!me.user) {
|
|
7351
|
+
const user = taskContext.target === "FIRST_PARTY"
|
|
7352
|
+
? readAgentTaskAccessIdentity(accessToken, taskContext)
|
|
7353
|
+
: (await client.me()).user;
|
|
7354
|
+
if (!user)
|
|
6069
7355
|
throw new Error("task_context_user_missing");
|
|
7356
|
+
return { ...ctx, token: accessToken, client, user };
|
|
7357
|
+
}
|
|
7358
|
+
function readAgentTaskAccessIdentity(token, taskContext) {
|
|
7359
|
+
try {
|
|
7360
|
+
const payloadSegment = token.split(".")[1];
|
|
7361
|
+
if (!payloadSegment)
|
|
7362
|
+
throw new Error("missing_payload");
|
|
7363
|
+
const payload = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8"));
|
|
7364
|
+
const id = Number(payload.sub);
|
|
7365
|
+
const username = typeof payload.username === "string" ? payload.username.trim() : "";
|
|
7366
|
+
const expiresAtSeconds = Number(payload.exp);
|
|
7367
|
+
if (payload.tokenKind !== "agent_task_access" ||
|
|
7368
|
+
payload.role !== "CLOUD_WORKER" ||
|
|
7369
|
+
payload.kind !== "AGENT" ||
|
|
7370
|
+
!Number.isInteger(id) ||
|
|
7371
|
+
id <= 0 ||
|
|
7372
|
+
!username ||
|
|
7373
|
+
Number(payload.taskId) !== taskContext.taskId ||
|
|
7374
|
+
Number(payload.taskAttempt) !== taskContext.attempt ||
|
|
7375
|
+
!Number.isFinite(expiresAtSeconds) ||
|
|
7376
|
+
expiresAtSeconds <= Date.now() / 1000) {
|
|
7377
|
+
throw new Error("invalid_claims");
|
|
7378
|
+
}
|
|
7379
|
+
return { id, username, role: "CLOUD_WORKER", kind: "AGENT" };
|
|
7380
|
+
}
|
|
7381
|
+
catch {
|
|
7382
|
+
throw new Error("agent_task_access_token_invalid");
|
|
6070
7383
|
}
|
|
6071
|
-
return { ...ctx, client, aiClient, user: me.user };
|
|
6072
7384
|
}
|
|
6073
7385
|
async function uploadTask(options = {}) {
|
|
6074
7386
|
const taskContext = readTaskContextFile();
|
|
6075
|
-
if (taskContext.kind === "STATIC_GAME") {
|
|
6076
|
-
throw new Error("task_upload_not_allowed_for_static_game");
|
|
6077
|
-
}
|
|
6078
7387
|
if (taskContext.kind === "GAME_REVIEW" || taskContext.kind === "GAME_EVAL") {
|
|
6079
7388
|
throw new Error("task_upload_not_allowed_for_game_review");
|
|
6080
7389
|
}
|
|
@@ -6113,22 +7422,12 @@ async function uploadTask(options = {}) {
|
|
|
6113
7422
|
(0, output_1.printSuccess)(`Task upload preflight passed for ${result.creatorUsername}/${result.appName}. Upload can start.`);
|
|
6114
7423
|
return;
|
|
6115
7424
|
}
|
|
6116
|
-
if (
|
|
6117
|
-
const existingResult = readTaskUploadResultFile(workspaceDir);
|
|
6118
|
-
assertTaskUploadResultMatchesContext({
|
|
6119
|
-
taskContext,
|
|
6120
|
-
uploadResult: existingResult,
|
|
6121
|
-
});
|
|
6122
|
-
(0, output_1.printSuccess)(`Task upload already completed for ${existingResult.creatorUsername}/${existingResult.appName} version ${existingResult.version}. Run "playdrop task done" to close the task.`);
|
|
6123
|
-
return;
|
|
6124
|
-
}
|
|
6125
|
-
await withTaskUploadLock(workspaceDir, async () => {
|
|
7425
|
+
if (taskContext.gameSourceGit) {
|
|
6126
7426
|
const ctx = await resolveTaskCommandContext("task upload", options.env, taskContext);
|
|
6127
|
-
if (!ctx)
|
|
7427
|
+
if (!ctx)
|
|
6128
7428
|
return;
|
|
6129
|
-
}
|
|
6130
7429
|
const projectDir = discoverWorkerProjectRoot(node_process_1.default.cwd());
|
|
6131
|
-
const
|
|
7430
|
+
const result = await (0, upload_1.preflightWorkerAppProject)({
|
|
6132
7431
|
client: ctx.client,
|
|
6133
7432
|
taskId: taskContext.taskId,
|
|
6134
7433
|
taskToken: taskContext.taskToken,
|
|
@@ -6146,6 +7445,63 @@ async function uploadTask(options = {}) {
|
|
|
6146
7445
|
token: ctx.token,
|
|
6147
7446
|
user: ctx.user,
|
|
6148
7447
|
});
|
|
7448
|
+
await (0, promises_1.writeFile)(workerGameSourceReadinessPath(workspaceDir), JSON.stringify({
|
|
7449
|
+
taskId: taskContext.taskId,
|
|
7450
|
+
attempt: taskContext.attempt,
|
|
7451
|
+
projectDir,
|
|
7452
|
+
readyAt: new Date().toISOString(),
|
|
7453
|
+
}, null, 2));
|
|
7454
|
+
for (const warning of result.warnings)
|
|
7455
|
+
console.error(`Preflight warning: ${warning}`);
|
|
7456
|
+
(0, output_1.printSuccess)("Task source is ready. Run \"playdrop task done\" when the work is complete.");
|
|
7457
|
+
return;
|
|
7458
|
+
}
|
|
7459
|
+
if (taskContext.kind === "STATIC_GAME") {
|
|
7460
|
+
throw new Error("task_upload_not_allowed_for_static_game");
|
|
7461
|
+
}
|
|
7462
|
+
if ((0, node_fs_1.existsSync)(workerTaskUploadResultPath(workspaceDir))) {
|
|
7463
|
+
const existingResult = readTaskUploadResultFile(workspaceDir);
|
|
7464
|
+
assertTaskUploadResultMatchesContext({
|
|
7465
|
+
taskContext,
|
|
7466
|
+
uploadResult: existingResult,
|
|
7467
|
+
});
|
|
7468
|
+
(0, output_1.printSuccess)(`Task upload already completed for ${existingResult.creatorUsername}/${existingResult.appName} version ${existingResult.version}. Run "playdrop task done" to close the task.`);
|
|
7469
|
+
return;
|
|
7470
|
+
}
|
|
7471
|
+
await withTaskUploadLock(workspaceDir, async () => {
|
|
7472
|
+
const ctx = await resolveTaskCommandContext("task upload", options.env, taskContext);
|
|
7473
|
+
if (!ctx) {
|
|
7474
|
+
return;
|
|
7475
|
+
}
|
|
7476
|
+
const projectDir = discoverWorkerProjectRoot(node_process_1.default.cwd());
|
|
7477
|
+
await (0, promises_1.rm)(workerTaskUploadFailurePath(workspaceDir), { force: true });
|
|
7478
|
+
let published;
|
|
7479
|
+
try {
|
|
7480
|
+
published = await (0, upload_1.publishWorkerAppProject)({
|
|
7481
|
+
client: ctx.client,
|
|
7482
|
+
taskId: taskContext.taskId,
|
|
7483
|
+
taskToken: taskContext.taskToken,
|
|
7484
|
+
kind: uploadKind,
|
|
7485
|
+
executionTarget: taskContext.target,
|
|
7486
|
+
expectedAppName: taskContext.outputAppName ?? undefined,
|
|
7487
|
+
expectedDisplayName: taskContext.outputDisplayName,
|
|
7488
|
+
expectedSubtitle: taskContext.outputSubtitle,
|
|
7489
|
+
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
7490
|
+
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
7491
|
+
projectDir,
|
|
7492
|
+
creatorUsername: taskContext.creatorUsername,
|
|
7493
|
+
apiBase: ctx.envConfig.apiBase,
|
|
7494
|
+
webBase: ctx.envConfig.webBase ?? null,
|
|
7495
|
+
token: ctx.token,
|
|
7496
|
+
user: ctx.user,
|
|
7497
|
+
});
|
|
7498
|
+
}
|
|
7499
|
+
catch (error) {
|
|
7500
|
+
await recordTaskUploadFailure({ workspaceDir, taskContext, error }).catch((recordError) => {
|
|
7501
|
+
console.error(`Could not record task upload failure: ${recordError instanceof Error ? recordError.message : String(recordError)}`);
|
|
7502
|
+
});
|
|
7503
|
+
throw error;
|
|
7504
|
+
}
|
|
6149
7505
|
const result = {
|
|
6150
7506
|
taskId: taskContext.taskId,
|
|
6151
7507
|
appId: published.appId,
|
|
@@ -6216,6 +7572,7 @@ async function claimSlugTask(options) {
|
|
|
6216
7572
|
async function completeTask(options) {
|
|
6217
7573
|
const taskContext = readTaskContextFile();
|
|
6218
7574
|
const workspaceDir = resolveTaskWorkspaceDir();
|
|
7575
|
+
const commandInputDir = taskContext.gameSourceGit ? node_process_1.default.cwd() : workspaceDir;
|
|
6219
7576
|
if (taskContext.kind === "GAME_REVIEW" || taskContext.kind === "GAME_EVAL") {
|
|
6220
7577
|
throw new Error("task_done_not_allowed_for_game_review");
|
|
6221
7578
|
}
|
|
@@ -6226,11 +7583,38 @@ async function completeTask(options) {
|
|
|
6226
7583
|
if (summary.length > 200) {
|
|
6227
7584
|
throw new Error("task_done_summary_too_long:max_200_characters");
|
|
6228
7585
|
}
|
|
7586
|
+
if (taskContext.gameSourceGit) {
|
|
7587
|
+
if (!(0, node_fs_1.existsSync)(workerGameSourceReadinessPath(workspaceDir)))
|
|
7588
|
+
throw new Error("task_done_source_not_ready");
|
|
7589
|
+
const ctx = await resolveTaskCommandContext("task done", options.env, taskContext);
|
|
7590
|
+
if (!ctx)
|
|
7591
|
+
return;
|
|
7592
|
+
const nextSteps = await readTaskNextStepsFileForCommand({
|
|
7593
|
+
filePath: options.nextSteps,
|
|
7594
|
+
workspaceDir: commandInputDir,
|
|
7595
|
+
command: "task done",
|
|
7596
|
+
ctx,
|
|
7597
|
+
taskContext,
|
|
7598
|
+
});
|
|
7599
|
+
if (nextSteps === NEXT_STEPS_READ_FAILED)
|
|
7600
|
+
return;
|
|
7601
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile, commandInputDir);
|
|
7602
|
+
const intent = {
|
|
7603
|
+
taskId: taskContext.taskId,
|
|
7604
|
+
attempt: taskContext.attempt,
|
|
7605
|
+
summary,
|
|
7606
|
+
...(nextSteps !== undefined ? { nextSteps } : {}),
|
|
7607
|
+
...(platformFeedback ? { platformFeedback } : {}),
|
|
7608
|
+
};
|
|
7609
|
+
await (0, promises_1.writeFile)(workerGameSourceCompletionPath(workspaceDir), `${JSON.stringify(intent, null, 2)}\n`, "utf8");
|
|
7610
|
+
(0, output_1.printSuccess)("Task completion recorded. The worker will reconcile and publish the source after the agent exits.");
|
|
7611
|
+
return;
|
|
7612
|
+
}
|
|
7613
|
+
const uploadResult = readTaskUploadResultFile();
|
|
6229
7614
|
const ctx = await resolveTaskCommandContext("task done", options.env, taskContext);
|
|
6230
7615
|
if (!ctx) {
|
|
6231
7616
|
return;
|
|
6232
7617
|
}
|
|
6233
|
-
const uploadResult = readTaskUploadResultFile();
|
|
6234
7618
|
const appId = uploadResult.appId;
|
|
6235
7619
|
const appVersionId = uploadResult.appVersionId;
|
|
6236
7620
|
if (!appId || !appVersionId) {
|
|
@@ -6275,12 +7659,6 @@ async function completeTask(options) {
|
|
|
6275
7659
|
taskToken: taskContext.taskToken,
|
|
6276
7660
|
error: "remix_relation_missing",
|
|
6277
7661
|
terminalReason: (0, types_1.classifyAgentTaskTerminalReason)("upload_validation:remix_relation_missing", "agent"),
|
|
6278
|
-
result: {
|
|
6279
|
-
appId: uploadResult.appId,
|
|
6280
|
-
appVersionId: uploadResult.appVersionId,
|
|
6281
|
-
appName: uploadResult.appName,
|
|
6282
|
-
version: uploadResult.version,
|
|
6283
|
-
},
|
|
6284
7662
|
...(platformFeedback ? { platformFeedback } : {}),
|
|
6285
7663
|
});
|
|
6286
7664
|
}
|
|
@@ -6543,11 +7921,6 @@ async function instrumentErrorTask(options) {
|
|
|
6543
7921
|
taskToken: taskContext.taskToken,
|
|
6544
7922
|
error: `instrument_error:${reason}`,
|
|
6545
7923
|
terminalReason: (0, types_1.classifyAgentTaskTerminalReason)(`instrument_error:${reason}`, "instrument"),
|
|
6546
|
-
result: {
|
|
6547
|
-
outcome: "instrument_error",
|
|
6548
|
-
reason,
|
|
6549
|
-
reportedBy: "agent",
|
|
6550
|
-
},
|
|
6551
7924
|
});
|
|
6552
7925
|
(0, output_1.printSuccess)(`Instrument error submitted (${reason}). This was the final operation; stop immediately.`);
|
|
6553
7926
|
}
|
|
@@ -6570,22 +7943,26 @@ async function failTask(options) {
|
|
|
6570
7943
|
}
|
|
6571
7944
|
const taskContext = readTaskContextFile();
|
|
6572
7945
|
const workspaceDir = resolveTaskWorkspaceDir();
|
|
7946
|
+
const uploadFailure = readTaskUploadFailureFile(workspaceDir);
|
|
7947
|
+
if (uploadFailure && (uploadFailure.taskId !== taskContext.taskId || uploadFailure.attempt !== taskContext.attempt)) {
|
|
7948
|
+
throw new Error("task_upload_failure_context_mismatch");
|
|
7949
|
+
}
|
|
6573
7950
|
const ctx = await resolveTaskCommandContext("task fail", options.env, taskContext);
|
|
6574
7951
|
if (!ctx) {
|
|
6575
7952
|
return;
|
|
6576
7953
|
}
|
|
6577
7954
|
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile, workspaceDir);
|
|
6578
|
-
const
|
|
7955
|
+
const classifiedMessage = (0, types_1.classifyAgentTaskTerminalReason)(message, "agent");
|
|
7956
|
+
const terminalReason = uploadFailure?.terminalReason ?? {
|
|
7957
|
+
...classifiedMessage,
|
|
7958
|
+
message: "The agent reported that it could not complete the task.",
|
|
7959
|
+
retryable: options.retryableWithSameInput === true,
|
|
7960
|
+
source: "agent",
|
|
7961
|
+
};
|
|
6579
7962
|
await ctx.client.workerFailAgentTask(taskContext.taskId, {
|
|
6580
7963
|
taskToken: taskContext.taskToken,
|
|
6581
|
-
error:
|
|
6582
|
-
terminalReason
|
|
6583
|
-
...terminalReason,
|
|
6584
|
-
message,
|
|
6585
|
-
retryable: options.retryableWithSameInput === true,
|
|
6586
|
-
source: "agent",
|
|
6587
|
-
},
|
|
6588
|
-
result: { failedBy: "agent" },
|
|
7964
|
+
error: uploadFailure?.terminalReason.code ?? terminalReason.code,
|
|
7965
|
+
terminalReason,
|
|
6589
7966
|
...(platformFeedback ? { platformFeedback } : {}),
|
|
6590
7967
|
});
|
|
6591
7968
|
(0, output_1.printSuccess)("Task marked failed. This was the final operation; send one short final status sentence and stop immediately.");
|