@playdrop/playdrop-cli 0.12.38 → 0.12.39
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/build.d.ts +3 -1
- package/dist/apps/build.js +4 -0
- package/dist/apps/loadCheck.d.ts +9 -2
- package/dist/apps/loadCheck.js +167 -32
- package/dist/apps/surfaceProfiles.d.ts +10 -0
- package/dist/apps/surfaceProfiles.js +64 -0
- package/dist/apps/upload.js +2 -0
- package/dist/catalogue.d.ts +7 -1
- package/dist/catalogue.js +27 -0
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.js +68 -2
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +18 -0
- package/dist/commands/worker/runtime.d.ts +6 -1
- package/dist/commands/worker/runtime.js +20 -3
- package/dist/commands/worker.d.ts +23 -3
- package/dist/commands/worker.js +98 -5
- package/dist/index.js +2 -0
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/types/dist/api.d.ts +15 -0
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +49 -0
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-playtest-tape.js +242 -0
- package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +2 -0
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +3 -0
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +56 -5
- package/node_modules/@playdrop/types/dist/version.d.ts +5 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/commands/upload.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.resolveUploadOwner = resolveUploadOwner;
|
|
|
4
4
|
exports.resolveDefaultUploadCreator = resolveDefaultUploadCreator;
|
|
5
5
|
exports.upload = upload;
|
|
6
6
|
exports.resolveWorkerPlaydropAssetRequirement = resolveWorkerPlaydropAssetRequirement;
|
|
7
|
+
exports.assertAgentNewGamePlaytestContract = assertAgentNewGamePlaytestContract;
|
|
7
8
|
exports.assertTaskPlaytestEvidenceManifest = assertTaskPlaytestEvidenceManifest;
|
|
8
9
|
exports.assertWorkerAppLocalUploadPreflight = assertWorkerAppLocalUploadPreflight;
|
|
9
10
|
exports.publishWorkerAppProject = publishWorkerAppProject;
|
|
@@ -1222,6 +1223,22 @@ function resolveWorkerPlaydropAssetRequirement(value) {
|
|
|
1222
1223
|
}
|
|
1223
1224
|
return null;
|
|
1224
1225
|
}
|
|
1226
|
+
function assertAgentNewGamePlaytestContract(task) {
|
|
1227
|
+
if (!task.primarySurface) {
|
|
1228
|
+
throw new Error('agent_task_primary_surface_required: NEW_GAME catalogue.json must declare primarySurface.');
|
|
1229
|
+
}
|
|
1230
|
+
if (!task.surfaceTargets.includes(task.primarySurface)) {
|
|
1231
|
+
throw new Error(`agent_task_primary_surface_not_supported:${task.primarySurface}`);
|
|
1232
|
+
}
|
|
1233
|
+
if (!task.playtestTapes) {
|
|
1234
|
+
throw new Error('agent_task_playtest_tapes_required: NEW_GAME catalogue.json must declare one playtest tape per supported surface.');
|
|
1235
|
+
}
|
|
1236
|
+
for (const surface of task.surfaceTargets) {
|
|
1237
|
+
if (!task.playtestTapes[surface]) {
|
|
1238
|
+
throw new Error(`agent_task_playtest_tape_missing:${surface}`);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1225
1242
|
const WORKER_SUPERVISOR_SOURCE_EXCLUDES = [
|
|
1226
1243
|
'PLAYDROP_TASK.md',
|
|
1227
1244
|
'AGENTS.md',
|
|
@@ -1885,6 +1902,7 @@ async function assertWorkerAppLocalUploadPreflight(input) {
|
|
|
1885
1902
|
() => assertGreyboxReport(input.task),
|
|
1886
1903
|
() => (0, listingPreflight_1.assertAgentGameHeroListingPreflight)(input.task),
|
|
1887
1904
|
...(input.kind === 'NEW_GAME' ? [() => (0, listingPreflight_1.assertAgentNewGameArtDirectionPreflight)(input.task)] : []),
|
|
1905
|
+
...(input.kind === 'NEW_GAME' ? [() => assertAgentNewGamePlaytestContract(input.task)] : []),
|
|
1888
1906
|
() => {
|
|
1889
1907
|
if (!appTaskSatisfiesPlaydropAssetRequirement(input.task, input.playdropAssetRequirement)) {
|
|
1890
1908
|
throw new Error('agent_task_playdrop_asset_dependency_missing: the creator asked for PlayDrop assets, but catalogue.json does not declare a matching uses.assets, uses.packs, or ownedAssets dependency.');
|
|
@@ -77,7 +77,12 @@ export declare function buildClaudeExecArgs(input: {
|
|
|
77
77
|
pluginDir?: string | null;
|
|
78
78
|
}): string[];
|
|
79
79
|
export declare const PLAYWRIGHT_MCP_VERSION = "0.0.78";
|
|
80
|
-
export
|
|
80
|
+
export type WorkerInstrumentSurface = 'DESKTOP' | 'MOBILE_LANDSCAPE' | 'MOBILE_PORTRAIT';
|
|
81
|
+
export declare function buildPlaywrightMcpCommand(surface?: WorkerInstrumentSurface | null): {
|
|
82
|
+
command: string;
|
|
83
|
+
args: string[];
|
|
84
|
+
};
|
|
85
|
+
export declare function buildPlaywrightMcpServerConfig(surface?: WorkerInstrumentSurface | null): Record<string, unknown>;
|
|
81
86
|
export declare function buildClaudePermissionSettings(input?: {
|
|
82
87
|
workspaceDir?: string | null;
|
|
83
88
|
denyReadRoots?: string[];
|
|
@@ -17,6 +17,7 @@ exports.runLoggedProcess = runLoggedProcess;
|
|
|
17
17
|
exports.buildCodexExecArgs = buildCodexExecArgs;
|
|
18
18
|
exports.readCodexSandboxMode = readCodexSandboxMode;
|
|
19
19
|
exports.buildClaudeExecArgs = buildClaudeExecArgs;
|
|
20
|
+
exports.buildPlaywrightMcpCommand = buildPlaywrightMcpCommand;
|
|
20
21
|
exports.buildPlaywrightMcpServerConfig = buildPlaywrightMcpServerConfig;
|
|
21
22
|
exports.buildClaudePermissionSettings = buildClaudePermissionSettings;
|
|
22
23
|
exports.buildClaudeDeniedPermissionRules = buildClaudeDeniedPermissionRules;
|
|
@@ -25,6 +26,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
25
26
|
const promises_1 = require("node:fs/promises");
|
|
26
27
|
const node_os_1 = __importDefault(require("node:os"));
|
|
27
28
|
const node_path_1 = __importDefault(require("node:path"));
|
|
29
|
+
const surfaceProfiles_1 = require("../../apps/surfaceProfiles");
|
|
28
30
|
const CHILD_KILL_GRACE_MS = 10000;
|
|
29
31
|
exports.DEFAULT_CODEX_TIMEOUT_MS = 2 * 60 * 60 * 1000;
|
|
30
32
|
exports.DEFAULT_WORKER_TOKEN_CAP = 20000000;
|
|
@@ -647,12 +649,27 @@ const CLAUDE_BASE_TOOL_NAMES = [
|
|
|
647
649
|
// never through @latest drift (see the 2026-07-13 auto-update incident).
|
|
648
650
|
exports.PLAYWRIGHT_MCP_VERSION = '0.0.78';
|
|
649
651
|
const PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE = 'mcp__playwright';
|
|
650
|
-
function
|
|
652
|
+
function buildPlaywrightMcpCommand(surface) {
|
|
653
|
+
const surfaceArgs = surface ? (0, surfaceProfiles_1.buildPlaywrightMcpSurfaceArgs)(surface) : [];
|
|
654
|
+
return {
|
|
655
|
+
command: 'npx',
|
|
656
|
+
args: [
|
|
657
|
+
'-y',
|
|
658
|
+
`@playwright/mcp@${exports.PLAYWRIGHT_MCP_VERSION}`,
|
|
659
|
+
'--browser',
|
|
660
|
+
'chrome',
|
|
661
|
+
'--isolated',
|
|
662
|
+
...surfaceArgs,
|
|
663
|
+
],
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function buildPlaywrightMcpServerConfig(surface) {
|
|
667
|
+
const server = buildPlaywrightMcpCommand(surface);
|
|
651
668
|
return {
|
|
652
669
|
mcpServers: {
|
|
653
670
|
playwright: {
|
|
654
|
-
command:
|
|
655
|
-
args:
|
|
671
|
+
command: server.command,
|
|
672
|
+
args: server.args,
|
|
656
673
|
},
|
|
657
674
|
},
|
|
658
675
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
-
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type AgentWorkerUpdatePolicyResponse, type WorkerTaskAssignmentV2, type WorkerTaskPluginV2, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
2
|
+
import { type AgentExecutionTarget, type AgentRuntime, type AgentTaskKind, type AgentTaskNextStepSuggestion, type AgentTaskResponse, type AgentWorkerCapabilities, type AgentWorkerResponse, type AgentWorkerUpdatePolicyResponse, type JsonValue, type WorkerTaskAssignmentV2, type WorkerTaskPluginV2, type WorkerClaimAgentTaskResponse } from '@playdrop/types';
|
|
3
3
|
import { loadConfig } from '../config';
|
|
4
4
|
import { runShell } from '../shellProbe';
|
|
5
5
|
import { type WorkerPlaydropAssetRequirement } from './upload';
|
|
6
|
-
import { type LoggedProcessResult, type LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
7
|
-
export { assertWorkerTokenUsageWithinCap, buildClaudeExecArgs, buildClaudePermissionSettings, buildCodexExecArgs, buildWorkerChildEnv, DEFAULT_CODEX_TIMEOUT_MS, DEFAULT_WORKER_TOKEN_CAP, extractAgentTokenUsage, extractCodexRolloutTokenUsage, extractCodexThreadId, extractCodexTokensUsed, readCodexRolloutTokenUsageForThread, readCodexSandboxMode, readEnvBoolean, runLoggedProcess } from './worker/runtime';
|
|
6
|
+
import { type WorkerInstrumentSurface, type LoggedProcessResult, type LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
7
|
+
export { assertWorkerTokenUsageWithinCap, buildClaudeExecArgs, buildClaudePermissionSettings, buildCodexExecArgs, buildPlaywrightMcpCommand, buildWorkerChildEnv, DEFAULT_CODEX_TIMEOUT_MS, DEFAULT_WORKER_TOKEN_CAP, extractAgentTokenUsage, extractCodexRolloutTokenUsage, extractCodexThreadId, extractCodexTokensUsed, readCodexRolloutTokenUsageForThread, readCodexSandboxMode, readEnvBoolean, runLoggedProcess } from './worker/runtime';
|
|
8
8
|
export type { AgentTokenUsage, LoggedProcessResult, LoggedProcessTranscriptChunk } from './worker/runtime';
|
|
9
9
|
export declare const WORKER_SESSION_EXPIRED_MESSAGE = "worker session expired: run \"playdrop auth login\" and start the worker again";
|
|
10
10
|
export declare const WORKER_CONTEXT_COMMAND_NOT_ALLOWED_MESSAGE = "worker_context_command_not_allowed: inside a PlayDrop worker task only task progress, task claim-slug, task upload/done/fail, assigned app scaffolding, project validation/build/dev/check/capture, read-only help/catalogue/documentation lookup, and PlayDrop AI generation are permitted.";
|
|
@@ -127,6 +127,24 @@ type WorkerTaskState = {
|
|
|
127
127
|
leaseExpiresAt: string | null;
|
|
128
128
|
claimedAt: string;
|
|
129
129
|
};
|
|
130
|
+
type WorkerTaskContextFile = {
|
|
131
|
+
taskId: number;
|
|
132
|
+
attempt: number;
|
|
133
|
+
taskToken: string;
|
|
134
|
+
taskType: string;
|
|
135
|
+
metadata: Record<string, JsonValue>;
|
|
136
|
+
kind: AgentTaskKind;
|
|
137
|
+
creatorUsername: string;
|
|
138
|
+
creatorRequest: string;
|
|
139
|
+
target: AgentExecutionTarget;
|
|
140
|
+
outputAppName: string | null;
|
|
141
|
+
outputVersion: string;
|
|
142
|
+
remixSourceRef?: string | null;
|
|
143
|
+
allowedTemplateKeys?: string[];
|
|
144
|
+
reviewAppVersionId?: number | null;
|
|
145
|
+
devPort: number;
|
|
146
|
+
env: string;
|
|
147
|
+
};
|
|
130
148
|
type WorkerCodexModel = {
|
|
131
149
|
model: string;
|
|
132
150
|
reasoningEffort: string;
|
|
@@ -148,6 +166,7 @@ export declare function claimBackoffDelayMs(currentMs: number, random?: () => nu
|
|
|
148
166
|
export declare function resolveWorkerHomeDir(): string;
|
|
149
167
|
export declare const WORKER_TASK_WORKSPACE_RETENTION = 20;
|
|
150
168
|
export declare function pruneWorkerTaskWorkspaces(incomingWorkspaceDir: string, retention?: number): Promise<string[]>;
|
|
169
|
+
export declare function resolveWorkerInstrumentSurface(taskContext: Pick<WorkerTaskContextFile, 'kind' | 'metadata'>): WorkerInstrumentSurface | null;
|
|
151
170
|
export declare function hasAgentTaskUploadedArtifact(task: Pick<AgentTaskResponse, 'appId' | 'appVersionId'>): boolean;
|
|
152
171
|
export declare function appendWorkerTranscriptChunks(input: {
|
|
153
172
|
client: Pick<ApiClient, 'workerAppendAgentTaskTranscriptChunks'>;
|
|
@@ -177,6 +196,7 @@ export declare function assertAssignmentPluginBundleExtractionBounds(buffer: Buf
|
|
|
177
196
|
export declare function stageAssignmentPluginBundle(input: {
|
|
178
197
|
workspaceDir: string;
|
|
179
198
|
pluginBundle: WorkerTaskPluginV2;
|
|
199
|
+
taskKind?: AgentTaskKind;
|
|
180
200
|
}): Promise<string>;
|
|
181
201
|
export declare function resolveDevPluginWorkingTree(input: {
|
|
182
202
|
workingTree?: string;
|
package/dist/commands/worker.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.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.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;
|
|
7
7
|
exports.allocateWorkerDevPort = allocateWorkerDevPort;
|
|
8
8
|
exports.isWorkerContextCommandAllowed = isWorkerContextCommandAllowed;
|
|
9
9
|
exports.resolveWorkerExecutionTargetFromRole = resolveWorkerExecutionTargetFromRole;
|
|
@@ -12,6 +12,7 @@ exports.nextClaimBackoffMs = nextClaimBackoffMs;
|
|
|
12
12
|
exports.claimBackoffDelayMs = claimBackoffDelayMs;
|
|
13
13
|
exports.resolveWorkerHomeDir = resolveWorkerHomeDir;
|
|
14
14
|
exports.pruneWorkerTaskWorkspaces = pruneWorkerTaskWorkspaces;
|
|
15
|
+
exports.resolveWorkerInstrumentSurface = resolveWorkerInstrumentSurface;
|
|
15
16
|
exports.hasAgentTaskUploadedArtifact = hasAgentTaskUploadedArtifact;
|
|
16
17
|
exports.appendWorkerTranscriptChunks = appendWorkerTranscriptChunks;
|
|
17
18
|
exports.readTaskNextStepsFile = readTaskNextStepsFile;
|
|
@@ -110,6 +111,7 @@ Object.defineProperty(exports, "assertWorkerTokenUsageWithinCap", { enumerable:
|
|
|
110
111
|
Object.defineProperty(exports, "buildClaudeExecArgs", { enumerable: true, get: function () { return runtime_2.buildClaudeExecArgs; } });
|
|
111
112
|
Object.defineProperty(exports, "buildClaudePermissionSettings", { enumerable: true, get: function () { return runtime_2.buildClaudePermissionSettings; } });
|
|
112
113
|
Object.defineProperty(exports, "buildCodexExecArgs", { enumerable: true, get: function () { return runtime_2.buildCodexExecArgs; } });
|
|
114
|
+
Object.defineProperty(exports, "buildPlaywrightMcpCommand", { enumerable: true, get: function () { return runtime_2.buildPlaywrightMcpCommand; } });
|
|
113
115
|
Object.defineProperty(exports, "buildWorkerChildEnv", { enumerable: true, get: function () { return runtime_2.buildWorkerChildEnv; } });
|
|
114
116
|
Object.defineProperty(exports, "DEFAULT_CODEX_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_2.DEFAULT_CODEX_TIMEOUT_MS; } });
|
|
115
117
|
Object.defineProperty(exports, "DEFAULT_WORKER_TOKEN_CAP", { enumerable: true, get: function () { return runtime_2.DEFAULT_WORKER_TOKEN_CAP; } });
|
|
@@ -149,6 +151,7 @@ const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
|
149
151
|
exports.WORKER_SESSION_EXPIRED_MESSAGE = 'worker session expired: run "playdrop auth login" and start the worker again';
|
|
150
152
|
const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
151
153
|
['task', 'context'],
|
|
154
|
+
['task', 'help'],
|
|
152
155
|
['task', 'report'],
|
|
153
156
|
['task', 'report-catalogue'],
|
|
154
157
|
['task', 'claim-slug'],
|
|
@@ -171,6 +174,7 @@ const WORKER_CONTEXT_ALLOWED_COMMANDS = [
|
|
|
171
174
|
['search'],
|
|
172
175
|
['detail'],
|
|
173
176
|
['versions', 'browse'],
|
|
177
|
+
['tags', 'browse'],
|
|
174
178
|
['documentation', 'browse'],
|
|
175
179
|
['documentation', 'read'],
|
|
176
180
|
['help'],
|
|
@@ -568,6 +572,48 @@ function buildWorkerTaskContextV2(assignment) {
|
|
|
568
572
|
target: assignment.task.executionTarget,
|
|
569
573
|
});
|
|
570
574
|
}
|
|
575
|
+
function normalizeWorkerInstrumentSurface(value) {
|
|
576
|
+
return value === 'DESKTOP' || value === 'MOBILE_LANDSCAPE' || value === 'MOBILE_PORTRAIT'
|
|
577
|
+
? value
|
|
578
|
+
: null;
|
|
579
|
+
}
|
|
580
|
+
function resolveWorkerInstrumentSurface(taskContext) {
|
|
581
|
+
const playdrop = taskContext.metadata.playdrop;
|
|
582
|
+
if (!playdrop || typeof playdrop !== 'object' || Array.isArray(playdrop)) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const metadata = playdrop;
|
|
586
|
+
if (taskContext.kind === 'GAME_REVIEW') {
|
|
587
|
+
const reviewTarget = metadata.reviewTarget;
|
|
588
|
+
if (!reviewTarget || typeof reviewTarget !== 'object' || Array.isArray(reviewTarget)) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
return normalizeWorkerInstrumentSurface(reviewTarget.primarySurface);
|
|
592
|
+
}
|
|
593
|
+
if (taskContext.kind === 'GAME_EVAL') {
|
|
594
|
+
const evalTarget = metadata.evalTarget;
|
|
595
|
+
if (!evalTarget || typeof evalTarget !== 'object' || Array.isArray(evalTarget)) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const targets = evalTarget.targets;
|
|
599
|
+
if (!Array.isArray(targets) || targets.length === 0) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const surfaces = new Set();
|
|
603
|
+
for (const target of targets) {
|
|
604
|
+
if (!target || typeof target !== 'object' || Array.isArray(target)) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const surface = normalizeWorkerInstrumentSurface(target.primarySurface);
|
|
608
|
+
if (!surface) {
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
surfaces.add(surface);
|
|
612
|
+
}
|
|
613
|
+
return surfaces.size === 1 ? Array.from(surfaces)[0] ?? null : null;
|
|
614
|
+
}
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
571
617
|
function workerTaskContextPath(workspaceDir) {
|
|
572
618
|
return node_path_1.default.join(workspaceDir, '.playdrop', 'task.json');
|
|
573
619
|
}
|
|
@@ -1063,9 +1109,24 @@ function readAgentBundleManifest(extractedRoot) {
|
|
|
1063
1109
|
if (!destinations.has('CLAUDE.md')) {
|
|
1064
1110
|
throw new Error('agent_bundle_claude_file_missing');
|
|
1065
1111
|
}
|
|
1112
|
+
const taskAgentOverlays = manifest.workspace.taskAgentOverlays;
|
|
1113
|
+
if (taskAgentOverlays !== undefined) {
|
|
1114
|
+
if (!taskAgentOverlays || typeof taskAgentOverlays !== 'object' || Array.isArray(taskAgentOverlays)) {
|
|
1115
|
+
throw new Error('agent_bundle_task_agent_overlays_invalid');
|
|
1116
|
+
}
|
|
1117
|
+
for (const [rawKind, rawSource] of Object.entries(taskAgentOverlays)) {
|
|
1118
|
+
if (!WORKER_SUPPORTED_KINDS.includes(rawKind)) {
|
|
1119
|
+
throw new Error(`agent_bundle_task_agent_overlay_kind_invalid:${rawKind}`);
|
|
1120
|
+
}
|
|
1121
|
+
const source = normalizeAgentBundlePath(extractedRoot, rawSource, 'agent_bundle_task_agent_overlay_source_invalid');
|
|
1122
|
+
if (!isFile(source)) {
|
|
1123
|
+
throw new Error(`agent_bundle_task_agent_overlay_source_missing:${rawSource ?? ''}`);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1066
1127
|
return { manifest, pluginRoot };
|
|
1067
1128
|
}
|
|
1068
|
-
async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir) {
|
|
1129
|
+
async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir, taskKind) {
|
|
1069
1130
|
const resolved = readAgentBundleManifest(extractedRoot);
|
|
1070
1131
|
for (const entry of resolved.manifest.workspace.files) {
|
|
1071
1132
|
const source = normalizeAgentBundlePath(extractedRoot, entry.source, 'agent_bundle_workspace_source_invalid');
|
|
@@ -1073,6 +1134,16 @@ async function stageAgentBundleWorkspaceFiles(extractedRoot, workspaceDir) {
|
|
|
1073
1134
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
1074
1135
|
await (0, promises_1.copyFile)(source, destination);
|
|
1075
1136
|
}
|
|
1137
|
+
const overlaySource = taskKind
|
|
1138
|
+
? resolved.manifest.workspace.taskAgentOverlays?.[taskKind]
|
|
1139
|
+
: null;
|
|
1140
|
+
if (overlaySource) {
|
|
1141
|
+
const agentsPath = resolveWorkspaceFileDestination(workspaceDir, 'AGENTS.md');
|
|
1142
|
+
const overlayPath = normalizeAgentBundlePath(extractedRoot, overlaySource, 'agent_bundle_task_agent_overlay_source_invalid');
|
|
1143
|
+
const base = (0, node_fs_1.readFileSync)(agentsPath, 'utf8').trimEnd();
|
|
1144
|
+
const overlay = (0, node_fs_1.readFileSync)(overlayPath, 'utf8').trim();
|
|
1145
|
+
await (0, promises_1.writeFile)(agentsPath, `${base}\n\n${overlay}\n`, 'utf8');
|
|
1146
|
+
}
|
|
1076
1147
|
return resolved.pluginRoot;
|
|
1077
1148
|
}
|
|
1078
1149
|
async function stageAssignmentPluginBundle(input) {
|
|
@@ -1128,7 +1199,7 @@ async function stageAssignmentPluginBundle(input) {
|
|
|
1128
1199
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
1129
1200
|
await (0, promises_1.writeFile)(destination, Buffer.from(content));
|
|
1130
1201
|
}
|
|
1131
|
-
return await stageAgentBundleWorkspaceFiles(pluginRoot, input.workspaceDir);
|
|
1202
|
+
return await stageAgentBundleWorkspaceFiles(pluginRoot, input.workspaceDir, input.taskKind);
|
|
1132
1203
|
}
|
|
1133
1204
|
function resolveDevPluginWorkingTree(input) {
|
|
1134
1205
|
const workingTree = input.workingTree?.trim();
|
|
@@ -1947,8 +2018,22 @@ async function runCodex(input) {
|
|
|
1947
2018
|
env: pluginEnv,
|
|
1948
2019
|
});
|
|
1949
2020
|
}
|
|
2021
|
+
if (input.enablePlaywrightMcp) {
|
|
2022
|
+
const server = (0, runtime_1.buildPlaywrightMcpCommand)(input.instrumentSurface);
|
|
2023
|
+
await execFileAsync('codex', ['mcp', 'add', 'playwright', '--', server.command, ...server.args], {
|
|
2024
|
+
cwd: input.workspaceDir,
|
|
2025
|
+
env: {
|
|
2026
|
+
...node_process_1.default.env,
|
|
2027
|
+
HOME: homeDir,
|
|
2028
|
+
CODEX_HOME: codexHomeDir,
|
|
2029
|
+
},
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
1950
2032
|
let observedCodexThreadId = null;
|
|
1951
2033
|
let codexThreadProbe = '';
|
|
2034
|
+
const screenshotCollector = input.enablePlaywrightMcp
|
|
2035
|
+
? (0, instrument_evidence_1.createWorkerInstrumentScreenshotCollector)(input.workspaceDir)
|
|
2036
|
+
: null;
|
|
1952
2037
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
1953
2038
|
command: 'codex',
|
|
1954
2039
|
args: (0, runtime_1.buildCodexExecArgs)({
|
|
@@ -1982,9 +2067,13 @@ async function runCodex(input) {
|
|
|
1982
2067
|
}
|
|
1983
2068
|
await input.onTranscriptChunks(chunks);
|
|
1984
2069
|
},
|
|
2070
|
+
...(screenshotCollector
|
|
2071
|
+
? { onStdoutText: (text) => screenshotCollector.push(text) > 0 }
|
|
2072
|
+
: {}),
|
|
1985
2073
|
transcriptFlushIntervalMs: runtime_1.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS,
|
|
1986
2074
|
onChild: input.onChild,
|
|
1987
2075
|
});
|
|
2076
|
+
screenshotCollector?.finish();
|
|
1988
2077
|
observedCodexThreadId = observedCodexThreadId
|
|
1989
2078
|
?? (0, runtime_1.extractCodexThreadId)(`${result.stdout}\n${result.stderr}\n${result.outputTail}`);
|
|
1990
2079
|
if (!observedCodexThreadId) {
|
|
@@ -2027,7 +2116,7 @@ async function runClaude(input) {
|
|
|
2027
2116
|
if (input.enablePlaywrightMcp) {
|
|
2028
2117
|
playwrightMcpConfigPath = node_path_1.default.join(input.workspaceDir, '.playdrop', 'claude-run', 'playwright-mcp.json');
|
|
2029
2118
|
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(playwrightMcpConfigPath), { recursive: true });
|
|
2030
|
-
(0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(), null, 2)}\n`, 'utf8');
|
|
2119
|
+
(0, node_fs_1.writeFileSync)(playwrightMcpConfigPath, `${JSON.stringify((0, runtime_1.buildPlaywrightMcpServerConfig)(input.instrumentSurface), null, 2)}\n`, 'utf8');
|
|
2031
2120
|
}
|
|
2032
2121
|
const result = await (0, runtime_1.runLoggedProcess)({
|
|
2033
2122
|
command: 'claude',
|
|
@@ -4033,11 +4122,12 @@ async function startWorker(options = {}) {
|
|
|
4033
4122
|
};
|
|
4034
4123
|
await stageAssignmentWorkspaceV2(workspaceDir, assignment.workspace);
|
|
4035
4124
|
const assignmentPluginRoot = devPluginWorkingTree
|
|
4036
|
-
? await stageAgentBundleWorkspaceFiles(devPluginWorkingTree, workspaceDir)
|
|
4125
|
+
? await stageAgentBundleWorkspaceFiles(devPluginWorkingTree, workspaceDir, task.kind)
|
|
4037
4126
|
: assignment.plugin
|
|
4038
4127
|
? await stageAssignmentPluginBundle({
|
|
4039
4128
|
workspaceDir,
|
|
4040
4129
|
pluginBundle: assignment.plugin,
|
|
4130
|
+
taskKind: task.kind,
|
|
4041
4131
|
})
|
|
4042
4132
|
: null;
|
|
4043
4133
|
availableSkillPaths = listPluginSkillPaths(assignmentPluginRoot);
|
|
@@ -4124,6 +4214,8 @@ async function startWorker(options = {}) {
|
|
|
4124
4214
|
devPort,
|
|
4125
4215
|
codexModel: codexModel ?? resolveCodexModel(assignment.agent.model),
|
|
4126
4216
|
taskPluginRoot: assignmentPluginRoot,
|
|
4217
|
+
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4218
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4127
4219
|
imagePaths: imageAttachments.map((attachment) => attachment.path),
|
|
4128
4220
|
onTranscriptChunks: async (chunks) => {
|
|
4129
4221
|
await appendTranscriptChunks(chunks);
|
|
@@ -4141,6 +4233,7 @@ async function startWorker(options = {}) {
|
|
|
4141
4233
|
prompt,
|
|
4142
4234
|
enableChrome: assignment.agent.browser === 'CHROME',
|
|
4143
4235
|
enablePlaywrightMcp: assignment.agent.browser === 'PLAYWRIGHT',
|
|
4236
|
+
instrumentSurface: resolveWorkerInstrumentSurface(taskContext),
|
|
4144
4237
|
envName: env,
|
|
4145
4238
|
workerUsername: username,
|
|
4146
4239
|
taskId: task.id,
|
package/dist/index.js
CHANGED
|
@@ -1021,12 +1021,14 @@ project
|
|
|
1021
1021
|
.option('--timeout <seconds>', 'Load-check timeout in seconds')
|
|
1022
1022
|
.option('--screenshot <path>', 'Screenshot output path')
|
|
1023
1023
|
.option('--actions <path>', 'JSON action script to dispatch after the game frame is ready')
|
|
1024
|
+
.option('--tape <surface>', 'Run matched idle and catalogue playtest-tape captures on a declared surface')
|
|
1024
1025
|
.action(async (target, opts = {}) => {
|
|
1025
1026
|
await (0, check_1.check)(target, {
|
|
1026
1027
|
app: opts.app,
|
|
1027
1028
|
timeout: opts.timeout,
|
|
1028
1029
|
screenshot: opts.screenshot,
|
|
1029
1030
|
actions: opts.actions,
|
|
1031
|
+
tape: opts.tape,
|
|
1030
1032
|
});
|
|
1031
1033
|
});
|
|
1032
1034
|
const projectCapture = project.command('capture').description('Record game media with the native macOS recorder');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AppAuthFilter, AppControllerFilter, AppSurfaceFilter } from './app-capability-filters.js';
|
|
2
2
|
import type { AppOrigin, AppSurface, AppType } from './app.js';
|
|
3
|
+
import type { AppPlaytestTapes } from './app-playtest-tape.js';
|
|
3
4
|
import type { AssetPackResponse } from './asset-pack.js';
|
|
4
5
|
import type { AppMetadataAssetSpecSupport, AssetSpecListResponse, AssetSpecResponse, AssetSpecSearchResult, AssetSpecVersionResponse, AssetSpecVersionsListResponse } from './asset-spec.js';
|
|
5
6
|
import type { AssetCategory, AssetResponse } from './asset.js';
|
|
@@ -480,6 +481,10 @@ export interface GameEvalTargetInput {
|
|
|
480
481
|
creatorPrompt: string;
|
|
481
482
|
primarySurface: AppSurface;
|
|
482
483
|
}
|
|
484
|
+
export interface GameEvalRequirements {
|
|
485
|
+
declaredPrimarySurface?: boolean;
|
|
486
|
+
completePlaytestTapes?: boolean;
|
|
487
|
+
}
|
|
483
488
|
export interface CreateGameEvalAgentTaskRequest {
|
|
484
489
|
mode: GameEvalMode;
|
|
485
490
|
agent: AgentRuntime;
|
|
@@ -488,6 +493,7 @@ export interface CreateGameEvalAgentTaskRequest {
|
|
|
488
493
|
judgeInstructionSha256: string;
|
|
489
494
|
tieBand?: number | null;
|
|
490
495
|
targets: GameEvalTargetInput[];
|
|
496
|
+
requirements?: GameEvalRequirements;
|
|
491
497
|
agentBundleSha?: string;
|
|
492
498
|
}
|
|
493
499
|
export interface CreateGameEvalAgentTaskResponse {
|
|
@@ -825,6 +831,13 @@ export interface GameEvalDimensionResult {
|
|
|
825
831
|
export interface GameEvalTargetResult {
|
|
826
832
|
targetId: string;
|
|
827
833
|
overall: number;
|
|
834
|
+
tapeCheck?: {
|
|
835
|
+
surface: AppSurface;
|
|
836
|
+
idleOutcome: string;
|
|
837
|
+
tapeOutcome: string;
|
|
838
|
+
criterion: string;
|
|
839
|
+
passed: boolean;
|
|
840
|
+
};
|
|
828
841
|
dimensions: Record<GameEvalDimension, GameEvalDimensionResult>;
|
|
829
842
|
comments: string;
|
|
830
843
|
evidence?: Record<string, unknown>;
|
|
@@ -1075,6 +1088,8 @@ export interface AppResponse {
|
|
|
1075
1088
|
entryPoint?: string | null;
|
|
1076
1089
|
/** Surface targets from current version (undefined for draft apps with no version) */
|
|
1077
1090
|
surfaceTargets?: AppSurface[];
|
|
1091
|
+
primarySurface?: AppSurface | null;
|
|
1092
|
+
playtestTapes?: AppPlaytestTapes | null;
|
|
1078
1093
|
/** Asset spec support declarations from the current version */
|
|
1079
1094
|
assetSpecSupport?: AppMetadataAssetSpecSupport[];
|
|
1080
1095
|
bundleSize?: number | null;
|