@playdrop/playdrop-cli 0.12.41 → 0.13.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 +2 -2
- package/dist/apps/index.d.ts +3 -0
- package/dist/apps/index.js +3 -1
- package/dist/apps/loadCheck.d.ts +21 -1
- package/dist/apps/loadCheck.js +253 -1
- package/dist/apps/playtestContract.d.ts +2 -0
- package/dist/apps/playtestContract.js +77 -0
- package/dist/apps/staticHtml.d.ts +28 -0
- package/dist/apps/staticHtml.js +343 -0
- package/dist/apps/upload.d.ts +1 -0
- package/dist/apps/upload.js +17 -0
- package/dist/apps/validate.js +2 -1
- package/dist/commands/devServer.js +19 -0
- package/dist/commands/feedback.d.ts +2 -0
- package/dist/commands/feedback.js +44 -2
- package/dist/commands/upload.d.ts +24 -0
- package/dist/commands/upload.js +131 -60
- package/dist/commands/worker/instrument-evidence.js +1 -1
- package/dist/commands/worker/runtime.d.ts +14 -0
- package/dist/commands/worker/runtime.js +97 -8
- package/dist/commands/worker.d.ts +27 -2
- package/dist/commands/worker.js +688 -146
- package/dist/index.js +7 -0
- package/dist/listingPreflight.js +15 -4
- package/node_modules/@playdrop/api-client/dist/client.d.ts +6 -2
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +3 -2
- 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 +13 -2
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/me.js +11 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +6 -3
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +14 -2
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/config/dist/src/constants.d.ts +1 -0
- package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/constants.js +1 -0
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +195 -4
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +25 -2
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +4 -0
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-playtest-tape.js +56 -0
- package/node_modules/@playdrop/types/dist/version.d.ts +49 -1
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/entities/decode-codepoint.d.ts +19 -0
- package/node_modules/entities/decode-codepoint.d.ts.map +1 -0
- package/node_modules/entities/decode-codepoint.js +77 -0
- package/node_modules/entities/decode-codepoint.js.map +1 -0
- package/node_modules/entities/decode.d.ts +209 -0
- package/node_modules/entities/decode.d.ts.map +1 -0
- package/node_modules/entities/decode.js +511 -0
- package/node_modules/entities/decode.js.map +1 -0
- package/node_modules/entities/encode.d.ts +22 -0
- package/node_modules/entities/encode.d.ts.map +1 -0
- package/node_modules/entities/encode.js +73 -0
- package/node_modules/entities/encode.js.map +1 -0
- package/node_modules/entities/escape.d.ts +43 -0
- package/node_modules/entities/escape.d.ts.map +1 -0
- package/node_modules/entities/escape.js +121 -0
- package/node_modules/entities/escape.js.map +1 -0
- package/node_modules/entities/generated/decode-data-html.d.ts +2 -0
- package/node_modules/entities/generated/decode-data-html.d.ts.map +1 -0
- package/node_modules/entities/generated/decode-data-html.js +10 -0
- package/node_modules/entities/generated/decode-data-html.js.map +1 -0
- package/node_modules/entities/generated/decode-data-xml.d.ts +2 -0
- package/node_modules/entities/generated/decode-data-xml.d.ts.map +1 -0
- package/node_modules/entities/generated/decode-data-xml.js +10 -0
- package/node_modules/entities/generated/decode-data-xml.js.map +1 -0
- package/node_modules/entities/generated/encode-html.d.ts +8 -0
- package/node_modules/entities/generated/encode-html.d.ts.map +1 -0
- package/node_modules/entities/generated/encode-html.js +13 -0
- package/node_modules/entities/generated/encode-html.js.map +1 -0
- package/node_modules/entities/index.d.ts +96 -0
- package/node_modules/entities/index.d.ts.map +1 -0
- package/node_modules/entities/index.js +131 -0
- package/node_modules/entities/index.js.map +1 -0
- package/node_modules/entities/package.json +3 -0
- package/node_modules/parse5/common/doctype.d.ts +4 -0
- package/node_modules/parse5/common/doctype.js +118 -0
- package/node_modules/parse5/common/error-codes.d.ts +67 -0
- package/node_modules/parse5/common/error-codes.js +66 -0
- package/node_modules/parse5/common/foreign-content.d.ts +9 -0
- package/node_modules/parse5/common/foreign-content.js +237 -0
- package/node_modules/parse5/common/html.d.ts +289 -0
- package/node_modules/parse5/common/html.js +528 -0
- package/node_modules/parse5/common/token.d.ts +84 -0
- package/node_modules/parse5/common/token.js +24 -0
- package/node_modules/parse5/common/unicode.d.ts +42 -0
- package/node_modules/parse5/common/unicode.js +70 -0
- package/node_modules/parse5/index.d.ts +71 -0
- package/node_modules/parse5/index.js +54 -0
- package/node_modules/parse5/package.json +1 -0
- package/node_modules/parse5/parser/formatting-element-list.d.ts +36 -0
- package/node_modules/parse5/parser/formatting-element-list.js +114 -0
- package/node_modules/parse5/parser/index.d.ts +221 -0
- package/node_modules/parse5/parser/index.js +3240 -0
- package/node_modules/parse5/parser/open-element-stack.d.ts +53 -0
- package/node_modules/parse5/parser/open-element-stack.js +328 -0
- package/node_modules/parse5/serializer/index.d.ts +60 -0
- package/node_modules/parse5/serializer/index.js +172 -0
- package/node_modules/parse5/tokenizer/index.d.ts +247 -0
- package/node_modules/parse5/tokenizer/index.js +2714 -0
- package/node_modules/parse5/tokenizer/preprocessor.d.ts +36 -0
- package/node_modules/parse5/tokenizer/preprocessor.js +200 -0
- package/node_modules/parse5/tree-adapters/default.d.ts +84 -0
- package/node_modules/parse5/tree-adapters/default.js +176 -0
- package/node_modules/parse5/tree-adapters/interface.d.ts +255 -0
- package/node_modules/parse5/tree-adapters/interface.js +2 -0
- package/package.json +7 -1
package/dist/commands/worker.js
CHANGED
|
@@ -3,7 +3,10 @@ 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_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.runLoggedProcess = exports.readEnvBoolean = exports.readCodexSandboxMode = exports.readCodexRolloutTokenUsageForThread = exports.extractCodexTokensUsed = exports.extractCodexThreadId = exports.extractCodexRolloutTokenUsage = exports.extractAgentTokenUsage = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = exports.buildWorkerChildEnv = exports.buildPlaywrightMcpCommand = exports.buildCodexExecArgs = exports.buildClaudePermissionSettings = exports.buildClaudeExecArgs = exports.assertWorkerTokenUsageWithinCap = void 0;
|
|
6
|
+
exports.WORKER_TASK_WORKSPACE_RETENTION = exports.WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = exports.WORKER_SESSION_EXPIRED_MESSAGE = exports.runLoggedProcess = exports.readEnvBoolean = exports.readCodexSandboxMode = exports.readCodexRolloutTokenUsageForThread = exports.extractCodexTokensUsed = exports.extractCodexThreadId = exports.extractClaudeSessionId = exports.extractCodexRolloutTokenUsage = exports.extractAgentTokenUsage = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS = exports.DEFAULT_CREATOR_AGENT_TIMEOUT_MS = exports.DEFAULT_CODEX_TIMEOUT_MS = exports.buildWorkerChildEnv = exports.buildPlaywrightMcpCommand = exports.buildCodexExecArgs = exports.buildCleanRoomChildEnv = exports.buildClaudePermissionSettings = exports.buildClaudeExecArgs = exports.assertWorkerTokenUsageWithinCap = void 0;
|
|
7
|
+
exports.usesCreatorRecovery = usesCreatorRecovery;
|
|
8
|
+
exports.buildCreatorRecoveryPrompt = buildCreatorRecoveryPrompt;
|
|
9
|
+
exports.buildGameEvalRetryPrompt = buildGameEvalRetryPrompt;
|
|
7
10
|
exports.allocateWorkerDevPort = allocateWorkerDevPort;
|
|
8
11
|
exports.isWorkerContextCommandAllowed = isWorkerContextCommandAllowed;
|
|
9
12
|
exports.resolveWorkerExecutionTargetFromRole = resolveWorkerExecutionTargetFromRole;
|
|
@@ -12,6 +15,7 @@ exports.nextClaimBackoffMs = nextClaimBackoffMs;
|
|
|
12
15
|
exports.claimBackoffDelayMs = claimBackoffDelayMs;
|
|
13
16
|
exports.resolveWorkerHomeDir = resolveWorkerHomeDir;
|
|
14
17
|
exports.pruneWorkerTaskWorkspaces = pruneWorkerTaskWorkspaces;
|
|
18
|
+
exports.prepareClaimedTaskWorkspace = prepareClaimedTaskWorkspace;
|
|
15
19
|
exports.resolveWorkerInstrumentSurface = resolveWorkerInstrumentSurface;
|
|
16
20
|
exports.hasAgentTaskUploadedArtifact = hasAgentTaskUploadedArtifact;
|
|
17
21
|
exports.appendWorkerTranscriptChunks = appendWorkerTranscriptChunks;
|
|
@@ -19,6 +23,7 @@ exports.readTaskNextStepsFile = readTaskNextStepsFile;
|
|
|
19
23
|
exports.assertTaskUploadResultMatchesContext = assertTaskUploadResultMatchesContext;
|
|
20
24
|
exports.stageAssignmentWorkspaceV2 = stageAssignmentWorkspaceV2;
|
|
21
25
|
exports.assertAssignmentPluginBundleExtractionBounds = assertAssignmentPluginBundleExtractionBounds;
|
|
26
|
+
exports.stageDevPluginWorkingTree = stageDevPluginWorkingTree;
|
|
22
27
|
exports.stageAssignmentPluginBundle = stageAssignmentPluginBundle;
|
|
23
28
|
exports.resolveDevPluginWorkingTree = resolveDevPluginWorkingTree;
|
|
24
29
|
exports.discoverWorkerProjectRoot = discoverWorkerProjectRoot;
|
|
@@ -43,6 +48,7 @@ exports.prepareCodexRunHomes = prepareCodexRunHomes;
|
|
|
43
48
|
exports.prepareClaudeRunConfig = prepareClaudeRunConfig;
|
|
44
49
|
exports.removeAgentRunCredentials = removeAgentRunCredentials;
|
|
45
50
|
exports.removeStaleAgentRunCredentials = removeStaleAgentRunCredentials;
|
|
51
|
+
exports.mergeCreatorRecoveryResults = mergeCreatorRecoveryResults;
|
|
46
52
|
exports.buildAgentFailureCode = buildAgentFailureCode;
|
|
47
53
|
exports.describeAgentFailureForEvent = describeAgentFailureForEvent;
|
|
48
54
|
exports.createLocalPlaydropShim = createLocalPlaydropShim;
|
|
@@ -71,6 +77,7 @@ exports.showWorkerStatus = showWorkerStatus;
|
|
|
71
77
|
exports.setupWorker = setupWorker;
|
|
72
78
|
exports.instrumentTaskChromeTitleMatchers = instrumentTaskChromeTitleMatchers;
|
|
73
79
|
exports.enforceInstrumentBrowserHygiene = enforceInstrumentBrowserHygiene;
|
|
80
|
+
exports.assignmentRequiresRegisteredBundle = assignmentRequiresRegisteredBundle;
|
|
74
81
|
exports.startWorker = startWorker;
|
|
75
82
|
exports.reportTask = reportTask;
|
|
76
83
|
exports.showTaskContext = showTaskContext;
|
|
@@ -110,13 +117,17 @@ var runtime_2 = require("./worker/runtime");
|
|
|
110
117
|
Object.defineProperty(exports, "assertWorkerTokenUsageWithinCap", { enumerable: true, get: function () { return runtime_2.assertWorkerTokenUsageWithinCap; } });
|
|
111
118
|
Object.defineProperty(exports, "buildClaudeExecArgs", { enumerable: true, get: function () { return runtime_2.buildClaudeExecArgs; } });
|
|
112
119
|
Object.defineProperty(exports, "buildClaudePermissionSettings", { enumerable: true, get: function () { return runtime_2.buildClaudePermissionSettings; } });
|
|
120
|
+
Object.defineProperty(exports, "buildCleanRoomChildEnv", { enumerable: true, get: function () { return runtime_2.buildCleanRoomChildEnv; } });
|
|
113
121
|
Object.defineProperty(exports, "buildCodexExecArgs", { enumerable: true, get: function () { return runtime_2.buildCodexExecArgs; } });
|
|
114
122
|
Object.defineProperty(exports, "buildPlaywrightMcpCommand", { enumerable: true, get: function () { return runtime_2.buildPlaywrightMcpCommand; } });
|
|
115
123
|
Object.defineProperty(exports, "buildWorkerChildEnv", { enumerable: true, get: function () { return runtime_2.buildWorkerChildEnv; } });
|
|
116
124
|
Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
|
|
125
|
+
Object.defineProperty(exports, "DEFAULT_CREATOR_AGENT_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CREATOR_AGENT_TIMEOUT_MS; } });
|
|
126
|
+
Object.defineProperty(exports, "DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS; } });
|
|
117
127
|
Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
|
|
118
128
|
Object.defineProperty(exports, "extractAgentTokenUsage", { enumerable: true, get: function () { return runtime_2.extractAgentTokenUsage; } });
|
|
119
129
|
Object.defineProperty(exports, "extractCodexRolloutTokenUsage", { enumerable: true, get: function () { return runtime_2.extractCodexRolloutTokenUsage; } });
|
|
130
|
+
Object.defineProperty(exports, "extractClaudeSessionId", { enumerable: true, get: function () { return runtime_2.extractClaudeSessionId; } });
|
|
120
131
|
Object.defineProperty(exports, "extractCodexThreadId", { enumerable: true, get: function () { return runtime_2.extractCodexThreadId; } });
|
|
121
132
|
Object.defineProperty(exports, "extractCodexTokensUsed", { enumerable: true, get: function () { return runtime_2.extractCodexTokensUsed; } });
|
|
122
133
|
Object.defineProperty(exports, "readCodexRolloutTokenUsageForThread", { enumerable: true, get: function () { return runtime_2.readCodexRolloutTokenUsageForThread; } });
|
|
@@ -137,7 +148,9 @@ const FAIL_REPORT_RETRY_DELAY_MS = 2000;
|
|
|
137
148
|
const WORKER_TRANSCRIPT_CHUNK_BATCH_SIZE = 100;
|
|
138
149
|
const SLACK_API_BASE_URL = 'https://slack.com/api';
|
|
139
150
|
const WORKER_WRAPPER_CONTRACT_VERSION = 2;
|
|
140
|
-
const WORKER_SUPPORTED_KINDS = ['NEW_GAME', 'REMIX_GAME', 'GAME_UPDATE', 'GAME_REVIEW', 'GAME_EVAL'];
|
|
151
|
+
const WORKER_SUPPORTED_KINDS = ['NEW_GAME', 'STATIC_GAME', 'REMIX_GAME', 'GAME_UPDATE', 'GAME_REVIEW', 'GAME_EVAL'];
|
|
152
|
+
const GAME_CREATION_TASK_KINDS = new Set(['NEW_GAME', 'STATIC_GAME', 'REMIX_GAME', 'GAME_UPDATE']);
|
|
153
|
+
const AGENT_MANAGED_UPLOAD_TASK_KINDS = new Set(['NEW_GAME', 'REMIX_GAME', 'GAME_UPDATE']);
|
|
141
154
|
const requireFromWorker = (0, node_module_1.createRequire)(__filename);
|
|
142
155
|
const AGENT_BUNDLE_MANIFEST_PATH = 'agent-bundle.json';
|
|
143
156
|
const ASSIGNMENT_PLUGIN_BUNDLE_MAX_FILES = 1000;
|
|
@@ -148,6 +161,36 @@ const ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
|
|
|
148
161
|
const ZIP_END_OF_CENTRAL_DIRECTORY_MIN_BYTES = 22;
|
|
149
162
|
const ZIP_MAX_COMMENT_BYTES = 0xffff;
|
|
150
163
|
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
164
|
+
function usesCreatorRecovery(kind) {
|
|
165
|
+
return GAME_CREATION_TASK_KINDS.has(kind);
|
|
166
|
+
}
|
|
167
|
+
function buildCreatorRecoveryPrompt(input) {
|
|
168
|
+
const prompt = input.prompt.trim();
|
|
169
|
+
if (!prompt) {
|
|
170
|
+
throw new Error('agent_task_assignment_prompt_missing');
|
|
171
|
+
}
|
|
172
|
+
if (!GAME_CREATION_TASK_KINDS.has(input.kind)) {
|
|
173
|
+
throw new Error(`agent_task_creation_recovery_unsupported:${input.kind}`);
|
|
174
|
+
}
|
|
175
|
+
const finishInstruction = AGENT_MANAGED_UPLOAD_TASK_KINDS.has(input.kind)
|
|
176
|
+
? 'Prioritize `playdrop task upload` and then `playdrop task done`.'
|
|
177
|
+
: 'Finish a valid standalone `index.html` at the project root, then stop so the worker can publish it.';
|
|
178
|
+
return [
|
|
179
|
+
'This task was paused because it was taking too long. Continue from the existing workspace and do not restart or redesign the game.',
|
|
180
|
+
'You have 10 minutes to finish the strongest solid version already present. Make only the smallest fixes required for validation and publishing.',
|
|
181
|
+
`${finishInstruction} Do not add features or polish. If completion cannot succeed, report the exact blocker and stop.`,
|
|
182
|
+
`Original request: ${prompt}`,
|
|
183
|
+
].join('\n\n');
|
|
184
|
+
}
|
|
185
|
+
function buildGameEvalRetryPrompt(prompt) {
|
|
186
|
+
return [
|
|
187
|
+
prompt,
|
|
188
|
+
'',
|
|
189
|
+
'This is a retry in the existing evaluation workspace.',
|
|
190
|
+
'Reuse the existing result.json and evidence. Do not replay completed game checks unless required evidence is missing or invalid.',
|
|
191
|
+
'First correct any deterministic submission-contract error, then submit the existing result and evidence and finish.',
|
|
192
|
+
].join('\n');
|
|
193
|
+
}
|
|
151
194
|
exports.WORKER_SESSION_EXPIRED_MESSAGE = 'worker session expired: run "playdrop auth login" and start the worker again';
|
|
152
195
|
const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
153
196
|
['task', 'context'],
|
|
@@ -505,6 +548,29 @@ async function pruneWorkerTaskWorkspaces(incomingWorkspaceDir, retention = expor
|
|
|
505
548
|
}
|
|
506
549
|
return removed;
|
|
507
550
|
}
|
|
551
|
+
async function prepareClaimedTaskWorkspace(input) {
|
|
552
|
+
await pruneWorkerTaskWorkspaces(input.workspaceDir);
|
|
553
|
+
const resumed = input.attempt > 1 && (0, node_fs_1.existsSync)(input.workspaceDir);
|
|
554
|
+
if (!resumed) {
|
|
555
|
+
await (0, promises_1.rm)(input.workspaceDir, { recursive: true, force: true });
|
|
556
|
+
await (0, promises_1.mkdir)(input.workspaceDir, { recursive: true });
|
|
557
|
+
return { resumed: false };
|
|
558
|
+
}
|
|
559
|
+
await removeAgentRunCredentials(input.workspaceDir);
|
|
560
|
+
const retainedProjectBindings = [
|
|
561
|
+
node_path_1.default.join(input.workspaceDir, '.playdrop.json'),
|
|
562
|
+
...(0, node_fs_1.readdirSync)(input.workspaceDir, { withFileTypes: true })
|
|
563
|
+
.filter((entry) => entry.isDirectory() && entry.name !== '.playdrop')
|
|
564
|
+
.map((entry) => node_path_1.default.join(input.workspaceDir, entry.name, '.playdrop.json')),
|
|
565
|
+
];
|
|
566
|
+
await Promise.all([
|
|
567
|
+
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, '.playdrop', 'task-events'), { recursive: true, force: true }),
|
|
568
|
+
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, '.playdrop', 'task-upload.lock'), { force: true }),
|
|
569
|
+
(0, promises_1.rm)(node_path_1.default.join(input.workspaceDir, '.playdrop', 'task-upload-result.json'), { force: true }),
|
|
570
|
+
...retainedProjectBindings.map((file) => (0, promises_1.rm)(file, { force: true })),
|
|
571
|
+
]);
|
|
572
|
+
return { resumed: true };
|
|
573
|
+
}
|
|
508
574
|
function buildWorkerTaskContext(input) {
|
|
509
575
|
const playdrop = input.metadata.playdrop;
|
|
510
576
|
if (!playdrop || typeof playdrop !== 'object' || Array.isArray(playdrop)) {
|
|
@@ -512,7 +578,7 @@ function buildWorkerTaskContext(input) {
|
|
|
512
578
|
}
|
|
513
579
|
const metadata = playdrop;
|
|
514
580
|
const kind = metadata.kind;
|
|
515
|
-
if (kind !== 'NEW_GAME' && kind !== 'REMIX_GAME' && kind !== 'GAME_UPDATE' && kind !== 'GAME_REVIEW' && kind !== 'GAME_EVAL') {
|
|
581
|
+
if (kind !== 'NEW_GAME' && kind !== 'STATIC_GAME' && kind !== 'REMIX_GAME' && kind !== 'GAME_UPDATE' && kind !== 'GAME_REVIEW' && kind !== 'GAME_EVAL') {
|
|
516
582
|
throw new Error('agent_task_context_kind_missing');
|
|
517
583
|
}
|
|
518
584
|
const creatorUsername = typeof metadata.creatorUsername === 'string'
|
|
@@ -583,6 +649,23 @@ function resolveWorkerInstrumentSurface(taskContext) {
|
|
|
583
649
|
return null;
|
|
584
650
|
}
|
|
585
651
|
const metadata = playdrop;
|
|
652
|
+
if (taskContext.kind === 'STATIC_GAME') {
|
|
653
|
+
const surfaceContext = metadata.surfaceContext;
|
|
654
|
+
if (!surfaceContext || typeof surfaceContext !== 'object' || Array.isArray(surfaceContext)) {
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
const context = surfaceContext;
|
|
658
|
+
if (context.deviceClass === 'DESKTOP') {
|
|
659
|
+
return 'DESKTOP';
|
|
660
|
+
}
|
|
661
|
+
if (context.orientation === 'PORTRAIT') {
|
|
662
|
+
return 'MOBILE_PORTRAIT';
|
|
663
|
+
}
|
|
664
|
+
if (context.orientation === 'LANDSCAPE') {
|
|
665
|
+
return 'MOBILE_LANDSCAPE';
|
|
666
|
+
}
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
586
669
|
if (taskContext.kind === 'GAME_REVIEW') {
|
|
587
670
|
const reviewTarget = metadata.reviewTarget;
|
|
588
671
|
if (!reviewTarget || typeof reviewTarget !== 'object' || Array.isArray(reviewTarget)) {
|
|
@@ -1073,17 +1156,19 @@ function readAgentBundleManifest(extractedRoot) {
|
|
|
1073
1156
|
}
|
|
1074
1157
|
const manifest = parsed;
|
|
1075
1158
|
if (manifest.version !== 1
|
|
1076
|
-
|| !manifest
|
|
1077
|
-
|| typeof manifest.plugin
|
|
1159
|
+
|| !Object.prototype.hasOwnProperty.call(manifest, 'plugin')
|
|
1160
|
+
|| (manifest.plugin !== null && typeof manifest.plugin?.root !== 'string')
|
|
1078
1161
|
|| !manifest.workspace
|
|
1079
1162
|
|| !Array.isArray(manifest.workspace.files)) {
|
|
1080
1163
|
throw new Error('agent_bundle_manifest_invalid');
|
|
1081
1164
|
}
|
|
1082
|
-
const pluginRoot =
|
|
1083
|
-
|
|
1165
|
+
const pluginRoot = manifest.plugin === null
|
|
1166
|
+
? null
|
|
1167
|
+
: normalizeAgentBundlePath(extractedRoot, manifest.plugin?.root, 'agent_bundle_plugin_root_invalid');
|
|
1168
|
+
if (pluginRoot && !isFile(node_path_1.default.join(pluginRoot, '.codex-plugin', 'plugin.json'))) {
|
|
1084
1169
|
throw new Error('agent_bundle_codex_plugin_manifest_missing');
|
|
1085
1170
|
}
|
|
1086
|
-
if (!isFile(node_path_1.default.join(pluginRoot, '.claude-plugin', 'plugin.json'))) {
|
|
1171
|
+
if (pluginRoot && !isFile(node_path_1.default.join(pluginRoot, '.claude-plugin', 'plugin.json'))) {
|
|
1087
1172
|
throw new Error('agent_bundle_claude_plugin_manifest_missing');
|
|
1088
1173
|
}
|
|
1089
1174
|
const destinations = new Set();
|
|
@@ -1146,6 +1231,19 @@ async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir, taskK
|
|
|
1146
1231
|
}
|
|
1147
1232
|
return resolved.pluginRoot;
|
|
1148
1233
|
}
|
|
1234
|
+
async function stageDevPluginWorkingTree(input) {
|
|
1235
|
+
const sourceRoot = node_path_1.default.resolve(input.workingTree);
|
|
1236
|
+
readAgentBundleManifest(sourceRoot);
|
|
1237
|
+
const snapshotRoot = node_path_1.default.join(input.workspaceDir, '.playdrop', 'dev-task-plugin');
|
|
1238
|
+
await (0, promises_1.rm)(snapshotRoot, { recursive: true, force: true });
|
|
1239
|
+
await (0, promises_1.mkdir)(node_path_1.default.dirname(snapshotRoot), { recursive: true });
|
|
1240
|
+
await (0, promises_1.cp)(sourceRoot, snapshotRoot, { recursive: true });
|
|
1241
|
+
const pluginRoot = await stageAgentBundleWorkspaceFiles(snapshotRoot, input.workspaceDir, input.taskKind);
|
|
1242
|
+
if (!pluginRoot) {
|
|
1243
|
+
throw new Error('worker_dev_plugin_working_tree_requires_plugin');
|
|
1244
|
+
}
|
|
1245
|
+
return pluginRoot;
|
|
1246
|
+
}
|
|
1149
1247
|
async function stageAssignmentPluginBundle(input) {
|
|
1150
1248
|
const sha256 = typeof input.pluginBundle.sha256 === 'string' ? input.pluginBundle.sha256.trim() : '';
|
|
1151
1249
|
const fileName = typeof input.pluginBundle.fileName === 'string' ? input.pluginBundle.fileName.trim() : '';
|
|
@@ -1168,7 +1266,7 @@ async function stageAssignmentPluginBundle(input) {
|
|
|
1168
1266
|
throw new Error('agent_task_plugin_bundle_sha_mismatch');
|
|
1169
1267
|
}
|
|
1170
1268
|
assertAssignmentPluginBundleExtractionBounds(buffer);
|
|
1171
|
-
const pluginRoot = node_path_1.default.join(input.workspaceDir, '.playdrop', 'task-plugin');
|
|
1269
|
+
const pluginRoot = node_path_1.default.join(input.extractionDir ?? input.workspaceDir, '.playdrop', 'task-plugin');
|
|
1172
1270
|
await (0, promises_1.rm)(pluginRoot, { recursive: true, force: true });
|
|
1173
1271
|
await (0, promises_1.mkdir)(pluginRoot, { recursive: true });
|
|
1174
1272
|
const entries = (0, fflate_1.unzipSync)(new Uint8Array(buffer));
|
|
@@ -1215,7 +1313,9 @@ function resolveDevPluginWorkingTree(input) {
|
|
|
1215
1313
|
throw new Error(`worker_dev_plugin_working_tree_requires_dev_environment:${input.env}`);
|
|
1216
1314
|
}
|
|
1217
1315
|
const resolved = node_path_1.default.resolve(workingTree);
|
|
1218
|
-
readAgentBundleManifest(resolved)
|
|
1316
|
+
if (!readAgentBundleManifest(resolved).pluginRoot) {
|
|
1317
|
+
throw new Error('worker_dev_plugin_working_tree_requires_plugin');
|
|
1318
|
+
}
|
|
1219
1319
|
return resolved;
|
|
1220
1320
|
}
|
|
1221
1321
|
// A PlayDrop project root is the topmost directory holding a catalogue.json,
|
|
@@ -1900,11 +2000,15 @@ async function prepareCodexRunHomes(input) {
|
|
|
1900
2000
|
const runRoot = node_path_1.default.join(input.workspaceDir, '.playdrop', 'codex-run');
|
|
1901
2001
|
const homeDir = node_path_1.default.join(runRoot, 'home');
|
|
1902
2002
|
const codexHomeDir = node_path_1.default.join(runRoot, 'codex-home');
|
|
1903
|
-
|
|
2003
|
+
if (!input.preserveExisting) {
|
|
2004
|
+
await (0, promises_1.rm)(runRoot, { recursive: true, force: true });
|
|
2005
|
+
}
|
|
1904
2006
|
await (0, promises_1.mkdir)(runRoot, { recursive: true, mode: 0o700 });
|
|
1905
2007
|
await (0, promises_1.chmod)(runRoot, 0o700);
|
|
1906
|
-
await (0, promises_1.mkdir)(homeDir, { mode: 0o700 });
|
|
1907
|
-
await (0, promises_1.
|
|
2008
|
+
await (0, promises_1.mkdir)(homeDir, { recursive: true, mode: 0o700 });
|
|
2009
|
+
await (0, promises_1.chmod)(homeDir, 0o700);
|
|
2010
|
+
await (0, promises_1.mkdir)(codexHomeDir, { recursive: true, mode: 0o700 });
|
|
2011
|
+
await (0, promises_1.chmod)(codexHomeDir, 0o700);
|
|
1908
2012
|
const sourceCodexHome = node_path_1.default.resolve(input.sourceCodexHome?.trim()
|
|
1909
2013
|
|| node_process_1.default.env.CODEX_HOME?.trim()
|
|
1910
2014
|
|| node_path_1.default.join(node_os_1.default.homedir(), '.codex'));
|
|
@@ -1915,12 +2019,14 @@ async function prepareCodexRunHomes(input) {
|
|
|
1915
2019
|
const stagedAuthPath = node_path_1.default.join(codexHomeDir, 'auth.json');
|
|
1916
2020
|
await (0, promises_1.copyFile)(sourceAuthPath, stagedAuthPath);
|
|
1917
2021
|
await (0, promises_1.chmod)(stagedAuthPath, 0o600);
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2022
|
+
if (input.stagePlaydropSession !== false) {
|
|
2023
|
+
await stageWorkerPlaydropSession({
|
|
2024
|
+
homeDir,
|
|
2025
|
+
workerUsername: input.workerUsername,
|
|
2026
|
+
envName: input.envName,
|
|
2027
|
+
playdropConfig: input.playdropConfig,
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
1924
2030
|
return { homeDir, codexHomeDir };
|
|
1925
2031
|
}
|
|
1926
2032
|
async function stageWorkerPlaydropSession(input) {
|
|
@@ -1999,11 +2105,13 @@ async function removeStaleAgentRunCredentials(workerHomeDir = resolveWorkerHomeD
|
|
|
1999
2105
|
}
|
|
2000
2106
|
async function runCodex(input) {
|
|
2001
2107
|
const { homeDir, codexHomeDir } = await prepareCodexRunHomes({
|
|
2002
|
-
workspaceDir: input.workspaceDir,
|
|
2108
|
+
workspaceDir: input.stateDir ?? input.workspaceDir,
|
|
2003
2109
|
workerUsername: input.workerUsername,
|
|
2004
2110
|
envName: input.envName,
|
|
2111
|
+
stagePlaydropSession: !input.cleanRoom,
|
|
2112
|
+
preserveExisting: Boolean(input.resumeSessionId),
|
|
2005
2113
|
});
|
|
2006
|
-
if (input.taskPluginRoot) {
|
|
2114
|
+
if (input.taskPluginRoot && !input.resumeSessionId) {
|
|
2007
2115
|
const pluginEnv = {
|
|
2008
2116
|
...node_process_1.default.env,
|
|
2009
2117
|
HOME: homeDir,
|
|
@@ -2018,7 +2126,7 @@ async function runCodex(input) {
|
|
|
2018
2126
|
env: pluginEnv,
|
|
2019
2127
|
});
|
|
2020
2128
|
}
|
|
2021
|
-
if (input.enablePlaywrightMcp) {
|
|
2129
|
+
if (input.enablePlaywrightMcp && !input.resumeSessionId) {
|
|
2022
2130
|
const server = (0, runtime_1.buildPlaywrightMcpCommand)(input.instrumentSurface);
|
|
2023
2131
|
await execFileAsync('codex', ['mcp', 'add', 'playwright', '--', server.command, ...server.args], {
|
|
2024
2132
|
cwd: input.workspaceDir,
|
|
@@ -2040,12 +2148,17 @@ async function runCodex(input) {
|
|
|
2040
2148
|
workspaceDir: input.workspaceDir,
|
|
2041
2149
|
model: input.codexModel.model,
|
|
2042
2150
|
reasoningEffort: input.codexModel.reasoningEffort,
|
|
2043
|
-
networkAccess: (0, runtime_1.readEnvBoolean)('PLAYDROP_WORKER_AGENT_NETWORK_ACCESS', false),
|
|
2151
|
+
networkAccess: input.cleanRoom || (0, runtime_1.readEnvBoolean)('PLAYDROP_WORKER_AGENT_NETWORK_ACCESS', false),
|
|
2044
2152
|
sandboxMode: (0, runtime_1.readCodexSandboxMode)(),
|
|
2045
2153
|
imagePaths: input.imagePaths,
|
|
2154
|
+
resumeSessionId: input.resumeSessionId,
|
|
2046
2155
|
}),
|
|
2047
2156
|
cwd: input.workspaceDir,
|
|
2048
|
-
env: (0, runtime_1.
|
|
2157
|
+
env: input.cleanRoom ? (0, runtime_1.buildCleanRoomChildEnv)({
|
|
2158
|
+
binDir: input.binDir,
|
|
2159
|
+
homeDir,
|
|
2160
|
+
codexHomeDir,
|
|
2161
|
+
}) : (0, runtime_1.buildWorkerChildEnv)({
|
|
2049
2162
|
binDir: input.binDir,
|
|
2050
2163
|
taskId: input.taskId,
|
|
2051
2164
|
taskToken: input.taskToken,
|
|
@@ -2058,7 +2171,7 @@ async function runCodex(input) {
|
|
|
2058
2171
|
playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
|
|
2059
2172
|
}),
|
|
2060
2173
|
stdin: input.prompt,
|
|
2061
|
-
timeoutMs: (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_CODEX_TIMEOUT_MS', runtime_1.DEFAULT_CODEX_TIMEOUT_MS),
|
|
2174
|
+
timeoutMs: input.timeoutMs ?? (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_CODEX_TIMEOUT_MS', runtime_1.DEFAULT_CODEX_TIMEOUT_MS),
|
|
2062
2175
|
maxOutputChars: runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS,
|
|
2063
2176
|
onTranscriptChunks: async (chunks) => {
|
|
2064
2177
|
if (!observedCodexThreadId) {
|
|
@@ -2080,16 +2193,17 @@ async function runCodex(input) {
|
|
|
2080
2193
|
if (result.tokenUsage.usageParseError) {
|
|
2081
2194
|
console.warn('Codex thread id was not observed; rollout token usage could not be read.');
|
|
2082
2195
|
}
|
|
2083
|
-
return result;
|
|
2196
|
+
return { ...result, providerSessionId: null };
|
|
2084
2197
|
}
|
|
2085
2198
|
const rolloutUsage = await (0, runtime_1.readCodexRolloutTokenUsageForThread)(observedCodexThreadId, {
|
|
2086
2199
|
sessionsRoot: node_path_1.default.join(codexHomeDir, 'sessions'),
|
|
2200
|
+
deleteAfterRead: false,
|
|
2087
2201
|
});
|
|
2088
2202
|
if (rolloutUsage.usageParseError || rolloutUsage.totalTokens === null) {
|
|
2089
2203
|
if (result.tokenUsage.usageParseError) {
|
|
2090
2204
|
console.warn(`Codex rollout token usage could not be read for thread ${observedCodexThreadId}: ${rolloutUsage.usageParseError ?? 'unknown_error'}.`);
|
|
2091
2205
|
}
|
|
2092
|
-
return result;
|
|
2206
|
+
return { ...result, providerSessionId: observedCodexThreadId };
|
|
2093
2207
|
}
|
|
2094
2208
|
if (!result.tokenUsage.usageParseError
|
|
2095
2209
|
&& result.tokenUsage.totalTokens !== null
|
|
@@ -2098,20 +2212,25 @@ async function runCodex(input) {
|
|
|
2098
2212
|
}
|
|
2099
2213
|
return {
|
|
2100
2214
|
...result,
|
|
2215
|
+
providerSessionId: observedCodexThreadId,
|
|
2101
2216
|
tokenUsage: rolloutUsage,
|
|
2102
2217
|
tokensUsed: rolloutUsage.totalTokens,
|
|
2103
2218
|
};
|
|
2104
2219
|
}
|
|
2105
2220
|
async function runClaude(input) {
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2221
|
+
const playdropConfigPath = input.cleanRoom
|
|
2222
|
+
? null
|
|
2223
|
+
: (await prepareClaudeRunConfig({
|
|
2224
|
+
workspaceDir: input.stateDir ?? input.workspaceDir,
|
|
2225
|
+
workerUsername: input.workerUsername,
|
|
2226
|
+
envName: input.envName,
|
|
2227
|
+
})).playdropConfigPath;
|
|
2111
2228
|
const denyReadRoots = resolveClaudeDenyReadRoots(input.workspaceDir);
|
|
2112
2229
|
const screenshotCollector = input.enableChrome || input.enablePlaywrightMcp
|
|
2113
2230
|
? (0, instrument_evidence_1.createWorkerInstrumentScreenshotCollector)(input.workspaceDir)
|
|
2114
2231
|
: null;
|
|
2232
|
+
let observedClaudeSessionId = input.resumeSessionId?.trim() || null;
|
|
2233
|
+
let claudeSessionProbe = '';
|
|
2115
2234
|
let playwrightMcpConfigPath = null;
|
|
2116
2235
|
if (input.enablePlaywrightMcp) {
|
|
2117
2236
|
playwrightMcpConfigPath = node_path_1.default.join(input.workspaceDir, '.playdrop', 'claude-run', 'playwright-mcp.json');
|
|
@@ -2125,12 +2244,16 @@ async function runClaude(input) {
|
|
|
2125
2244
|
workspaceDir: input.workspaceDir,
|
|
2126
2245
|
denyReadRoots,
|
|
2127
2246
|
pluginDir: input.taskPluginRoot,
|
|
2247
|
+
playdropCliEnabled: !input.cleanRoom,
|
|
2128
2248
|
chrome: input.enableChrome,
|
|
2129
2249
|
playwrightMcp: input.enablePlaywrightMcp,
|
|
2130
2250
|
playwrightMcpConfigPath,
|
|
2251
|
+
resumeSessionId: input.resumeSessionId,
|
|
2131
2252
|
}),
|
|
2132
2253
|
cwd: input.workspaceDir,
|
|
2133
|
-
env: (0, runtime_1.
|
|
2254
|
+
env: input.cleanRoom ? (0, runtime_1.buildCleanRoomChildEnv)({
|
|
2255
|
+
binDir: input.binDir,
|
|
2256
|
+
}) : (0, runtime_1.buildWorkerChildEnv)({
|
|
2134
2257
|
binDir: input.binDir,
|
|
2135
2258
|
taskId: input.taskId,
|
|
2136
2259
|
taskToken: input.taskToken,
|
|
@@ -2138,7 +2261,7 @@ async function runClaude(input) {
|
|
|
2138
2261
|
envName: input.envName,
|
|
2139
2262
|
eventDir: input.eventDir,
|
|
2140
2263
|
devPort: input.devPort,
|
|
2141
|
-
playdropConfigPath,
|
|
2264
|
+
playdropConfigPath: playdropConfigPath,
|
|
2142
2265
|
playwrightBrowsersPath: resolveWorkerPlaywrightBrowsersPath(),
|
|
2143
2266
|
}),
|
|
2144
2267
|
stdin: `${JSON.stringify({
|
|
@@ -2158,9 +2281,15 @@ async function runClaude(input) {
|
|
|
2158
2281
|
],
|
|
2159
2282
|
},
|
|
2160
2283
|
})}\n`,
|
|
2161
|
-
timeoutMs: (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_CLAUDE_TIMEOUT_MS', runtime_1.DEFAULT_CODEX_TIMEOUT_MS),
|
|
2284
|
+
timeoutMs: input.timeoutMs ?? (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_CLAUDE_TIMEOUT_MS', runtime_1.DEFAULT_CODEX_TIMEOUT_MS),
|
|
2162
2285
|
maxOutputChars: runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS,
|
|
2163
|
-
onTranscriptChunks:
|
|
2286
|
+
onTranscriptChunks: async (chunks) => {
|
|
2287
|
+
if (!observedClaudeSessionId) {
|
|
2288
|
+
claudeSessionProbe = (claudeSessionProbe + chunks.map((chunk) => chunk.content).join('')).slice(-64000);
|
|
2289
|
+
observedClaudeSessionId = (0, runtime_1.extractClaudeSessionId)(claudeSessionProbe);
|
|
2290
|
+
}
|
|
2291
|
+
await input.onTranscriptChunks(chunks);
|
|
2292
|
+
},
|
|
2164
2293
|
...(screenshotCollector
|
|
2165
2294
|
? { onStdoutText: (text) => screenshotCollector.push(text) > 0 }
|
|
2166
2295
|
: {}),
|
|
@@ -2168,7 +2297,9 @@ async function runClaude(input) {
|
|
|
2168
2297
|
onChild: input.onChild,
|
|
2169
2298
|
});
|
|
2170
2299
|
screenshotCollector?.finish();
|
|
2171
|
-
|
|
2300
|
+
observedClaudeSessionId = observedClaudeSessionId
|
|
2301
|
+
?? (0, runtime_1.extractClaudeSessionId)(`${result.stdout}\n${result.stderr}\n${result.outputTail}`);
|
|
2302
|
+
return { ...result, providerSessionId: observedClaudeSessionId };
|
|
2172
2303
|
}
|
|
2173
2304
|
function isPathInside(parent, child) {
|
|
2174
2305
|
const relative = node_path_1.default.relative(parent, child);
|
|
@@ -2199,6 +2330,35 @@ function resolveClaudeDenyReadRoots(workspaceDir) {
|
|
|
2199
2330
|
}
|
|
2200
2331
|
// Raw stdout/stderr tails stay out of task results on purpose: transcripts are
|
|
2201
2332
|
// streamed separately and results can surface to creators (spec section 11).
|
|
2333
|
+
function mergeCreatorRecoveryResults(runtime, initial, recovery) {
|
|
2334
|
+
const sumNullable = (left, right) => (left === null && right === null ? null : (left ?? 0) + (right ?? 0));
|
|
2335
|
+
const tokenUsage = runtime === 'CODEX' && recovery.tokenUsage.totalTokens !== null
|
|
2336
|
+
? recovery.tokenUsage
|
|
2337
|
+
: {
|
|
2338
|
+
inputTokens: sumNullable(initial.tokenUsage.inputTokens, recovery.tokenUsage.inputTokens),
|
|
2339
|
+
outputTokens: sumNullable(initial.tokenUsage.outputTokens, recovery.tokenUsage.outputTokens),
|
|
2340
|
+
cacheCreationInputTokens: sumNullable(initial.tokenUsage.cacheCreationInputTokens, recovery.tokenUsage.cacheCreationInputTokens),
|
|
2341
|
+
cacheReadInputTokens: sumNullable(initial.tokenUsage.cacheReadInputTokens, recovery.tokenUsage.cacheReadInputTokens),
|
|
2342
|
+
totalTokens: sumNullable(initial.tokenUsage.totalTokens, recovery.tokenUsage.totalTokens),
|
|
2343
|
+
rawProviderUsage: {
|
|
2344
|
+
initial: initial.tokenUsage.rawProviderUsage,
|
|
2345
|
+
recovery: recovery.tokenUsage.rawProviderUsage,
|
|
2346
|
+
},
|
|
2347
|
+
usageParseError: initial.tokenUsage.totalTokens === null || recovery.tokenUsage.totalTokens === null
|
|
2348
|
+
? 'creator_recovery_usage_incomplete'
|
|
2349
|
+
: null,
|
|
2350
|
+
};
|
|
2351
|
+
const recoveryMarker = recovery.timedOut ? '\nagent_task_creation_recovery_timeout' : '';
|
|
2352
|
+
return {
|
|
2353
|
+
...recovery,
|
|
2354
|
+
stdout: `${initial.stdout}\n${recovery.stdout}`.slice(-runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS),
|
|
2355
|
+
stderr: `${initial.stderr}\n${recovery.stderr}${recoveryMarker}`.slice(-runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS),
|
|
2356
|
+
outputTail: `${initial.outputTail}\n${recovery.outputTail}${recoveryMarker}`.slice(-runtime_1.DEFAULT_CODEX_LOG_TAIL_CHARS),
|
|
2357
|
+
tokenUsage,
|
|
2358
|
+
tokensUsed: tokenUsage.totalTokens,
|
|
2359
|
+
providerSessionId: recovery.providerSessionId ?? initial.providerSessionId ?? null,
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2202
2362
|
function buildAgentRunResult(result) {
|
|
2203
2363
|
return {
|
|
2204
2364
|
exitCode: result.exitCode,
|
|
@@ -2326,6 +2486,12 @@ function extractProviderStatusCode(text) {
|
|
|
2326
2486
|
function buildAgentFailureCode(agent, result, request) {
|
|
2327
2487
|
const provider = providerNameForAgent(agent);
|
|
2328
2488
|
const combinedOutput = `${result.outputTail}\n${result.stderr}\n${result.stdout}`;
|
|
2489
|
+
if (combinedOutput.includes('agent_task_resume_session_missing')) {
|
|
2490
|
+
return `agent_task_resume_session_missing:${provider}`;
|
|
2491
|
+
}
|
|
2492
|
+
if (combinedOutput.includes('agent_task_creation_recovery_timeout')) {
|
|
2493
|
+
return `agent_task_creation_recovery_timeout:${provider}`;
|
|
2494
|
+
}
|
|
2329
2495
|
if (request
|
|
2330
2496
|
&& (/\b(?:unknown|invalid|unsupported)\s+(?:model|effort)\b/i.test(combinedOutput)
|
|
2331
2497
|
|| /\b(?:model|effort)\b.{0,80}\b(?:not found|not available|does not exist|is unavailable|is not supported)\b/i.test(combinedOutput))) {
|
|
@@ -2363,6 +2529,12 @@ function describeAgentFailureForEvent(errorCode) {
|
|
|
2363
2529
|
if (code === 'agent_provider_timeout') {
|
|
2364
2530
|
return `${providerLabel} timed out before completing the task. Retry this task when the provider is healthy.`;
|
|
2365
2531
|
}
|
|
2532
|
+
if (code === 'agent_task_resume_session_missing') {
|
|
2533
|
+
return `${providerLabel} timed out, but the worker could not recover the same conversation. The task was stopped without starting over.`;
|
|
2534
|
+
}
|
|
2535
|
+
if (code === 'agent_task_creation_recovery_timeout') {
|
|
2536
|
+
return `${providerLabel} did not finish during the final creation recovery window, so the task was stopped permanently.`;
|
|
2537
|
+
}
|
|
2366
2538
|
if (code === 'worker_shutdown') {
|
|
2367
2539
|
return 'The worker stopped while running this task. Start the worker again, then retry the task.';
|
|
2368
2540
|
}
|
|
@@ -2422,6 +2594,24 @@ async function createLocalPlaydropShim(input) {
|
|
|
2422
2594
|
}
|
|
2423
2595
|
return binDir;
|
|
2424
2596
|
}
|
|
2597
|
+
async function createStaticGameCleanToolPath(workspaceDir, runtime) {
|
|
2598
|
+
const binDir = node_path_1.default.join(workspaceDir, '.clean-tools');
|
|
2599
|
+
await (0, promises_1.rm)(binDir, { recursive: true, force: true });
|
|
2600
|
+
await (0, promises_1.mkdir)(binDir, { recursive: true });
|
|
2601
|
+
const commands = runtime === 'CODEX'
|
|
2602
|
+
? ['codex', 'node', 'npm', 'npx']
|
|
2603
|
+
: ['claude', 'node', 'npm', 'npx'];
|
|
2604
|
+
for (const command of commands) {
|
|
2605
|
+
const target = command === 'node'
|
|
2606
|
+
? node_process_1.default.execPath
|
|
2607
|
+
: (await execFileAsync('/usr/bin/which', [command])).stdout.trim();
|
|
2608
|
+
if (!target || !node_path_1.default.isAbsolute(target)) {
|
|
2609
|
+
throw new Error(`static_game_clean_tool_missing:${command}`);
|
|
2610
|
+
}
|
|
2611
|
+
(0, node_fs_1.symlinkSync)(target, node_path_1.default.join(binDir, command));
|
|
2612
|
+
}
|
|
2613
|
+
return binDir;
|
|
2614
|
+
}
|
|
2425
2615
|
function probeCodexInstallation() {
|
|
2426
2616
|
const probe = (0, shellProbe_1.runShell)((0, shellProbe_1.buildCommandPathProbe)('codex'));
|
|
2427
2617
|
if (probe.exitCode !== 0 || !probe.output.trim()) {
|
|
@@ -3799,7 +3989,7 @@ on run matchers
|
|
|
3799
3989
|
return "remaining=" & remainingCount & ";blank=" & blankWindowCount
|
|
3800
3990
|
end run
|
|
3801
3991
|
`;
|
|
3802
|
-
async function
|
|
3992
|
+
async function enforceInstrumentBrowserHygieneUnlocked(assignment, options = {}) {
|
|
3803
3993
|
if (assignment.agent.browser === 'NONE') {
|
|
3804
3994
|
return { remainingTaskTabs: 0, blankWindows: 0 };
|
|
3805
3995
|
}
|
|
@@ -3814,13 +4004,22 @@ async function enforceInstrumentBrowserHygiene(assignment, options = {}) {
|
|
|
3814
4004
|
const result = await execFileAsync(command, args, { timeout: 15000, maxBuffer: 1024 * 1024 });
|
|
3815
4005
|
return { stdout: result.stdout, stderr: result.stderr };
|
|
3816
4006
|
});
|
|
3817
|
-
let result;
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
4007
|
+
let result = null;
|
|
4008
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
4009
|
+
try {
|
|
4010
|
+
result = await runner('osascript', ['-e', INSTRUMENT_BROWSER_HYGIENE_APPLESCRIPT, ...matchers]);
|
|
4011
|
+
break;
|
|
4012
|
+
}
|
|
4013
|
+
catch (error) {
|
|
4014
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4015
|
+
if (!message.includes('Connection is invalid. (-609)') || attempt === 3) {
|
|
4016
|
+
throw new Error(`worker_browser_hygiene_automation_failed:${message}`);
|
|
4017
|
+
}
|
|
4018
|
+
await sleep(250);
|
|
4019
|
+
}
|
|
3823
4020
|
}
|
|
4021
|
+
if (!result)
|
|
4022
|
+
throw new Error('worker_browser_hygiene_automation_failed:no_result');
|
|
3824
4023
|
const match = /remaining=(\d+);blank=(\d+)/.exec(result.stdout.trim());
|
|
3825
4024
|
if (!match) {
|
|
3826
4025
|
throw new Error(`worker_browser_hygiene_invalid_result:${result.stdout.trim() || result.stderr.trim() || 'empty'}`);
|
|
@@ -3832,12 +4031,25 @@ async function enforceInstrumentBrowserHygiene(assignment, options = {}) {
|
|
|
3832
4031
|
}
|
|
3833
4032
|
return { remainingTaskTabs, blankWindows };
|
|
3834
4033
|
}
|
|
4034
|
+
let instrumentBrowserHygieneTail = Promise.resolve();
|
|
4035
|
+
async function enforceInstrumentBrowserHygiene(assignment, options = {}) {
|
|
4036
|
+
const result = instrumentBrowserHygieneTail.then(() => enforceInstrumentBrowserHygieneUnlocked(assignment, options));
|
|
4037
|
+
instrumentBrowserHygieneTail = result.then(() => undefined, () => undefined);
|
|
4038
|
+
return result;
|
|
4039
|
+
}
|
|
4040
|
+
function assignmentRequiresRegisteredBundle(assignment) {
|
|
4041
|
+
const playdropMetadata = assignment.task.metadata.playdrop;
|
|
4042
|
+
return Boolean(playdropMetadata
|
|
4043
|
+
&& typeof playdropMetadata === 'object'
|
|
4044
|
+
&& !Array.isArray(playdropMetadata)
|
|
4045
|
+
&& playdropMetadata.bundleSource === 'REGISTERED');
|
|
4046
|
+
}
|
|
3835
4047
|
function throwWorkerRuntimeError(error) {
|
|
3836
4048
|
throw error;
|
|
3837
4049
|
}
|
|
3838
4050
|
async function startWorker(options = {}) {
|
|
3839
4051
|
loadWorkerEnvFile(options.env);
|
|
3840
|
-
await (0, commandContext_1.withEnvironment)('worker start', 'Starting the PlayDrop worker', async ({ client, env }) => {
|
|
4052
|
+
await (0, commandContext_1.withEnvironment)('worker start', 'Starting the PlayDrop worker', async ({ client, env, envConfig, token }) => {
|
|
3841
4053
|
const devPluginWorkingTree = resolveDevPluginWorkingTree({
|
|
3842
4054
|
workingTree: options.devPluginWorkingTree,
|
|
3843
4055
|
env,
|
|
@@ -4033,6 +4245,7 @@ async function startWorker(options = {}) {
|
|
|
4033
4245
|
let heartbeatObservedFinalizedTask = false;
|
|
4034
4246
|
let retainWorkspace = false;
|
|
4035
4247
|
let workspaceDir = null;
|
|
4248
|
+
let agentWorkspaceDir = null;
|
|
4036
4249
|
let availableSkillPaths = [];
|
|
4037
4250
|
const observedSkillPaths = new Set();
|
|
4038
4251
|
const assignment = claimedAssignment;
|
|
@@ -4062,6 +4275,38 @@ async function startWorker(options = {}) {
|
|
|
4062
4275
|
});
|
|
4063
4276
|
telemetryReported = true;
|
|
4064
4277
|
};
|
|
4278
|
+
const failUploadedArtifactWithoutTaskDone = async () => {
|
|
4279
|
+
const failureCode = 'agent_exited_after_upload_without_task_done';
|
|
4280
|
+
retainWorkspace = true;
|
|
4281
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4282
|
+
workerKey,
|
|
4283
|
+
leaseToken,
|
|
4284
|
+
kind: 'progress',
|
|
4285
|
+
phase: 'failed',
|
|
4286
|
+
message: 'The agent uploaded an artifact but exited without marking the task done.',
|
|
4287
|
+
pct: null,
|
|
4288
|
+
payload: { error: failureCode },
|
|
4289
|
+
});
|
|
4290
|
+
await failTaskWithRetry(client, task.id, {
|
|
4291
|
+
workerKey,
|
|
4292
|
+
leaseToken,
|
|
4293
|
+
error: failureCode,
|
|
4294
|
+
terminalReason: {
|
|
4295
|
+
kind: 'failure',
|
|
4296
|
+
category: 'agent_failure',
|
|
4297
|
+
code: failureCode,
|
|
4298
|
+
message: 'The agent uploaded an artifact but did not complete the task protocol.',
|
|
4299
|
+
retryable: false,
|
|
4300
|
+
rawError: failureCode,
|
|
4301
|
+
source: 'agent',
|
|
4302
|
+
},
|
|
4303
|
+
result: {
|
|
4304
|
+
stage: 'completion',
|
|
4305
|
+
uploadedArtifactPreserved: true,
|
|
4306
|
+
},
|
|
4307
|
+
});
|
|
4308
|
+
await reportTelemetry('FAILED');
|
|
4309
|
+
};
|
|
4065
4310
|
try {
|
|
4066
4311
|
const codexModel = assignment.agent.runtime === 'CODEX'
|
|
4067
4312
|
? resolveCodexModel(assignment.agent.model, assignment.agent.reasoningEffort)
|
|
@@ -4071,24 +4316,35 @@ async function startWorker(options = {}) {
|
|
|
4071
4316
|
: null;
|
|
4072
4317
|
resolvedRuntimeModel = codexModel?.model ?? claudeModel?.model ?? assignment.agent.model;
|
|
4073
4318
|
reasoningEffort = codexModel?.reasoningEffort ?? claudeModel?.effort ?? null;
|
|
4319
|
+
const cleanRoom = task.kind === 'STATIC_GAME';
|
|
4074
4320
|
workspaceDir = workerTaskWorkspaceDirFromAssignment(assignment);
|
|
4075
|
-
await
|
|
4076
|
-
await (0, promises_1.rm)(workspaceDir, { recursive: true, force: true });
|
|
4077
|
-
await (0, promises_1.mkdir)(workspaceDir, { recursive: true });
|
|
4078
|
-
const eventDir = node_path_1.default.join(workspaceDir, '.playdrop', 'task-events');
|
|
4079
|
-
await (0, promises_1.mkdir)(eventDir, { recursive: true });
|
|
4080
|
-
await stageWorkerTaskContext({
|
|
4081
|
-
workspaceDir,
|
|
4082
|
-
env,
|
|
4083
|
-
devPort,
|
|
4084
|
-
taskContext,
|
|
4085
|
-
});
|
|
4086
|
-
const binDir = await createLocalPlaydropShim({
|
|
4321
|
+
const preparedWorkspace = await prepareClaimedTaskWorkspace({
|
|
4087
4322
|
workspaceDir,
|
|
4088
|
-
|
|
4089
|
-
attempt: taskContext.attempt,
|
|
4090
|
-
devPort,
|
|
4323
|
+
attempt: task.attempts,
|
|
4091
4324
|
});
|
|
4325
|
+
if (preparedWorkspace.resumed) {
|
|
4326
|
+
console.log(`Agent task ${task.id} retry resumed retained workspace attempt ${task.attempts}.`);
|
|
4327
|
+
}
|
|
4328
|
+
agentWorkspaceDir = cleanRoom ? node_path_1.default.join(workspaceDir, 'project') : workspaceDir;
|
|
4329
|
+
await (0, promises_1.mkdir)(agentWorkspaceDir, { recursive: true });
|
|
4330
|
+
const eventDir = node_path_1.default.join(workspaceDir, '.playdrop', 'task-events');
|
|
4331
|
+
await (0, promises_1.mkdir)(eventDir, { recursive: true });
|
|
4332
|
+
if (!cleanRoom) {
|
|
4333
|
+
await stageWorkerTaskContext({
|
|
4334
|
+
workspaceDir,
|
|
4335
|
+
env,
|
|
4336
|
+
devPort,
|
|
4337
|
+
taskContext,
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
const binDir = cleanRoom
|
|
4341
|
+
? await createStaticGameCleanToolPath(workspaceDir, assignment.agent.runtime)
|
|
4342
|
+
: await createLocalPlaydropShim({
|
|
4343
|
+
workspaceDir,
|
|
4344
|
+
taskId: taskContext.taskId,
|
|
4345
|
+
attempt: taskContext.attempt,
|
|
4346
|
+
devPort,
|
|
4347
|
+
});
|
|
4092
4348
|
const handleEventDrainFailure = (error) => {
|
|
4093
4349
|
const classification = classifyWorkerEventDrainError(error);
|
|
4094
4350
|
if (classification === 'session_expired') {
|
|
@@ -4120,17 +4376,59 @@ async function startWorker(options = {}) {
|
|
|
4120
4376
|
appendObservedSkillPathsFromTranscript(chunks);
|
|
4121
4377
|
await appendWorkerTranscriptChunks({ client, taskId: task.id, workerKey, leaseToken, chunks });
|
|
4122
4378
|
};
|
|
4123
|
-
await stageAssignmentWorkspaceV2(
|
|
4124
|
-
const assignmentPluginRoot =
|
|
4125
|
-
|
|
4379
|
+
await stageAssignmentWorkspaceV2(agentWorkspaceDir, assignment.workspace);
|
|
4380
|
+
const assignmentPluginRoot = !cleanRoom
|
|
4381
|
+
&& devPluginWorkingTree
|
|
4382
|
+
&& !assignmentRequiresRegisteredBundle(assignment)
|
|
4383
|
+
? await stageDevPluginWorkingTree({
|
|
4384
|
+
workingTree: devPluginWorkingTree,
|
|
4385
|
+
workspaceDir: agentWorkspaceDir,
|
|
4386
|
+
taskKind: task.kind,
|
|
4387
|
+
})
|
|
4126
4388
|
: assignment.plugin
|
|
4127
4389
|
? await stageAssignmentPluginBundle({
|
|
4128
|
-
workspaceDir,
|
|
4390
|
+
workspaceDir: agentWorkspaceDir,
|
|
4391
|
+
extractionDir: workspaceDir,
|
|
4129
4392
|
pluginBundle: assignment.plugin,
|
|
4130
4393
|
taskKind: task.kind,
|
|
4131
4394
|
})
|
|
4132
4395
|
: null;
|
|
4133
4396
|
availableSkillPaths = listPluginSkillPaths(assignmentPluginRoot);
|
|
4397
|
+
if (cleanRoom) {
|
|
4398
|
+
const playdropMetadata = assignment.task.metadata.playdrop;
|
|
4399
|
+
const postAgentAction = playdropMetadata && typeof playdropMetadata === 'object' && !Array.isArray(playdropMetadata)
|
|
4400
|
+
? playdropMetadata.postAgentAction
|
|
4401
|
+
: null;
|
|
4402
|
+
const publishInstruction = postAgentAction && typeof postAgentAction === 'object' && !Array.isArray(postAgentAction)
|
|
4403
|
+
? postAgentAction
|
|
4404
|
+
: null;
|
|
4405
|
+
if (!assignment.plugin
|
|
4406
|
+
|| assignmentPluginRoot !== null
|
|
4407
|
+
|| assignment.agent.browser !== 'NONE'
|
|
4408
|
+
|| publishInstruction?.type !== 'PUBLISH_STATIC_GAME'
|
|
4409
|
+
|| publishInstruction.entryPoint !== 'index.html') {
|
|
4410
|
+
throw new Error('static_game_clean_room_contract_invalid');
|
|
4411
|
+
}
|
|
4412
|
+
availableSkillPaths = [];
|
|
4413
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4414
|
+
workerKey,
|
|
4415
|
+
leaseToken,
|
|
4416
|
+
kind: 'system',
|
|
4417
|
+
phase: 'clean_room',
|
|
4418
|
+
message: 'Static game clean-room capability evidence recorded.',
|
|
4419
|
+
payload: {
|
|
4420
|
+
bundleHash: assignment.plugin.sha256,
|
|
4421
|
+
requestedModel: assignment.agent.model,
|
|
4422
|
+
resolvedModel: resolvedRuntimeModel,
|
|
4423
|
+
networkAccess: true,
|
|
4424
|
+
browser: assignment.agent.browser,
|
|
4425
|
+
availableSkills: [],
|
|
4426
|
+
pluginInstalled: false,
|
|
4427
|
+
playdropCliInPath: false,
|
|
4428
|
+
taskCredentialsExposed: false,
|
|
4429
|
+
},
|
|
4430
|
+
});
|
|
4431
|
+
}
|
|
4134
4432
|
heartbeatTimer = setInterval(() => {
|
|
4135
4433
|
client.workerHeartbeatAgentTask(task.id, { workerKey, leaseToken })
|
|
4136
4434
|
.then((heartbeat) => {
|
|
@@ -4189,81 +4487,161 @@ async function startWorker(options = {}) {
|
|
|
4189
4487
|
handleEventDrainFailure(error);
|
|
4190
4488
|
});
|
|
4191
4489
|
}, 1000);
|
|
4192
|
-
const
|
|
4490
|
+
const gameCreationTask = usesCreatorRecovery(task.kind);
|
|
4491
|
+
const startsInCreationRecovery = gameCreationTask && preparedWorkspace.resumed;
|
|
4492
|
+
const prompt = startsInCreationRecovery
|
|
4493
|
+
? buildCreatorRecoveryPrompt({
|
|
4494
|
+
prompt: assignment.request.prompt,
|
|
4495
|
+
kind: task.kind,
|
|
4496
|
+
})
|
|
4497
|
+
: task.kind === 'GAME_EVAL' && preparedWorkspace.resumed
|
|
4498
|
+
? buildGameEvalRetryPrompt(assignment.request.prompt)
|
|
4499
|
+
: assignment.request.prompt;
|
|
4193
4500
|
const imageAttachments = assignment.request.attachments
|
|
4194
4501
|
.filter((attachment) => attachment.contentType.startsWith('image/'))
|
|
4195
4502
|
.map((attachment) => ({
|
|
4196
|
-
path: resolveWorkspaceFileDestination(
|
|
4503
|
+
path: resolveWorkspaceFileDestination(agentWorkspaceDir, attachment.path),
|
|
4197
4504
|
contentType: attachment.contentType,
|
|
4198
4505
|
}));
|
|
4199
4506
|
if (shuttingDown) {
|
|
4200
4507
|
throw new Error('worker_shutdown');
|
|
4201
4508
|
}
|
|
4509
|
+
const runAssignedAgent = async (input) => {
|
|
4510
|
+
if (assignment.agent.runtime === 'CODEX') {
|
|
4511
|
+
return runCodex({
|
|
4512
|
+
workspaceDir: agentWorkspaceDir,
|
|
4513
|
+
stateDir: cleanRoom ? workspaceDir : undefined,
|
|
4514
|
+
binDir,
|
|
4515
|
+
eventDir,
|
|
4516
|
+
prompt: input.prompt,
|
|
4517
|
+
envName: env,
|
|
4518
|
+
workerUsername: username,
|
|
4519
|
+
taskId: task.id,
|
|
4520
|
+
taskToken: assignment.task.token,
|
|
4521
|
+
attempt: taskContext.attempt,
|
|
4522
|
+
devPort,
|
|
4523
|
+
codexModel: codexModel ?? resolveCodexModel(assignment.agent.model),
|
|
4524
|
+
taskPluginRoot: assignmentPluginRoot,
|
|
4525
|
+
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4526
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4527
|
+
imagePaths: imageAttachments.map((attachment) => attachment.path),
|
|
4528
|
+
onTranscriptChunks: async (chunks) => {
|
|
4529
|
+
await appendTranscriptChunks(chunks);
|
|
4530
|
+
},
|
|
4531
|
+
onChild: (controls) => {
|
|
4532
|
+
activeTerminators.set(task.id, controls.terminate);
|
|
4533
|
+
},
|
|
4534
|
+
cleanRoom,
|
|
4535
|
+
timeoutMs: input.timeoutMs,
|
|
4536
|
+
resumeSessionId: input.resumeSessionId,
|
|
4537
|
+
});
|
|
4538
|
+
}
|
|
4539
|
+
if (assignment.agent.runtime === 'CLAUDE_CODE') {
|
|
4540
|
+
return runClaude({
|
|
4541
|
+
workspaceDir: agentWorkspaceDir,
|
|
4542
|
+
stateDir: cleanRoom ? workspaceDir : undefined,
|
|
4543
|
+
binDir,
|
|
4544
|
+
eventDir,
|
|
4545
|
+
prompt: input.prompt,
|
|
4546
|
+
enableChrome: assignment.agent.browser === 'CHROME',
|
|
4547
|
+
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4548
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4549
|
+
envName: env,
|
|
4550
|
+
workerUsername: username,
|
|
4551
|
+
taskId: task.id,
|
|
4552
|
+
taskToken: assignment.task.token,
|
|
4553
|
+
attempt: taskContext.attempt,
|
|
4554
|
+
devPort,
|
|
4555
|
+
claudeModel: claudeModel ?? resolveClaudeModel(assignment.agent.model),
|
|
4556
|
+
taskPluginRoot: assignmentPluginRoot,
|
|
4557
|
+
imageAttachments,
|
|
4558
|
+
onTranscriptChunks: async (chunks) => {
|
|
4559
|
+
await appendTranscriptChunks(chunks);
|
|
4560
|
+
},
|
|
4561
|
+
onChild: (controls) => {
|
|
4562
|
+
activeTerminators.set(task.id, controls.terminate);
|
|
4563
|
+
},
|
|
4564
|
+
cleanRoom,
|
|
4565
|
+
timeoutMs: input.timeoutMs,
|
|
4566
|
+
resumeSessionId: input.resumeSessionId,
|
|
4567
|
+
});
|
|
4568
|
+
}
|
|
4569
|
+
if (assignment.agent.runtime === 'CURSOR_COMPOSER') {
|
|
4570
|
+
await client.workerRecordAgentTaskAttemptUnavailable(task.id, {
|
|
4571
|
+
workerKey,
|
|
4572
|
+
leaseToken,
|
|
4573
|
+
attemptIndex: 0,
|
|
4574
|
+
reason: 'unavailable_runtime',
|
|
4575
|
+
message: 'Cursor Composer noninteractive worker execution is not configured on this worker.',
|
|
4576
|
+
});
|
|
4577
|
+
fenced = true;
|
|
4578
|
+
throw new Error('worker_cursor_composer_runner_not_configured');
|
|
4579
|
+
}
|
|
4580
|
+
throw new Error(`unsupported_worker_agent:${assignment.agent.runtime}`);
|
|
4581
|
+
};
|
|
4202
4582
|
agentStartedAt = new Date();
|
|
4203
|
-
if (
|
|
4204
|
-
|
|
4205
|
-
workspaceDir,
|
|
4206
|
-
binDir,
|
|
4207
|
-
eventDir,
|
|
4208
|
-
prompt,
|
|
4209
|
-
envName: env,
|
|
4210
|
-
workerUsername: username,
|
|
4211
|
-
taskId: task.id,
|
|
4212
|
-
taskToken: assignment.task.token,
|
|
4213
|
-
attempt: taskContext.attempt,
|
|
4214
|
-
devPort,
|
|
4215
|
-
codexModel: codexModel ?? resolveCodexModel(assignment.agent.model),
|
|
4216
|
-
taskPluginRoot: assignmentPluginRoot,
|
|
4217
|
-
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4218
|
-
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4219
|
-
imagePaths: imageAttachments.map((attachment) => attachment.path),
|
|
4220
|
-
onTranscriptChunks: async (chunks) => {
|
|
4221
|
-
await appendTranscriptChunks(chunks);
|
|
4222
|
-
},
|
|
4223
|
-
onChild: (controls) => {
|
|
4224
|
-
activeTerminators.set(task.id, controls.terminate);
|
|
4225
|
-
},
|
|
4226
|
-
});
|
|
4227
|
-
}
|
|
4228
|
-
else if (assignment.agent.runtime === 'CLAUDE_CODE') {
|
|
4229
|
-
agentResult = await runClaude({
|
|
4230
|
-
workspaceDir,
|
|
4231
|
-
binDir,
|
|
4232
|
-
eventDir,
|
|
4233
|
-
prompt,
|
|
4234
|
-
enableChrome: assignment.agent.browser === 'CHROME',
|
|
4235
|
-
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4236
|
-
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4237
|
-
envName: env,
|
|
4238
|
-
workerUsername: username,
|
|
4239
|
-
taskId: task.id,
|
|
4240
|
-
taskToken: assignment.task.token,
|
|
4241
|
-
attempt: taskContext.attempt,
|
|
4242
|
-
devPort,
|
|
4243
|
-
claudeModel: claudeModel ?? resolveClaudeModel(assignment.agent.model),
|
|
4244
|
-
taskPluginRoot: assignmentPluginRoot,
|
|
4245
|
-
imageAttachments,
|
|
4246
|
-
onTranscriptChunks: async (chunks) => {
|
|
4247
|
-
await appendTranscriptChunks(chunks);
|
|
4248
|
-
},
|
|
4249
|
-
onChild: (controls) => {
|
|
4250
|
-
activeTerminators.set(task.id, controls.terminate);
|
|
4251
|
-
},
|
|
4252
|
-
});
|
|
4253
|
-
}
|
|
4254
|
-
else if (assignment.agent.runtime === 'CURSOR_COMPOSER') {
|
|
4255
|
-
await client.workerRecordAgentTaskAttemptUnavailable(task.id, {
|
|
4583
|
+
if (startsInCreationRecovery) {
|
|
4584
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4256
4585
|
workerKey,
|
|
4257
4586
|
leaseToken,
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
message: '
|
|
4587
|
+
kind: 'progress',
|
|
4588
|
+
phase: 'creation_recovery',
|
|
4589
|
+
message: 'Resumed the retained game workspace with a focused creation recovery window.',
|
|
4261
4590
|
});
|
|
4262
|
-
fenced = true;
|
|
4263
|
-
throw new Error('worker_cursor_composer_runner_not_configured');
|
|
4264
4591
|
}
|
|
4265
|
-
|
|
4266
|
-
|
|
4592
|
+
agentResult = await runAssignedAgent({
|
|
4593
|
+
prompt,
|
|
4594
|
+
timeoutMs: startsInCreationRecovery
|
|
4595
|
+
? runtime_1.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS
|
|
4596
|
+
: gameCreationTask
|
|
4597
|
+
? runtime_1.DEFAULT_CREATOR_AGENT_TIMEOUT_MS
|
|
4598
|
+
: undefined,
|
|
4599
|
+
});
|
|
4600
|
+
if (startsInCreationRecovery && agentResult.timedOut) {
|
|
4601
|
+
const marker = 'agent_task_creation_recovery_timeout';
|
|
4602
|
+
agentResult = {
|
|
4603
|
+
...agentResult,
|
|
4604
|
+
stderr: `${agentResult.stderr}\n${marker}`.trim(),
|
|
4605
|
+
outputTail: `${agentResult.outputTail}\n${marker}`.trim(),
|
|
4606
|
+
};
|
|
4607
|
+
}
|
|
4608
|
+
if (gameCreationTask
|
|
4609
|
+
&& !startsInCreationRecovery
|
|
4610
|
+
&& agentResult.timedOut
|
|
4611
|
+
&& !fenced
|
|
4612
|
+
&& !shuttingDown) {
|
|
4613
|
+
const timedOutResult = agentResult;
|
|
4614
|
+
const refreshedAfterTimeout = await fetchTaskDetail(client, target, task.id);
|
|
4615
|
+
if (refreshedAfterTimeout.task.status === 'RUNNING'
|
|
4616
|
+
&& !hasAgentTaskUploadedArtifact(refreshedAfterTimeout.task)) {
|
|
4617
|
+
const providerSessionId = timedOutResult.providerSessionId?.trim() ?? '';
|
|
4618
|
+
if (!providerSessionId) {
|
|
4619
|
+
const marker = 'agent_task_resume_session_missing';
|
|
4620
|
+
agentResult = {
|
|
4621
|
+
...timedOutResult,
|
|
4622
|
+
stderr: `${timedOutResult.stderr}\n${marker}`.trim(),
|
|
4623
|
+
outputTail: `${timedOutResult.outputTail}\n${marker}`.trim(),
|
|
4624
|
+
};
|
|
4625
|
+
}
|
|
4626
|
+
else {
|
|
4627
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4628
|
+
workerKey,
|
|
4629
|
+
leaseToken,
|
|
4630
|
+
kind: 'progress',
|
|
4631
|
+
phase: 'creation_recovery',
|
|
4632
|
+
message: 'The one-hour creation ceiling was reached. Resuming the same agent conversation to finish the existing game.',
|
|
4633
|
+
});
|
|
4634
|
+
const recoveryResult = await runAssignedAgent({
|
|
4635
|
+
prompt: buildCreatorRecoveryPrompt({
|
|
4636
|
+
prompt: assignment.request.prompt,
|
|
4637
|
+
kind: task.kind,
|
|
4638
|
+
}),
|
|
4639
|
+
timeoutMs: runtime_1.DEFAULT_CREATOR_UPLOAD_RECOVERY_TIMEOUT_MS,
|
|
4640
|
+
resumeSessionId: providerSessionId,
|
|
4641
|
+
});
|
|
4642
|
+
agentResult = mergeCreatorRecoveryResults(assignment.agent.runtime, timedOutResult, recoveryResult);
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4267
4645
|
}
|
|
4268
4646
|
agentCompletedAt = new Date();
|
|
4269
4647
|
await queueEventDrain().catch((error) => {
|
|
@@ -4309,16 +4687,8 @@ async function startWorker(options = {}) {
|
|
|
4309
4687
|
await reportTelemetry(refreshed.task.status);
|
|
4310
4688
|
}
|
|
4311
4689
|
else if (hasAgentTaskUploadedArtifact(refreshed.task)) {
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4315
|
-
workerKey,
|
|
4316
|
-
leaseToken,
|
|
4317
|
-
kind: 'progress',
|
|
4318
|
-
phase: 'upload',
|
|
4319
|
-
message: 'Agent exited after upload before task done; manual completion repair required.',
|
|
4320
|
-
});
|
|
4321
|
-
await reportTelemetry('RUNNING');
|
|
4690
|
+
console.error(`Agent task ${task.id} uploaded an artifact but exited before task completion; failing it explicitly.`);
|
|
4691
|
+
await failUploadedArtifactWithoutTaskDone();
|
|
4322
4692
|
}
|
|
4323
4693
|
else {
|
|
4324
4694
|
retainWorkspace = true;
|
|
@@ -4352,17 +4722,158 @@ async function startWorker(options = {}) {
|
|
|
4352
4722
|
console.error(`Agent task ${task.id} was finalized by the agent as ${refreshed.task.status}.`);
|
|
4353
4723
|
await reportTelemetry(refreshed.task.status);
|
|
4354
4724
|
}
|
|
4355
|
-
else if (
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
leaseToken,
|
|
4361
|
-
kind: 'progress',
|
|
4362
|
-
phase: 'upload',
|
|
4363
|
-
message: 'Agent exited after upload before task done; manual completion repair required.',
|
|
4725
|
+
else if (task.kind === 'STATIC_GAME') {
|
|
4726
|
+
const tokenCap = (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_TOKEN_CAP', runtime_1.DEFAULT_WORKER_TOKEN_CAP);
|
|
4727
|
+
(0, runtime_1.assertWorkerTokenUsageWithinCap)({
|
|
4728
|
+
tokensUsed: agentResult.tokensUsed,
|
|
4729
|
+
tokenCap,
|
|
4364
4730
|
});
|
|
4365
|
-
await reportTelemetry('
|
|
4731
|
+
await reportTelemetry('DONE');
|
|
4732
|
+
try {
|
|
4733
|
+
let appId = refreshed.task.appId;
|
|
4734
|
+
let appVersionId = refreshed.task.appVersionId;
|
|
4735
|
+
let result;
|
|
4736
|
+
if (Number.isInteger(appId) && Number(appId) > 0
|
|
4737
|
+
&& Number.isInteger(appVersionId) && Number(appVersionId) > 0) {
|
|
4738
|
+
result = {
|
|
4739
|
+
appId,
|
|
4740
|
+
appVersionId,
|
|
4741
|
+
appName: refreshed.task.claimedAppName,
|
|
4742
|
+
version: refreshed.task.outputVersion,
|
|
4743
|
+
completedBy: 'worker_static_game_retry',
|
|
4744
|
+
};
|
|
4745
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4746
|
+
workerKey,
|
|
4747
|
+
leaseToken,
|
|
4748
|
+
kind: 'system',
|
|
4749
|
+
phase: 'static_game_publish_retry',
|
|
4750
|
+
message: 'Reused the static game version already linked to this task.',
|
|
4751
|
+
});
|
|
4752
|
+
}
|
|
4753
|
+
else {
|
|
4754
|
+
const primarySurface = resolveWorkerInstrumentSurface(taskContext);
|
|
4755
|
+
if (!primarySurface) {
|
|
4756
|
+
throw new Error('static_game_artifact_primary_surface_missing');
|
|
4757
|
+
}
|
|
4758
|
+
if (!agentWorkspaceDir) {
|
|
4759
|
+
throw new Error('static_game_artifact_workspace_missing');
|
|
4760
|
+
}
|
|
4761
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4762
|
+
workerKey,
|
|
4763
|
+
leaseToken,
|
|
4764
|
+
kind: 'system',
|
|
4765
|
+
phase: 'static_game_publish_started',
|
|
4766
|
+
message: 'Publishing the clean-room static game output.',
|
|
4767
|
+
});
|
|
4768
|
+
const taskClient = (0, apiClient_1.createCliApiClient)({
|
|
4769
|
+
baseUrl: envConfig.apiBase,
|
|
4770
|
+
token,
|
|
4771
|
+
onBehalfCreatorUsername: taskContext.creatorUsername,
|
|
4772
|
+
agentTaskToken: assignment.task.token,
|
|
4773
|
+
agentTaskId: task.id,
|
|
4774
|
+
agentTaskAttempt: taskContext.attempt,
|
|
4775
|
+
});
|
|
4776
|
+
const published = await (0, upload_1.publishStaticHtmlProject)({
|
|
4777
|
+
client: taskClient,
|
|
4778
|
+
taskId: task.id,
|
|
4779
|
+
taskToken: assignment.task.token,
|
|
4780
|
+
projectDir: agentWorkspaceDir,
|
|
4781
|
+
creatorUsername: taskContext.creatorUsername,
|
|
4782
|
+
primarySurface,
|
|
4783
|
+
apiBase: envConfig.apiBase,
|
|
4784
|
+
webBase: envConfig.webBase ?? null,
|
|
4785
|
+
claimedAppName: refreshed.task.claimedAppName,
|
|
4786
|
+
devRouterPort: devPort,
|
|
4787
|
+
token,
|
|
4788
|
+
user: me.user,
|
|
4789
|
+
});
|
|
4790
|
+
appId = published.appId;
|
|
4791
|
+
appVersionId = published.appVersionId;
|
|
4792
|
+
result = {
|
|
4793
|
+
appId,
|
|
4794
|
+
appVersionId,
|
|
4795
|
+
appName: published.appName,
|
|
4796
|
+
version: published.version,
|
|
4797
|
+
versionNodeId: published.versionNodeId,
|
|
4798
|
+
creatorUsername: published.creatorUsername,
|
|
4799
|
+
sourceHash: published.sourceHash,
|
|
4800
|
+
runtimeHash: published.runtimeHash,
|
|
4801
|
+
playUrl: published.playUrl,
|
|
4802
|
+
sourceContainsInjectedSdk: published.sourceContainsInjectedSdk,
|
|
4803
|
+
runtimeContainsInjectedSdk: published.runtimeContainsInjectedSdk,
|
|
4804
|
+
hostedLoadCheckPassed: published.hostedLoadCheckPassed,
|
|
4805
|
+
versionVisibility: 'PRIVATE',
|
|
4806
|
+
warnings: published.warnings,
|
|
4807
|
+
completedBy: 'worker_static_game_publish',
|
|
4808
|
+
};
|
|
4809
|
+
}
|
|
4810
|
+
if (!Number.isInteger(appId) || Number(appId) <= 0 || !Number.isInteger(appVersionId) || Number(appVersionId) <= 0) {
|
|
4811
|
+
throw new Error('static_game_publish_linked_version_missing');
|
|
4812
|
+
}
|
|
4813
|
+
await client.workerCompleteAgentTask(task.id, {
|
|
4814
|
+
workerKey,
|
|
4815
|
+
leaseToken,
|
|
4816
|
+
appId: Number(appId),
|
|
4817
|
+
appVersionId: Number(appVersionId),
|
|
4818
|
+
result,
|
|
4819
|
+
summary: 'Created and privately hosted a standalone static web game.',
|
|
4820
|
+
tokensUsed: agentResult.tokensUsed,
|
|
4821
|
+
});
|
|
4822
|
+
retainWorkspace = false;
|
|
4823
|
+
console.log(`Agent task ${task.id} published and completed as STATIC_GAME.`);
|
|
4824
|
+
}
|
|
4825
|
+
catch (error) {
|
|
4826
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
4827
|
+
const artifactFailure = rawMessage.startsWith('static_game_artifact_');
|
|
4828
|
+
const failureCode = artifactFailure
|
|
4829
|
+
? rawMessage.split(':', 1)[0]
|
|
4830
|
+
: 'static_game_publish_failed';
|
|
4831
|
+
await client.workerCreateAgentTaskEvent(task.id, {
|
|
4832
|
+
workerKey,
|
|
4833
|
+
leaseToken,
|
|
4834
|
+
kind: 'system',
|
|
4835
|
+
phase: artifactFailure ? 'static_game_artifact_failed' : 'static_game_publish_failed',
|
|
4836
|
+
message: artifactFailure
|
|
4837
|
+
? `Static game output was invalid: ${rawMessage}`
|
|
4838
|
+
: `Static game publishing failed: ${rawMessage}`,
|
|
4839
|
+
payload: { error: failureCode, detail: rawMessage },
|
|
4840
|
+
});
|
|
4841
|
+
retainWorkspace = true;
|
|
4842
|
+
const terminalReason = artifactFailure
|
|
4843
|
+
? {
|
|
4844
|
+
kind: 'failure',
|
|
4845
|
+
category: 'upload_validation',
|
|
4846
|
+
code: failureCode,
|
|
4847
|
+
message: 'The static game output did not pass validation.',
|
|
4848
|
+
retryable: false,
|
|
4849
|
+
rawError: rawMessage,
|
|
4850
|
+
source: 'worker',
|
|
4851
|
+
}
|
|
4852
|
+
: {
|
|
4853
|
+
kind: 'failure',
|
|
4854
|
+
category: 'instrument_error',
|
|
4855
|
+
code: failureCode,
|
|
4856
|
+
message: 'The platform failed to publish the static game.',
|
|
4857
|
+
retryable: true,
|
|
4858
|
+
rawError: rawMessage,
|
|
4859
|
+
source: 'worker',
|
|
4860
|
+
};
|
|
4861
|
+
await failTaskWithRetry(client, task.id, {
|
|
4862
|
+
workerKey,
|
|
4863
|
+
leaseToken,
|
|
4864
|
+
error: failureCode,
|
|
4865
|
+
terminalReason,
|
|
4866
|
+
result: {
|
|
4867
|
+
stage: artifactFailure ? 'artifact' : 'publish',
|
|
4868
|
+
error: rawMessage,
|
|
4869
|
+
agentRunSucceeded: true,
|
|
4870
|
+
},
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
else if (hasAgentTaskUploadedArtifact(refreshed.task)) {
|
|
4875
|
+
console.error(`Agent task ${task.id} uploaded an artifact but exited before task completion; failing it explicitly.`);
|
|
4876
|
+
await failUploadedArtifactWithoutTaskDone();
|
|
4366
4877
|
}
|
|
4367
4878
|
else {
|
|
4368
4879
|
const tokenCap = (0, runtime_1.readPositiveEnvInt)('PLAYDROP_WORKER_TOKEN_CAP', runtime_1.DEFAULT_WORKER_TOKEN_CAP);
|
|
@@ -4716,6 +5227,9 @@ async function resolveTaskCommandContext(command, optionsEnv, taskContext) {
|
|
|
4716
5227
|
}
|
|
4717
5228
|
async function uploadTask(options = {}) {
|
|
4718
5229
|
const taskContext = readTaskContextFile();
|
|
5230
|
+
if (taskContext.kind === 'STATIC_GAME') {
|
|
5231
|
+
throw new Error('task_upload_not_allowed_for_static_game');
|
|
5232
|
+
}
|
|
4719
5233
|
if (taskContext.kind === 'GAME_REVIEW' || taskContext.kind === 'GAME_EVAL') {
|
|
4720
5234
|
throw new Error('task_upload_not_allowed_for_game_review');
|
|
4721
5235
|
}
|
|
@@ -4828,6 +5342,7 @@ async function completeTask(options) {
|
|
|
4828
5342
|
if (nextSteps === NEXT_STEPS_READ_FAILED) {
|
|
4829
5343
|
return;
|
|
4830
5344
|
}
|
|
5345
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
4831
5346
|
try {
|
|
4832
5347
|
await ctx.client.workerCompleteAgentTask(taskContext.taskId, {
|
|
4833
5348
|
taskToken: taskContext.taskToken,
|
|
@@ -4844,6 +5359,7 @@ async function completeTask(options) {
|
|
|
4844
5359
|
},
|
|
4845
5360
|
summary,
|
|
4846
5361
|
...(nextSteps !== undefined ? { nextSteps } : {}),
|
|
5362
|
+
...(platformFeedback ? { platformFeedback } : {}),
|
|
4847
5363
|
});
|
|
4848
5364
|
}
|
|
4849
5365
|
catch (error) {
|
|
@@ -4858,6 +5374,7 @@ async function completeTask(options) {
|
|
|
4858
5374
|
appName: uploadResult.appName,
|
|
4859
5375
|
version: uploadResult.version,
|
|
4860
5376
|
},
|
|
5377
|
+
...(platformFeedback ? { platformFeedback } : {}),
|
|
4861
5378
|
});
|
|
4862
5379
|
}
|
|
4863
5380
|
throw error;
|
|
@@ -4903,6 +5420,27 @@ function readOptionalTextFile(filePath) {
|
|
|
4903
5420
|
const content = (0, node_fs_1.readFileSync)(resolved, 'utf8').trim();
|
|
4904
5421
|
return content || null;
|
|
4905
5422
|
}
|
|
5423
|
+
function readPlatformFeedbackFile(filePath) {
|
|
5424
|
+
const normalizedPath = typeof filePath === 'string' ? filePath.trim() : '';
|
|
5425
|
+
if (!normalizedPath) {
|
|
5426
|
+
return null;
|
|
5427
|
+
}
|
|
5428
|
+
try {
|
|
5429
|
+
const resolved = node_path_1.default.resolve(normalizedPath);
|
|
5430
|
+
if (!(0, node_fs_1.existsSync)(resolved)) {
|
|
5431
|
+
throw new Error('file_missing');
|
|
5432
|
+
}
|
|
5433
|
+
const content = (0, node_fs_1.readFileSync)(resolved, 'utf8').trim();
|
|
5434
|
+
if (!content || content.length > 10000) {
|
|
5435
|
+
throw new Error(content ? 'file_too_long' : 'file_empty');
|
|
5436
|
+
}
|
|
5437
|
+
return content;
|
|
5438
|
+
}
|
|
5439
|
+
catch (error) {
|
|
5440
|
+
console.warn(`Platform feedback was not attached: ${error instanceof Error ? error.message : String(error)}`);
|
|
5441
|
+
return null;
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
4906
5444
|
function contentTypeForEvidenceFile(fileName) {
|
|
4907
5445
|
const extension = node_path_1.default.extname(fileName).toLowerCase();
|
|
4908
5446
|
if (extension === '.png')
|
|
@@ -4981,6 +5519,7 @@ async function submitReviewTask(options) {
|
|
|
4981
5519
|
const creatorFeedback = state === 'ERROR'
|
|
4982
5520
|
? null
|
|
4983
5521
|
: readOptionalTextFile(creatorFeedbackFile ?? undefined);
|
|
5522
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
4984
5523
|
await (0, review_1.validateGameReviewResult)({
|
|
4985
5524
|
reviewState: state,
|
|
4986
5525
|
reviewMessage,
|
|
@@ -5009,6 +5548,7 @@ async function submitReviewTask(options) {
|
|
|
5009
5548
|
reviewState: state,
|
|
5010
5549
|
reviewMessage,
|
|
5011
5550
|
creatorFeedback,
|
|
5551
|
+
...(platformFeedback ? { platformFeedback } : {}),
|
|
5012
5552
|
...(nextSteps !== undefined ? { nextSteps } : {}),
|
|
5013
5553
|
evidenceFiles,
|
|
5014
5554
|
instrumentEvidence,
|
|
@@ -5136,11 +5676,13 @@ async function failTask(options) {
|
|
|
5136
5676
|
if (!ctx) {
|
|
5137
5677
|
return;
|
|
5138
5678
|
}
|
|
5679
|
+
const platformFeedback = readPlatformFeedbackFile(options.platformFeedbackFile);
|
|
5139
5680
|
await ctx.client.workerFailAgentTask(taskContext.taskId, {
|
|
5140
5681
|
taskToken: taskContext.taskToken,
|
|
5141
5682
|
error: message,
|
|
5142
5683
|
terminalReason: (0, types_1.classifyAgentTaskTerminalReason)(message, 'agent'),
|
|
5143
5684
|
result: { failedBy: 'agent' },
|
|
5685
|
+
...(platformFeedback ? { platformFeedback } : {}),
|
|
5144
5686
|
});
|
|
5145
5687
|
(0, output_1.printSuccess)('Task marked failed. This was the final operation; send one short final status sentence and stop immediately.');
|
|
5146
5688
|
}
|