@playdrop/playdrop-cli 0.17.1 → 0.17.4
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 +3 -3
- package/dist/apps/build.js +24 -4
- package/dist/apps/upload.js +31 -14
- package/dist/commands/check.js +2 -0
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +4 -3
- package/dist/commands/worker/game-source-commands.d.ts +18 -0
- package/dist/commands/worker/game-source-commands.js +182 -0
- package/dist/commands/worker/game-source-git.d.ts +1 -0
- package/dist/commands/worker/game-source-git.js +44 -16
- package/dist/commands/worker/generation.d.ts +1 -0
- package/dist/commands/worker/generation.js +36 -19
- package/dist/commands/worker.js +353 -248
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.js +7 -2
- package/node_modules/@playdrop/config/client-meta.json +3 -3
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/node_modules/@playwright/mcp/node_modules/fsevents/LICENSE +0 -22
- package/node_modules/@playwright/mcp/node_modules/fsevents/README.md +0 -83
- package/node_modules/@playwright/mcp/node_modules/fsevents/fsevents.d.ts +0 -46
- package/node_modules/@playwright/mcp/node_modules/fsevents/fsevents.js +0 -82
- package/node_modules/@playwright/mcp/node_modules/fsevents/fsevents.node +0 -0
- package/node_modules/@playwright/mcp/node_modules/fsevents/package.json +0 -62
package/dist/commands/worker.js
CHANGED
|
@@ -150,6 +150,7 @@ const provider_telemetry_1 = require("./worker/provider-telemetry");
|
|
|
150
150
|
const archive_staging_1 = require("./worker/archive-staging");
|
|
151
151
|
const game_server_infra_1 = require("./worker/game-server-infra");
|
|
152
152
|
const game_source_git_1 = require("./worker/game-source-git");
|
|
153
|
+
const game_source_commands_1 = require("./worker/game-source-commands");
|
|
153
154
|
const generation_1 = require("./worker/generation");
|
|
154
155
|
const e2e_fixtures_1 = require("./worker/e2e-fixtures");
|
|
155
156
|
var archive_staging_2 = require("./worker/archive-staging");
|
|
@@ -1241,9 +1242,6 @@ function workerTaskUploadFailurePath(workspaceDir) {
|
|
|
1241
1242
|
function workerTaskMaterialsPath(workspaceDir) {
|
|
1242
1243
|
return node_path_1.default.join(workspaceDir, ".playdrop", "task-materials.json");
|
|
1243
1244
|
}
|
|
1244
|
-
function workerGameSourceReadinessPath(workspaceDir) {
|
|
1245
|
-
return node_path_1.default.join(workspaceDir, ".playdrop", "game-source-ready.json");
|
|
1246
|
-
}
|
|
1247
1245
|
function workerGameSourceCompletionPath(workspaceDir) {
|
|
1248
1246
|
return node_path_1.default.join(workspaceDir, ".playdrop", "game-source-completion.json");
|
|
1249
1247
|
}
|
|
@@ -5636,6 +5634,11 @@ async function startWorker(options = {}) {
|
|
|
5636
5634
|
let telemetryReported = false;
|
|
5637
5635
|
let gameServerDevLease = null;
|
|
5638
5636
|
let gameSourceWorkspace = null;
|
|
5637
|
+
let gameSourceCommandServer = null;
|
|
5638
|
+
let gameSourcePublication = null;
|
|
5639
|
+
let previousGameSourceCommitSha;
|
|
5640
|
+
let preparedGameSourceProposal = null;
|
|
5641
|
+
let completedGameSourceGeneration = null;
|
|
5639
5642
|
let transcriptSpoolPath = null;
|
|
5640
5643
|
let transcriptSpoolSequence = 0;
|
|
5641
5644
|
let heartbeatTransientFailures = 0;
|
|
@@ -5643,6 +5646,8 @@ async function startWorker(options = {}) {
|
|
|
5643
5646
|
let automaticContinuationAttempted = false;
|
|
5644
5647
|
let workerFailureStage = "setup";
|
|
5645
5648
|
const sendTaskHeartbeat = async () => {
|
|
5649
|
+
if (completedGameSourceGeneration)
|
|
5650
|
+
return;
|
|
5646
5651
|
try {
|
|
5647
5652
|
const heartbeat = await client.workerHeartbeatAgentTask(task.id, { workerKey, leaseToken });
|
|
5648
5653
|
heartbeatTransientFailures = 0;
|
|
@@ -5708,6 +5713,8 @@ async function startWorker(options = {}) {
|
|
|
5708
5713
|
startedAt: agentStartedAt ?? completedAt,
|
|
5709
5714
|
completedAt,
|
|
5710
5715
|
...(terminalReason ? { terminalReason } : {}),
|
|
5716
|
+
}).catch((error) => {
|
|
5717
|
+
console.error(`Agent task ${task.id} telemetry could not be recorded: ${error instanceof Error ? error.message : String(error)}`);
|
|
5711
5718
|
});
|
|
5712
5719
|
telemetryReported = true;
|
|
5713
5720
|
};
|
|
@@ -5803,6 +5810,255 @@ async function startWorker(options = {}) {
|
|
|
5803
5810
|
});
|
|
5804
5811
|
await reportTelemetry("FAILED", undefined, terminalReason);
|
|
5805
5812
|
};
|
|
5813
|
+
// The supervisor keeps the lease, but both commands wait for the real
|
|
5814
|
+
// operation while the agent can still read errors, repair, and retry.
|
|
5815
|
+
const publishGameSourceForAgent = async () => {
|
|
5816
|
+
if (gameSourcePublication)
|
|
5817
|
+
return gameSourcePublication.upload;
|
|
5818
|
+
if (fenced || shuttingDown)
|
|
5819
|
+
throw new Error("agent_task_lease_invalid");
|
|
5820
|
+
if (!gameSourceWorkspace || !workspaceDir || !agentWorkspaceDir) {
|
|
5821
|
+
throw new Error("game_source_workspace_missing");
|
|
5822
|
+
}
|
|
5823
|
+
const currentTask = await fetchTaskDetail(client, target, task.id, workerKey);
|
|
5824
|
+
if (currentTask.task.status !== "RUNNING")
|
|
5825
|
+
throw new Error("agent_task_not_running");
|
|
5826
|
+
if (task.kind === "STATIC_GAME" && !(0, node_fs_1.existsSync)(node_path_1.default.join(agentWorkspaceDir, "catalogue.json"))) {
|
|
5827
|
+
const primarySurface = resolveWorkerInstrumentSurface(taskContext);
|
|
5828
|
+
if (!primarySurface)
|
|
5829
|
+
throw new Error("static_game_artifact_primary_surface_missing");
|
|
5830
|
+
if (!envConfig.cdnBase)
|
|
5831
|
+
throw new Error("static_game_publish_cdn_base_missing");
|
|
5832
|
+
const preparedStatic = await (0, staticHtml_1.prepareStaticHtmlProject)({
|
|
5833
|
+
projectDir: agentWorkspaceDir,
|
|
5834
|
+
taskId: task.id,
|
|
5835
|
+
appName: currentTask.task.claimedAppName ?? undefined,
|
|
5836
|
+
primarySurface,
|
|
5837
|
+
cdnBase: envConfig.cdnBase,
|
|
5838
|
+
});
|
|
5839
|
+
try {
|
|
5840
|
+
const sourceTask = preparedStatic.sourceTask;
|
|
5841
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(agentWorkspaceDir, "catalogue.json"), `${JSON.stringify({
|
|
5842
|
+
apps: [{
|
|
5843
|
+
name: sourceTask.name,
|
|
5844
|
+
version: sourceTask.version,
|
|
5845
|
+
displayName: sourceTask.displayName,
|
|
5846
|
+
subtitle: sourceTask.subtitle,
|
|
5847
|
+
description: sourceTask.description,
|
|
5848
|
+
type: "GAME",
|
|
5849
|
+
authMode: "NONE",
|
|
5850
|
+
controllerMode: "UNSUPPORTED",
|
|
5851
|
+
previewable: true,
|
|
5852
|
+
file: "index.html",
|
|
5853
|
+
primarySurface,
|
|
5854
|
+
surfaceTargets: {
|
|
5855
|
+
desktop: sourceTask.surfaceTargets.includes("DESKTOP"),
|
|
5856
|
+
mobileLandscape: sourceTask.surfaceTargets.includes("MOBILE_LANDSCAPE"),
|
|
5857
|
+
mobilePortrait: sourceTask.surfaceTargets.includes("MOBILE_PORTRAIT"),
|
|
5858
|
+
},
|
|
5859
|
+
releaseNotes: sourceTask.releaseNotes,
|
|
5860
|
+
hostingMode: "HOSTED",
|
|
5861
|
+
license: "CLOSED",
|
|
5862
|
+
}],
|
|
5863
|
+
}, null, 2)}\n`, "utf8");
|
|
5864
|
+
}
|
|
5865
|
+
finally {
|
|
5866
|
+
await preparedStatic.cleanup();
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
const outputProjectDir = discoverWorkerProjectRoot(agentWorkspaceDir);
|
|
5870
|
+
if (node_path_1.default.resolve(outputProjectDir) !== node_path_1.default.resolve(gameSourceWorkspace.projectDir)) {
|
|
5871
|
+
throw new Error("game_source_output_must_be_repository_root");
|
|
5872
|
+
}
|
|
5873
|
+
workerFailureStage = "game_source_reconcile";
|
|
5874
|
+
const reconciled = await (0, game_source_git_1.reconcileGameSourceWorkspace)({
|
|
5875
|
+
workspace: gameSourceWorkspace,
|
|
5876
|
+
taskId: task.id,
|
|
5877
|
+
summary: `Prepared ${taskContext.outputAppName ?? "game"} for publication`,
|
|
5878
|
+
previousCommitSha: previousGameSourceCommitSha,
|
|
5879
|
+
});
|
|
5880
|
+
previousGameSourceCommitSha = reconciled.commitSha;
|
|
5881
|
+
const taskClient = (0, apiClient_1.createCliApiClient)({
|
|
5882
|
+
baseUrl: envConfig.apiBase,
|
|
5883
|
+
tokenProvider: ctx.getToken,
|
|
5884
|
+
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
5885
|
+
agentTaskToken: assignment.task.token,
|
|
5886
|
+
agentTaskId: task.id,
|
|
5887
|
+
agentTaskAttempt: taskContext.attempt,
|
|
5888
|
+
});
|
|
5889
|
+
let published;
|
|
5890
|
+
let finalizedGameSource = null;
|
|
5891
|
+
let preparedGameSource = null;
|
|
5892
|
+
const prepareGitTransition = async (input) => {
|
|
5893
|
+
const versionParts = input.version.split(".").map((part) => Number(part));
|
|
5894
|
+
if (versionParts.length !== 3 ||
|
|
5895
|
+
versionParts.some((part) => !Number.isSafeInteger(part) || part < 0)) {
|
|
5896
|
+
throw new Error("game_source_version_invalid");
|
|
5897
|
+
}
|
|
5898
|
+
// A finalize response can be lost after the version is saved. An
|
|
5899
|
+
// unchanged retry must keep that exact source transition, not a
|
|
5900
|
+
// new timestamped commit or bundle. This cache is attempt-local.
|
|
5901
|
+
if (preparedGameSourceProposal?.appId === input.appId &&
|
|
5902
|
+
preparedGameSourceProposal.version === input.version &&
|
|
5903
|
+
preparedGameSourceProposal.finalized.commitSha === reconciled.commitSha) {
|
|
5904
|
+
finalizedGameSource = preparedGameSourceProposal.finalized;
|
|
5905
|
+
}
|
|
5906
|
+
else {
|
|
5907
|
+
finalizedGameSource = await (0, game_source_git_1.finalizeGameSourceRepository)({
|
|
5908
|
+
reconciled,
|
|
5909
|
+
appVersionId: 0,
|
|
5910
|
+
version: { major: versionParts[0], minor: versionParts[1], patch: versionParts[2] },
|
|
5911
|
+
});
|
|
5912
|
+
preparedGameSourceProposal = {
|
|
5913
|
+
appId: input.appId,
|
|
5914
|
+
version: input.version,
|
|
5915
|
+
finalized: finalizedGameSource,
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
// Recheck ownership and generation for this upload session, even
|
|
5919
|
+
// when the immutable bundle is reused after an aborted session.
|
|
5920
|
+
preparedGameSource = await (0, game_source_git_1.uploadFinalizedGameSource)({
|
|
5921
|
+
client,
|
|
5922
|
+
taskId: task.id,
|
|
5923
|
+
attempt: taskContext.attempt,
|
|
5924
|
+
workerKey,
|
|
5925
|
+
leaseToken,
|
|
5926
|
+
appId: input.appId,
|
|
5927
|
+
appUploadSessionId: input.sessionId,
|
|
5928
|
+
source: finalizedGameSource,
|
|
5929
|
+
});
|
|
5930
|
+
return {
|
|
5931
|
+
commitSha: preparedGameSource.commitSha,
|
|
5932
|
+
headSha: preparedGameSource.headSha,
|
|
5933
|
+
baseGeneration: preparedGameSource.baseGeneration,
|
|
5934
|
+
bundleStorageKey: preparedGameSource.bundleStorageKey,
|
|
5935
|
+
bundleSha256: preparedGameSource.bundleSha256,
|
|
5936
|
+
bundleSizeBytes: preparedGameSource.bundleBytes,
|
|
5937
|
+
};
|
|
5938
|
+
};
|
|
5939
|
+
workerFailureStage = "game_publish";
|
|
5940
|
+
if (task.kind === "STATIC_GAME") {
|
|
5941
|
+
const primarySurface = resolveWorkerInstrumentSurface(taskContext);
|
|
5942
|
+
if (!primarySurface)
|
|
5943
|
+
throw new Error("static_game_artifact_primary_surface_missing");
|
|
5944
|
+
if (!envConfig.cdnBase)
|
|
5945
|
+
throw new Error("static_game_publish_cdn_base_missing");
|
|
5946
|
+
const result = await (0, upload_1.publishStaticHtmlProject)({
|
|
5947
|
+
client: taskClient,
|
|
5948
|
+
taskId: task.id,
|
|
5949
|
+
taskToken: assignment.task.token,
|
|
5950
|
+
projectDir: agentWorkspaceDir,
|
|
5951
|
+
creatorUsername: taskContext.creatorUsername,
|
|
5952
|
+
primarySurface,
|
|
5953
|
+
apiBase: envConfig.apiBase,
|
|
5954
|
+
webBase: envConfig.webBase ?? null,
|
|
5955
|
+
cdnBase: envConfig.cdnBase,
|
|
5956
|
+
claimedAppName: currentTask.task.claimedAppName,
|
|
5957
|
+
devRouterPort: devPort,
|
|
5958
|
+
token: ctx.getToken(),
|
|
5959
|
+
user: me.user,
|
|
5960
|
+
prepareGitTransition,
|
|
5961
|
+
});
|
|
5962
|
+
published = result;
|
|
5963
|
+
}
|
|
5964
|
+
else {
|
|
5965
|
+
const fixtureKind = (0, e2e_fixtures_1.detectPlaydropE2EFixture)(assignment.request.prompt);
|
|
5966
|
+
const result = await (0, upload_1.publishWorkerAppProject)({
|
|
5967
|
+
client: taskClient,
|
|
5968
|
+
taskId: task.id,
|
|
5969
|
+
taskToken: assignment.task.token,
|
|
5970
|
+
kind: task.kind,
|
|
5971
|
+
executionTarget: task.executionTarget,
|
|
5972
|
+
expectedAppName: taskContext.outputAppName ?? undefined,
|
|
5973
|
+
expectedDisplayName: taskContext.outputDisplayName,
|
|
5974
|
+
expectedSubtitle: taskContext.outputSubtitle,
|
|
5975
|
+
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
5976
|
+
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
5977
|
+
projectDir: agentWorkspaceDir,
|
|
5978
|
+
creatorUsername: taskContext.creatorUsername,
|
|
5979
|
+
apiBase: envConfig.apiBase,
|
|
5980
|
+
webBase: envConfig.webBase ?? null,
|
|
5981
|
+
token: ctx.getToken(),
|
|
5982
|
+
user: me.user,
|
|
5983
|
+
devRouterPort: devPort,
|
|
5984
|
+
deterministicE2EFixture: fixtureKind === task.kind,
|
|
5985
|
+
prepareGitTransition,
|
|
5986
|
+
});
|
|
5987
|
+
published = result;
|
|
5988
|
+
}
|
|
5989
|
+
if (!finalizedGameSource || !preparedGameSource) {
|
|
5990
|
+
throw new Error("game_source_transition_not_prepared");
|
|
5991
|
+
}
|
|
5992
|
+
const finalized = finalizedGameSource;
|
|
5993
|
+
const gameSource = preparedGameSource;
|
|
5994
|
+
if (!published.versionNodeId)
|
|
5995
|
+
throw new Error("game_source_version_node_missing");
|
|
5996
|
+
const upload = {
|
|
5997
|
+
taskId: task.id,
|
|
5998
|
+
appId: published.appId,
|
|
5999
|
+
appVersionId: published.appVersionId,
|
|
6000
|
+
appName: published.appName,
|
|
6001
|
+
version: published.version,
|
|
6002
|
+
versionNodeId: published.versionNodeId,
|
|
6003
|
+
creatorUsername: taskContext.creatorUsername,
|
|
6004
|
+
};
|
|
6005
|
+
gameSourcePublication = { upload, source: gameSource, finalized };
|
|
6006
|
+
await (0, promises_1.writeFile)(workerTaskUploadResultPath(workspaceDir), JSON.stringify(upload, null, 2), "utf8");
|
|
6007
|
+
await (0, promises_1.rm)(workerTaskUploadFailurePath(workspaceDir), { force: true });
|
|
6008
|
+
for (const warning of published.warnings)
|
|
6009
|
+
console.error(`Upload warning: ${warning}`);
|
|
6010
|
+
return upload;
|
|
6011
|
+
};
|
|
6012
|
+
const completeGameSourceForAgent = async (intent) => {
|
|
6013
|
+
if (completedGameSourceGeneration)
|
|
6014
|
+
return;
|
|
6015
|
+
if (fenced || shuttingDown)
|
|
6016
|
+
throw new Error("agent_task_lease_invalid");
|
|
6017
|
+
if (!gameSourcePublication)
|
|
6018
|
+
throw new Error("task_done_missing_upload_result: Run playdrop task upload successfully first.");
|
|
6019
|
+
if (intent.taskId !== task.id || intent.attempt !== taskContext.attempt ||
|
|
6020
|
+
typeof intent.summary !== "string" || !intent.summary.trim() ||
|
|
6021
|
+
intent.summary.length > 200 || /[\r\n]/.test(intent.summary)) {
|
|
6022
|
+
throw new Error("game_source_completion_intent_invalid");
|
|
6023
|
+
}
|
|
6024
|
+
const { upload: published, source: gameSource, finalized } = gameSourcePublication;
|
|
6025
|
+
workerFailureStage = "task_complete";
|
|
6026
|
+
const completion = await completeWorkerAgentTaskWithRetry({
|
|
6027
|
+
client,
|
|
6028
|
+
taskId: task.id,
|
|
6029
|
+
body: {
|
|
6030
|
+
workerKey,
|
|
6031
|
+
leaseToken,
|
|
6032
|
+
appId: published.appId,
|
|
6033
|
+
appVersionId: published.appVersionId,
|
|
6034
|
+
result: {
|
|
6035
|
+
appId: published.appId,
|
|
6036
|
+
appVersionId: published.appVersionId,
|
|
6037
|
+
appName: published.appName,
|
|
6038
|
+
version: published.version,
|
|
6039
|
+
versionNodeId: published.versionNodeId,
|
|
6040
|
+
completedBy: "worker_game_source_git",
|
|
6041
|
+
},
|
|
6042
|
+
summary: intent.summary,
|
|
6043
|
+
...(intent.nextSteps ? { nextSteps: intent.nextSteps } : {}),
|
|
6044
|
+
...(intent.platformFeedback ? { platformFeedback: intent.platformFeedback } : {}),
|
|
6045
|
+
...(agentResult ? { tokensUsed: agentResult.tokensUsed } : {}),
|
|
6046
|
+
gameSource,
|
|
6047
|
+
},
|
|
6048
|
+
});
|
|
6049
|
+
if (!completion.gameSource)
|
|
6050
|
+
throw new Error("game_source_completion_metadata_missing");
|
|
6051
|
+
completedGameSourceGeneration = completion.gameSource.repoGeneration;
|
|
6052
|
+
(0, game_source_git_1.emitGameSourceWorkerEvent)("playdrop.game_source.completed", {
|
|
6053
|
+
taskId: task.id,
|
|
6054
|
+
appId: published.appId,
|
|
6055
|
+
repoGeneration: completion.gameSource.repoGeneration,
|
|
6056
|
+
bundleBytes: finalized.bundle.length,
|
|
6057
|
+
materialCount: finalized.materials.length,
|
|
6058
|
+
outcome: "DONE",
|
|
6059
|
+
});
|
|
6060
|
+
retainWorkspace = false;
|
|
6061
|
+
};
|
|
5806
6062
|
try {
|
|
5807
6063
|
await sendTaskHeartbeat();
|
|
5808
6064
|
if (sessionExpired) {
|
|
@@ -5896,13 +6152,18 @@ async function startWorker(options = {}) {
|
|
|
5896
6152
|
devPort,
|
|
5897
6153
|
gameSourceGit: Boolean(assignment.gameSource),
|
|
5898
6154
|
});
|
|
5899
|
-
const handleEventDrainFailure = (error) => {
|
|
6155
|
+
const handleEventDrainFailure = async (error) => {
|
|
6156
|
+
if (completedGameSourceGeneration)
|
|
6157
|
+
return;
|
|
5900
6158
|
const classification = classifyWorkerEventDrainError(error);
|
|
5901
6159
|
if (classification === "session_expired") {
|
|
5902
6160
|
handleSessionExpiry();
|
|
5903
6161
|
return;
|
|
5904
6162
|
}
|
|
5905
6163
|
if (classification === "lease_invalid") {
|
|
6164
|
+
const current = await fetchTaskDetail(client, target, task.id, workerKey).catch(() => null);
|
|
6165
|
+
if (current?.task.status === "DONE")
|
|
6166
|
+
return;
|
|
5906
6167
|
fenced = true;
|
|
5907
6168
|
activeTerminators.get(task.id)?.();
|
|
5908
6169
|
return;
|
|
@@ -5910,7 +6171,10 @@ async function startWorker(options = {}) {
|
|
|
5910
6171
|
console.error(`Agent task event drain failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
5911
6172
|
};
|
|
5912
6173
|
const queueEventDrain = () => {
|
|
5913
|
-
|
|
6174
|
+
const drain = () => completedGameSourceGeneration
|
|
6175
|
+
? Promise.resolve()
|
|
6176
|
+
: drainWorkerEventQueue({ eventDir, client, taskId: task.id, workerKey, leaseToken });
|
|
6177
|
+
eventDrainPromise = eventDrainPromise.then(drain, drain);
|
|
5914
6178
|
return eventDrainPromise;
|
|
5915
6179
|
};
|
|
5916
6180
|
const appendObservedSkillPathsFromTranscript = (chunks) => {
|
|
@@ -6041,7 +6305,7 @@ async function startWorker(options = {}) {
|
|
|
6041
6305
|
}
|
|
6042
6306
|
eventDrainTimer = setInterval(() => {
|
|
6043
6307
|
queueEventDrain().catch((error) => {
|
|
6044
|
-
handleEventDrainFailure(error);
|
|
6308
|
+
return handleEventDrainFailure(error);
|
|
6045
6309
|
});
|
|
6046
6310
|
}, 1000);
|
|
6047
6311
|
const gameCreationTask = usesCreatorRecovery(task.kind);
|
|
@@ -6078,7 +6342,7 @@ async function startWorker(options = {}) {
|
|
|
6078
6342
|
try {
|
|
6079
6343
|
enqueueWorkerEvent(eventDir, initialProgress);
|
|
6080
6344
|
queueEventDrain().catch((error) => {
|
|
6081
|
-
handleEventDrainFailure(error);
|
|
6345
|
+
return handleEventDrainFailure(error);
|
|
6082
6346
|
});
|
|
6083
6347
|
}
|
|
6084
6348
|
catch (error) {
|
|
@@ -6223,6 +6487,31 @@ async function startWorker(options = {}) {
|
|
|
6223
6487
|
message: "Resumed the retained game workspace with a focused creation recovery window.",
|
|
6224
6488
|
});
|
|
6225
6489
|
}
|
|
6490
|
+
if (assignment.gameSource && task.kind !== "STATIC_GAME") {
|
|
6491
|
+
gameSourceCommandServer = await (0, game_source_commands_1.startWorkerGameSourceCommandServer)({
|
|
6492
|
+
workspaceDir,
|
|
6493
|
+
taskId: task.id,
|
|
6494
|
+
attempt: taskContext.attempt,
|
|
6495
|
+
onCommand: async (request) => {
|
|
6496
|
+
try {
|
|
6497
|
+
if (request.command === "upload")
|
|
6498
|
+
return await publishGameSourceForAgent();
|
|
6499
|
+
if (!request.payload || typeof request.payload !== "object") {
|
|
6500
|
+
throw new Error("game_source_completion_intent_invalid");
|
|
6501
|
+
}
|
|
6502
|
+
await completeGameSourceForAgent(request.payload);
|
|
6503
|
+
return { completed: true };
|
|
6504
|
+
}
|
|
6505
|
+
catch (error) {
|
|
6506
|
+
await recordTaskUploadFailure({ workspaceDir: workspaceDir, taskContext, error }).catch((recordError) => console.error(`Could not record task command failure: ${recordError}`));
|
|
6507
|
+
throw error;
|
|
6508
|
+
}
|
|
6509
|
+
finally {
|
|
6510
|
+
workerFailureStage = "agent";
|
|
6511
|
+
}
|
|
6512
|
+
},
|
|
6513
|
+
});
|
|
6514
|
+
}
|
|
6226
6515
|
workerFailureStage = "agent";
|
|
6227
6516
|
agentResult = await runAssignedAgent({
|
|
6228
6517
|
prompt,
|
|
@@ -6314,10 +6603,12 @@ async function startWorker(options = {}) {
|
|
|
6314
6603
|
}
|
|
6315
6604
|
}
|
|
6316
6605
|
}
|
|
6606
|
+
await gameSourceCommandServer?.close();
|
|
6607
|
+
gameSourceCommandServer = null;
|
|
6317
6608
|
resolvedRuntimeModel = agentResult.providerResolvedModel?.trim() || resolvedRuntimeModel;
|
|
6318
6609
|
agentCompletedAt = new Date();
|
|
6319
6610
|
await queueEventDrain().catch((error) => {
|
|
6320
|
-
handleEventDrainFailure(error);
|
|
6611
|
+
return handleEventDrainFailure(error);
|
|
6321
6612
|
});
|
|
6322
6613
|
void archiveWorkerTranscriptBestEffort({
|
|
6323
6614
|
client,
|
|
@@ -6461,215 +6752,16 @@ async function startWorker(options = {}) {
|
|
|
6461
6752
|
await failMissingAgentOutcome(agentRunResult);
|
|
6462
6753
|
}
|
|
6463
6754
|
else if (assignment.gameSource) {
|
|
6464
|
-
if (
|
|
6465
|
-
throw new Error("
|
|
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");
|
|
6755
|
+
if (task.kind !== "STATIC_GAME") {
|
|
6756
|
+
throw new Error("agent_exited_without_successful_task_done");
|
|
6476
6757
|
}
|
|
6477
|
-
|
|
6478
|
-
|
|
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,
|
|
6758
|
+
await publishGameSourceForAgent();
|
|
6759
|
+
await completeGameSourceForAgent({
|
|
6530
6760
|
taskId: task.id,
|
|
6531
|
-
|
|
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",
|
|
6761
|
+
attempt: taskContext.attempt,
|
|
6762
|
+
summary: "Created and privately hosted a standalone static web game.",
|
|
6661
6763
|
});
|
|
6662
6764
|
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
6765
|
}
|
|
6674
6766
|
else if (task.kind === "STATIC_GAME") {
|
|
6675
6767
|
await reportTelemetry("DONE");
|
|
@@ -6842,6 +6934,8 @@ async function startWorker(options = {}) {
|
|
|
6842
6934
|
}
|
|
6843
6935
|
catch (error) {
|
|
6844
6936
|
const message = error instanceof Error ? error.message : String(error);
|
|
6937
|
+
await gameSourceCommandServer?.close();
|
|
6938
|
+
gameSourceCommandServer = null;
|
|
6845
6939
|
if (gameSourceWorkspace) {
|
|
6846
6940
|
await (0, game_source_git_1.checkpointFailedGameSourceWorkspace)({ workspace: gameSourceWorkspace, taskId: task.id }).catch((checkpointError) => {
|
|
6847
6941
|
console.error(`Failed Git workspace checkpoint did not complete: ${checkpointError instanceof Error ? checkpointError.message : String(checkpointError)}`);
|
|
@@ -6899,6 +6993,7 @@ async function startWorker(options = {}) {
|
|
|
6899
6993
|
}
|
|
6900
6994
|
}
|
|
6901
6995
|
finally {
|
|
6996
|
+
await gameSourceCommandServer?.close();
|
|
6902
6997
|
if (heartbeatTimer) {
|
|
6903
6998
|
clearInterval(heartbeatTimer);
|
|
6904
6999
|
}
|
|
@@ -6907,6 +7002,20 @@ async function startWorker(options = {}) {
|
|
|
6907
7002
|
}
|
|
6908
7003
|
activeTerminators.delete(task.id);
|
|
6909
7004
|
await gameServerDevLease?.cleanup();
|
|
7005
|
+
if (completedGameSourceGeneration && gameSourcePublication && gameSourceWorkspace) {
|
|
7006
|
+
const publication = gameSourcePublication;
|
|
7007
|
+
await (0, game_source_git_1.cleanupCompletedGameSourceWorkspace)({
|
|
7008
|
+
workspace: gameSourceWorkspace,
|
|
7009
|
+
workerHomeDir: resolveWorkerHomeDir(),
|
|
7010
|
+
appId: publication.upload.appId,
|
|
7011
|
+
repoGeneration: completedGameSourceGeneration,
|
|
7012
|
+
headSha: publication.finalized.commitSha,
|
|
7013
|
+
versions: publication.finalized.versions,
|
|
7014
|
+
}).catch((cleanupError) => {
|
|
7015
|
+
retainWorkspace = true;
|
|
7016
|
+
console.error(`Completed game source cache cleanup failed: ${cleanupError}`);
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
6910
7019
|
if (workspaceDir) {
|
|
6911
7020
|
await removeAgentRunCredentials(workspaceDir);
|
|
6912
7021
|
}
|
|
@@ -7423,37 +7532,20 @@ async function uploadTask(options = {}) {
|
|
|
7423
7532
|
return;
|
|
7424
7533
|
}
|
|
7425
7534
|
if (taskContext.gameSourceGit) {
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
expectedPrimarySurface: taskContext.outputPrimarySurface,
|
|
7440
|
-
remixSourceRef: taskContext.remixSourceRef ?? null,
|
|
7441
|
-
projectDir,
|
|
7442
|
-
creatorUsername: taskContext.creatorUsername,
|
|
7443
|
-
apiBase: ctx.envConfig.apiBase,
|
|
7444
|
-
webBase: ctx.envConfig.webBase ?? null,
|
|
7445
|
-
token: ctx.token,
|
|
7446
|
-
user: ctx.user,
|
|
7535
|
+
await withTaskUploadLock(workspaceDir, async () => {
|
|
7536
|
+
const ctx = await resolveTaskCommandContext("task upload", options.env, taskContext);
|
|
7537
|
+
if (!ctx)
|
|
7538
|
+
return;
|
|
7539
|
+
await (0, game_source_commands_1.requestWorkerGameSourceCommand)({
|
|
7540
|
+
workspaceDir,
|
|
7541
|
+
taskId: taskContext.taskId,
|
|
7542
|
+
attempt: taskContext.attempt,
|
|
7543
|
+
command: "upload",
|
|
7544
|
+
});
|
|
7545
|
+
const result = readTaskUploadResultFile(workspaceDir);
|
|
7546
|
+
assertTaskUploadResultMatchesContext({ taskContext, uploadResult: result });
|
|
7547
|
+
(0, output_1.printSuccess)(`Uploaded ${result.creatorUsername}/${result.appName} version ${result.version}. Run "playdrop task done" to close the task.`);
|
|
7447
7548
|
});
|
|
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
7549
|
return;
|
|
7458
7550
|
}
|
|
7459
7551
|
if (taskContext.kind === "STATIC_GAME") {
|
|
@@ -7494,6 +7586,7 @@ async function uploadTask(options = {}) {
|
|
|
7494
7586
|
webBase: ctx.envConfig.webBase ?? null,
|
|
7495
7587
|
token: ctx.token,
|
|
7496
7588
|
user: ctx.user,
|
|
7589
|
+
devRouterPort: taskContext.devPort,
|
|
7497
7590
|
});
|
|
7498
7591
|
}
|
|
7499
7592
|
catch (error) {
|
|
@@ -7584,8 +7677,8 @@ async function completeTask(options) {
|
|
|
7584
7677
|
throw new Error("task_done_summary_too_long:max_200_characters");
|
|
7585
7678
|
}
|
|
7586
7679
|
if (taskContext.gameSourceGit) {
|
|
7587
|
-
|
|
7588
|
-
|
|
7680
|
+
const uploadResult = readTaskUploadResultFile(workspaceDir);
|
|
7681
|
+
assertTaskUploadResultMatchesContext({ taskContext, uploadResult });
|
|
7589
7682
|
const ctx = await resolveTaskCommandContext("task done", options.env, taskContext);
|
|
7590
7683
|
if (!ctx)
|
|
7591
7684
|
return;
|
|
@@ -7606,8 +7699,20 @@ async function completeTask(options) {
|
|
|
7606
7699
|
...(nextSteps !== undefined ? { nextSteps } : {}),
|
|
7607
7700
|
...(platformFeedback ? { platformFeedback } : {}),
|
|
7608
7701
|
};
|
|
7609
|
-
await (
|
|
7610
|
-
|
|
7702
|
+
await withTaskUploadLock(workspaceDir, async () => {
|
|
7703
|
+
const result = await (0, game_source_commands_1.requestWorkerGameSourceCommand)({
|
|
7704
|
+
workspaceDir,
|
|
7705
|
+
taskId: taskContext.taskId,
|
|
7706
|
+
attempt: taskContext.attempt,
|
|
7707
|
+
command: "done",
|
|
7708
|
+
payload: intent,
|
|
7709
|
+
});
|
|
7710
|
+
if (!result || typeof result !== "object" || !("completed" in result) || result.completed !== true) {
|
|
7711
|
+
throw new Error("game_source_completion_response_invalid");
|
|
7712
|
+
}
|
|
7713
|
+
await (0, promises_1.writeFile)(workerGameSourceCompletionPath(workspaceDir), `${JSON.stringify(intent, null, 2)}\n`, "utf8");
|
|
7714
|
+
});
|
|
7715
|
+
(0, output_1.printSuccess)("Task marked done.");
|
|
7611
7716
|
return;
|
|
7612
7717
|
}
|
|
7613
7718
|
const uploadResult = readTaskUploadResultFile();
|
|
@@ -7955,7 +8060,7 @@ async function failTask(options) {
|
|
|
7955
8060
|
const classifiedMessage = (0, types_1.classifyAgentTaskTerminalReason)(message, "agent");
|
|
7956
8061
|
const terminalReason = uploadFailure?.terminalReason ?? {
|
|
7957
8062
|
...classifiedMessage,
|
|
7958
|
-
message
|
|
8063
|
+
message,
|
|
7959
8064
|
retryable: options.retryableWithSameInput === true,
|
|
7960
8065
|
source: "agent",
|
|
7961
8066
|
};
|