@playdrop/playdrop-cli 0.12.40 → 0.12.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/config/client-meta.json +2 -2
  2. package/dist/apps/index.d.ts +1 -0
  3. package/dist/apps/index.js +1 -0
  4. package/dist/apps/loadCheck.d.ts +18 -1
  5. package/dist/apps/loadCheck.js +223 -1
  6. package/dist/apps/playtestContract.d.ts +2 -0
  7. package/dist/apps/playtestContract.js +77 -0
  8. package/dist/apps/upload.d.ts +1 -0
  9. package/dist/apps/upload.js +17 -0
  10. package/dist/commands/check.js +20 -2
  11. package/dist/commands/upload.js +9 -1
  12. package/dist/commands/worker/instrument-evidence.js +8 -1
  13. package/dist/commands/worker.js +1 -1
  14. package/node_modules/@playdrop/api-client/dist/client.d.ts +2 -1
  15. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  16. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +2 -1
  17. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  18. package/node_modules/@playdrop/api-client/dist/domains/apps.js +11 -0
  19. package/node_modules/@playdrop/config/client-meta.json +2 -2
  20. package/node_modules/@playdrop/types/dist/api.d.ts +25 -0
  21. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  22. package/node_modules/@playdrop/types/dist/api.js +3 -0
  23. package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +5 -0
  24. package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
  25. package/node_modules/@playdrop/types/dist/app-playtest-tape.js +82 -0
  26. package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +1 -0
  27. package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
  28. package/node_modules/@playdrop/types/dist/instrument-evidence.js +7 -0
  29. package/node_modules/@playdrop/types/dist/version.d.ts +49 -1
  30. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  31. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "0.12.40",
2
+ "version": "0.12.42",
3
3
  "build": 1,
4
- "runtimeSdkVersion": "0.12.40",
4
+ "runtimeSdkVersion": "0.12.42",
5
5
  "runtimeSdkBuild": 1,
6
6
  "clients": {
7
7
  "all": {
@@ -25,6 +25,7 @@ export type AppPipelineOptions = {
25
25
  agentTaskId?: number;
26
26
  playtestProof?: AgentTaskPlaytestProof;
27
27
  mediaCaptureRequired?: boolean;
28
+ machineCausalGateRequired?: boolean;
28
29
  captureSession?: TaskCaptureSession | null;
29
30
  };
30
31
  export declare function runAppPipeline(client: ApiClient, task: AppTask, options?: AppPipelineOptions): Promise<AppPipelineResult>;
@@ -66,6 +66,7 @@ async function runAppPipeline(client, task, options) {
66
66
  agentTaskId: options?.agentTaskId,
67
67
  playtestProof: options?.playtestProof,
68
68
  mediaCaptureRequired: options?.mediaCaptureRequired,
69
+ machineCausalGateRequired: options?.machineCausalGateRequired,
69
70
  captureSession: options?.captureSession,
70
71
  };
71
72
  const upload = await (0, upload_1.uploadApp)(client, task, artifacts, uploadOptions);
@@ -1,5 +1,5 @@
1
1
  import type { ApiClient } from '@playdrop/api-client';
2
- import { type AppPlaytestTape, type AppSurface, type UserResponse } from '@playdrop/types';
2
+ import { type AppPlaytestTape, type AppSurface, type AppUploadCausalComparison, type AppUploadMachineCausalEvidence, type UserResponse } from '@playdrop/types';
3
3
  import type { BrowserContextOptions, Frame, Page } from 'playwright-core';
4
4
  import type { AppTask } from '../catalogue';
5
5
  import { type TaskCaptureSession } from '../appUrls';
@@ -84,4 +84,21 @@ export declare function runUploadedHostedLoadCheck(input: {
84
84
  currentUser?: UserResponse | null;
85
85
  captureSession?: TaskCaptureSession | null;
86
86
  screenshotPath?: string | null;
87
+ readyGameFrameScreenshotPath?: string | null;
88
+ gameFrameScreenshotPath?: string | null;
89
+ playtestTape?: AppPlaytestTape;
90
+ postReadyWaitMs?: number;
87
91
  }): Promise<HostedLoadCheckResult>;
92
+ export declare function sliceFirstPrimaryVerbAction(tape: AppPlaytestTape): AppPlaytestTape;
93
+ export declare function compareCausalFrames(firstPath: string, secondPath: string): Promise<AppUploadCausalComparison>;
94
+ export declare function runUploadedHostedCausalCheck(input: {
95
+ client: ApiClient;
96
+ creatorUsername: string;
97
+ task: AppTask;
98
+ sessionId: string;
99
+ teachingText: string;
100
+ timeoutMs?: number;
101
+ token?: string | null;
102
+ currentUser?: UserResponse | null;
103
+ captureSession?: TaskCaptureSession | null;
104
+ }): Promise<AppUploadMachineCausalEvidence>;
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.PLAYDROP_SURFACE_CONTEXT_OPTIONS = void 0;
4
7
  exports.resolveLoadCheckSurface = resolveLoadCheckSurface;
@@ -12,9 +15,15 @@ exports.formatHostedLoadCheckFailure = formatHostedLoadCheckFailure;
12
15
  exports.redactHostedLoadCheckSecrets = redactHostedLoadCheckSecrets;
13
16
  exports.runLocalHostedLoadCheck = runLocalHostedLoadCheck;
14
17
  exports.runUploadedHostedLoadCheck = runUploadedHostedLoadCheck;
18
+ exports.sliceFirstPrimaryVerbAction = sliceFirstPrimaryVerbAction;
19
+ exports.compareCausalFrames = compareCausalFrames;
20
+ exports.runUploadedHostedCausalCheck = runUploadedHostedCausalCheck;
15
21
  const types_1 = require("@playdrop/types");
22
+ const node_crypto_1 = require("node:crypto");
16
23
  const promises_1 = require("node:fs/promises");
24
+ const node_os_1 = require("node:os");
17
25
  const node_path_1 = require("node:path");
26
+ const sharp_1 = __importDefault(require("sharp"));
18
27
  const appUrls_1 = require("../appUrls");
19
28
  const devShared_1 = require("../commands/devShared");
20
29
  const dev_1 = require("../commands/dev");
@@ -30,6 +39,8 @@ Object.defineProperty(exports, "PLAYDROP_SURFACE_CONTEXT_OPTIONS", { enumerable:
30
39
  const FRAME_SELECTOR = 'iframe[title="Game"]';
31
40
  const DEFAULT_HOSTED_LOAD_TIMEOUT_MS = 15000;
32
41
  const POST_READY_SETTLE_MS = 750;
42
+ const CAUSAL_FRAME_MEAN_DELTA_MIN = 0.025;
43
+ const CAUSAL_FRAME_CHANGED_PIXEL_RATIO_MIN = 0.12;
33
44
  const LOAD_CHECK_SURFACE_ORDER = ['DESKTOP', 'MOBILE_LANDSCAPE', 'MOBILE_PORTRAIT'];
34
45
  const GOOGLE_TELEMETRY_HOSTS = [
35
46
  'analytics.google.com',
@@ -499,6 +510,14 @@ async function saveScreenshot(page, screenshotPath) {
499
510
  console.log(`[check] Saved screenshot to ${(0, node_path_1.relative)(process.cwd(), normalizedPath) || normalizedPath}`);
500
511
  return normalizedPath;
501
512
  }
513
+ async function saveGameFrameScreenshot(page, screenshotPath) {
514
+ const normalizedPath = typeof screenshotPath === 'string' ? screenshotPath.trim() : '';
515
+ if (!normalizedPath)
516
+ return null;
517
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(normalizedPath), { recursive: true });
518
+ await page.locator(FRAME_SELECTOR).screenshot({ path: normalizedPath, type: 'png' });
519
+ return normalizedPath;
520
+ }
502
521
  function normalizeLoadCheckFailure(error, artifactFingerprint) {
503
522
  const rawMessage = error instanceof Error ? error.message : String(error);
504
523
  const message = normalizeLoadCheckMessage(rawMessage);
@@ -716,7 +735,7 @@ async function runHostedLoadCheck(options) {
716
735
  hostedLaunchWaiter,
717
736
  page.waitForTimeout(timeoutMs).then(() => {
718
737
  throw new Error(expectedHostedLaunchState === 'ready'
719
- ? 'hosted_app_missing_ready: Hosted app did not report ready before the load-check timeout.'
738
+ ? 'hosted_app_missing_ready: instrument_error:game_ready_timeout: Hosted app did not report sdk.host.ready() before the instrument timeout.'
720
739
  : `hosted_app_gate_missing: Hosted app did not report ${expectedHostedLaunchState} before the load-check timeout.`);
721
740
  }),
722
741
  ]);
@@ -737,6 +756,7 @@ async function runHostedLoadCheck(options) {
737
756
  if (isSoftwareWebGlRenderer(webglRenderer)) {
738
757
  throw new Error(`project_check_webgl_renderer_software: WebGL renderer is "${webglRenderer}". Real hardware acceleration is required.`);
739
758
  }
759
+ await saveGameFrameScreenshot(page, options.readyGameFrameScreenshotPath);
740
760
  if (options.playtestTape && options.actions?.length) {
741
761
  throw new Error('project_check_input_mode_conflict: playtest tape and legacy actions cannot run together.');
742
762
  }
@@ -753,6 +773,7 @@ async function runHostedLoadCheck(options) {
753
773
  else if (options.postReadyWaitMs && options.postReadyWaitMs > 0) {
754
774
  await page.waitForTimeout(options.postReadyWaitMs);
755
775
  }
776
+ await saveGameFrameScreenshot(page, options.gameFrameScreenshotPath);
756
777
  screenshotPath = await saveScreenshot(page, options.screenshotPath);
757
778
  }, {
758
779
  ...options.contextOptions,
@@ -981,6 +1002,11 @@ async function runUploadedHostedLoadCheck(input) {
981
1002
  expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
982
1003
  contextOptions,
983
1004
  screenshotPath: input.screenshotPath,
1005
+ readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
1006
+ gameFrameScreenshotPath: input.gameFrameScreenshotPath,
1007
+ playtestTape: input.playtestTape,
1008
+ playtestSurface: captureSurface,
1009
+ postReadyWaitMs: input.postReadyWaitMs,
984
1010
  });
985
1011
  }
986
1012
  else if (anonymousExpectedState === 'login_required') {
@@ -1009,6 +1035,11 @@ async function runUploadedHostedLoadCheck(input) {
1009
1035
  savedSessionBootstrap: true,
1010
1036
  contextOptions,
1011
1037
  screenshotPath: input.screenshotPath,
1038
+ readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
1039
+ gameFrameScreenshotPath: input.gameFrameScreenshotPath,
1040
+ playtestTape: input.playtestTape,
1041
+ playtestSurface: captureSurface,
1042
+ postReadyWaitMs: input.postReadyWaitMs,
1012
1043
  });
1013
1044
  }
1014
1045
  }
@@ -1021,6 +1052,11 @@ async function runUploadedHostedLoadCheck(input) {
1021
1052
  expectedHostedLaunchState: anonymousExpectedState,
1022
1053
  contextOptions,
1023
1054
  screenshotPath: input.screenshotPath,
1055
+ readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
1056
+ gameFrameScreenshotPath: input.gameFrameScreenshotPath,
1057
+ playtestTape: input.playtestTape,
1058
+ playtestSurface: captureSurface,
1059
+ postReadyWaitMs: input.postReadyWaitMs,
1024
1060
  });
1025
1061
  }
1026
1062
  const recorded = await input.client.recordAppUploadLaunchCheck(input.creatorUsername, input.task.name, input.sessionId, {
@@ -1042,3 +1078,189 @@ async function runUploadedHostedLoadCheck(input) {
1042
1078
  artifactFingerprint: recordedLaunchCheck.artifactFingerprint,
1043
1079
  };
1044
1080
  }
1081
+ function sliceFirstPrimaryVerbAction(tape) {
1082
+ const prefix = tape.events.slice(0, tape.startOnlyEventCount);
1083
+ const postStartEvents = tape.events.slice(tape.startOnlyEventCount);
1084
+ let actionEventCount = 0;
1085
+ if (tape.primaryVerb === 'tap') {
1086
+ actionEventCount = 1;
1087
+ }
1088
+ else if (tape.primaryVerb === 'key') {
1089
+ const activeKeys = new Set();
1090
+ for (let index = 0; index < postStartEvents.length; index += 1) {
1091
+ const event = postStartEvents[index];
1092
+ if (event.type === 'keyDown')
1093
+ activeKeys.add(event.key);
1094
+ if (event.type === 'keyUp')
1095
+ activeKeys.delete(event.key);
1096
+ if (activeKeys.size === 0 && index > 0) {
1097
+ actionEventCount = index + 1;
1098
+ break;
1099
+ }
1100
+ }
1101
+ }
1102
+ else {
1103
+ const activePointers = new Set();
1104
+ for (let index = 0; index < postStartEvents.length; index += 1) {
1105
+ const event = postStartEvents[index];
1106
+ if (event.type === 'pointerDown')
1107
+ activePointers.add(event.pointerId ?? 0);
1108
+ if (event.type === 'pointerUp')
1109
+ activePointers.delete(event.pointerId ?? 0);
1110
+ if (activePointers.size === 0 && index > 0) {
1111
+ actionEventCount = index + 1;
1112
+ break;
1113
+ }
1114
+ }
1115
+ }
1116
+ if (actionEventCount <= 0) {
1117
+ throw new Error(`agent_task_machine_causal_gate_invalid_tape:first_${tape.primaryVerb}_action_incomplete`);
1118
+ }
1119
+ return {
1120
+ ...tape,
1121
+ events: [...prefix, ...postStartEvents.slice(0, actionEventCount)],
1122
+ };
1123
+ }
1124
+ async function hashFile(path) {
1125
+ return (0, node_crypto_1.createHash)('sha256').update(await (0, promises_1.readFile)(path)).digest('hex');
1126
+ }
1127
+ async function compareCausalFrames(firstPath, secondPath) {
1128
+ const readPixels = async (path) => {
1129
+ return await (0, sharp_1.default)(path)
1130
+ .resize(64, 64, { fit: 'fill' })
1131
+ .greyscale()
1132
+ .raw()
1133
+ .toBuffer();
1134
+ };
1135
+ const [first, second] = await Promise.all([readPixels(firstPath), readPixels(secondPath)]);
1136
+ if (first.length !== second.length || first.length === 0) {
1137
+ throw new Error('agent_task_machine_causal_gate_frame_shape_mismatch');
1138
+ }
1139
+ let deltaTotal = 0;
1140
+ let changedPixels = 0;
1141
+ for (let index = 0; index < first.length; index += 1) {
1142
+ const delta = Math.abs(first[index] - second[index]);
1143
+ deltaTotal += delta;
1144
+ if (delta >= 16)
1145
+ changedPixels += 1;
1146
+ }
1147
+ const meanDelta = deltaTotal / first.length / 255;
1148
+ const changedPixelRatio = changedPixels / first.length;
1149
+ return {
1150
+ meanDelta,
1151
+ changedPixelRatio,
1152
+ passed: meanDelta >= CAUSAL_FRAME_MEAN_DELTA_MIN || changedPixelRatio >= CAUSAL_FRAME_CHANGED_PIXEL_RATIO_MIN,
1153
+ };
1154
+ }
1155
+ async function runUploadedHostedCausalCheck(input) {
1156
+ const primarySurface = input.task.primarySurface;
1157
+ const fullTape = primarySurface ? input.task.playtestTapes?.[primarySurface] : null;
1158
+ if (!primarySurface || !fullTape) {
1159
+ throw new Error('agent_task_machine_causal_gate_primary_tape_required');
1160
+ }
1161
+ if (fullTape.startOnlyEventCount <= 0) {
1162
+ throw new Error('agent_task_machine_causal_gate_start_only_required');
1163
+ }
1164
+ const startOnlyTape = {
1165
+ ...fullTape,
1166
+ events: fullTape.events.slice(0, fullTape.startOnlyEventCount),
1167
+ };
1168
+ const firstActionTape = sliceFirstPrimaryVerbAction(fullTape);
1169
+ const tempDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'playdrop-causal-gate-'));
1170
+ const paths = {
1171
+ zeroInput: (0, node_path_1.join)(tempDir, 'zero-input.png'),
1172
+ startOnlyReady: (0, node_path_1.join)(tempDir, 'start-only-ready.png'),
1173
+ startOnly: (0, node_path_1.join)(tempDir, 'start-only.png'),
1174
+ firstAction: (0, node_path_1.join)(tempDir, 'first-action.png'),
1175
+ fullTape: (0, node_path_1.join)(tempDir, 'full-tape.png'),
1176
+ };
1177
+ const run = async (label, options) => {
1178
+ console.log(`[upload] Machine causal gate: ${label} from sdk.host.ready().`);
1179
+ const result = await runUploadedHostedLoadCheck({
1180
+ client: input.client,
1181
+ creatorUsername: input.creatorUsername,
1182
+ task: input.task,
1183
+ sessionId: input.sessionId,
1184
+ timeoutMs: input.timeoutMs,
1185
+ token: input.token,
1186
+ currentUser: input.currentUser,
1187
+ captureSession: input.captureSession,
1188
+ ...options,
1189
+ });
1190
+ if (result.status !== 'PASSED') {
1191
+ throw new Error(`Machine causal ${label} run failed. ${formatHostedLoadCheckFailure(input.task.name, result, 'staged')}`);
1192
+ }
1193
+ if (!result.artifactFingerprint) {
1194
+ throw new Error('agent_task_machine_causal_gate_artifact_fingerprint_missing');
1195
+ }
1196
+ return result;
1197
+ };
1198
+ try {
1199
+ const zeroInput = await run('zero-input', {
1200
+ postReadyWaitMs: fullTape.durationMs,
1201
+ gameFrameScreenshotPath: paths.zeroInput,
1202
+ });
1203
+ const startOnly = await run('start-only', {
1204
+ playtestTape: startOnlyTape,
1205
+ readyGameFrameScreenshotPath: paths.startOnlyReady,
1206
+ gameFrameScreenshotPath: paths.startOnly,
1207
+ });
1208
+ const firstAction = await run('first-action', {
1209
+ playtestTape: firstActionTape,
1210
+ gameFrameScreenshotPath: paths.firstAction,
1211
+ });
1212
+ const full = await run('full-tape', {
1213
+ playtestTape: fullTape,
1214
+ gameFrameScreenshotPath: paths.fullTape,
1215
+ });
1216
+ const artifactFingerprint = full.artifactFingerprint;
1217
+ if ([zeroInput, startOnly, firstAction].some((result) => result.artifactFingerprint !== artifactFingerprint)) {
1218
+ throw new Error('agent_task_machine_causal_gate_artifact_changed_during_check');
1219
+ }
1220
+ const comparisons = {
1221
+ startOnlyVsReady: await compareCausalFrames(paths.startOnlyReady, paths.startOnly),
1222
+ startOnlyVsZeroInput: await compareCausalFrames(paths.zeroInput, paths.startOnly),
1223
+ fullTapeVsZeroInput: await compareCausalFrames(paths.zeroInput, paths.fullTape),
1224
+ fullTapeVsStartOnly: await compareCausalFrames(paths.startOnly, paths.fullTape),
1225
+ fullTapeVsFirstAction: await compareCausalFrames(paths.firstAction, paths.fullTape),
1226
+ };
1227
+ if (!comparisons.startOnlyVsReady.passed || !comparisons.startOnlyVsZeroInput.passed) {
1228
+ throw new Error('agent_task_machine_causal_gate_failed:start-only did not provably leave the title state. Fix the start action or ready-relative timing.');
1229
+ }
1230
+ if (!comparisons.fullTapeVsZeroInput.passed || !comparisons.fullTapeVsStartOnly.passed) {
1231
+ throw new Error('agent_task_machine_causal_gate_failed:full tape did not meaningfully beat zero-input and start-only controls. Fix the game or tape before upload.');
1232
+ }
1233
+ if (!comparisons.fullTapeVsFirstAction.passed) {
1234
+ throw new Error('agent_task_machine_causal_gate_failed:first primary action trivially reached the full-tape outcome. Add a sustained causal loop before upload.');
1235
+ }
1236
+ const teachingText = input.teachingText.trim();
1237
+ const evidence = {
1238
+ version: 1,
1239
+ source: 'CLI_MACHINE',
1240
+ artifactFingerprint,
1241
+ primarySurface,
1242
+ primaryVerb: fullTape.primaryVerb,
1243
+ runs: {
1244
+ zeroInput: { finalFrameSha256: await hashFile(paths.zeroInput) },
1245
+ startOnly: {
1246
+ readyFrameSha256: await hashFile(paths.startOnlyReady),
1247
+ finalFrameSha256: await hashFile(paths.startOnly),
1248
+ },
1249
+ firstAction: { finalFrameSha256: await hashFile(paths.firstAction) },
1250
+ fullTape: { finalFrameSha256: await hashFile(paths.fullTape) },
1251
+ },
1252
+ comparisons,
1253
+ teachingText,
1254
+ teachingTextSha256: (0, node_crypto_1.createHash)('sha256').update(teachingText).digest('hex'),
1255
+ };
1256
+ const recorded = await input.client.recordAppUploadCausalCheck(input.creatorUsername, input.task.name, input.sessionId, { evidence });
1257
+ if (recorded.session.causalCheck.status !== 'PASSED') {
1258
+ throw new Error('agent_task_machine_causal_gate_not_recorded');
1259
+ }
1260
+ console.log(`[upload] Machine causal gate passed for ${input.creatorUsername}/${input.task.name}.`);
1261
+ return evidence;
1262
+ }
1263
+ finally {
1264
+ await (0, promises_1.rm)(tempDir, { recursive: true, force: true });
1265
+ }
1266
+ }
@@ -0,0 +1,2 @@
1
+ import type { AppTask } from '../catalogue';
2
+ export declare function assertPrimaryVerbTeachingParity(task: AppTask): string;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertPrimaryVerbTeachingParity = assertPrimaryVerbTeachingParity;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const types_1 = require("@playdrop/types");
7
+ function extractPotentialPlayerText(source, extension) {
8
+ const candidates = new Set();
9
+ if (extension === '.html') {
10
+ const visibleText = source
11
+ .replace(/<!--[\s\S]*?-->/g, ' ')
12
+ .replace(/<(?:script|style)\b[^>]*>[\s\S]*?<\/(?:script|style)>/gi, ' ')
13
+ .replace(/<[^>]+>/g, ' ')
14
+ .replace(/\s+/g, ' ')
15
+ .trim();
16
+ if (visibleText)
17
+ candidates.add(visibleText.slice(0, 300));
18
+ }
19
+ const stringLiteral = /"((?:\\.|[^"\\]){1,300})"|'((?:\\.|[^'\\]){1,300})'|`((?:\\.|[^`\\]){1,300})`/g;
20
+ for (const match of source.matchAll(stringLiteral)) {
21
+ const text = (match[1] ?? match[2] ?? match[3] ?? '')
22
+ .replace(/\\[nrt]/g, ' ')
23
+ .replace(/\s+/g, ' ')
24
+ .trim();
25
+ if (text)
26
+ candidates.add(text);
27
+ }
28
+ return Array.from(candidates);
29
+ }
30
+ function readPrimaryVerbTeachingText(task) {
31
+ const runtimeRoot = (0, node_path_1.dirname)(task.filePath);
32
+ const matches = new Set();
33
+ const walk = (dir) => {
34
+ for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
35
+ const absolute = (0, node_path_1.join)(dir, entry.name);
36
+ if (entry.isDirectory()) {
37
+ if (!['node_modules', '.git', 'assets'].includes(entry.name))
38
+ walk(absolute);
39
+ continue;
40
+ }
41
+ if (!entry.isFile() || !['.html', '.js', '.mjs', '.cjs'].includes((0, node_path_1.extname)(entry.name).toLowerCase()))
42
+ continue;
43
+ const extension = (0, node_path_1.extname)(entry.name).toLowerCase();
44
+ const source = (0, node_fs_1.readFileSync)(absolute, 'utf8');
45
+ for (const text of extractPotentialPlayerText(source, extension)) {
46
+ if (types_1.APP_PLAYTEST_PRIMARY_VERB_VALUES.some((verb) => (0, types_1.appPlaytestTeachingTextMatchesVerb)(text, verb))) {
47
+ matches.add(text);
48
+ }
49
+ }
50
+ }
51
+ };
52
+ walk(runtimeRoot);
53
+ return Array.from(matches);
54
+ }
55
+ function assertPrimaryVerbTeachingParity(task) {
56
+ const primarySurface = task.primarySurface;
57
+ const tape = primarySurface ? task.playtestTapes?.[primarySurface] : null;
58
+ if (!primarySurface || !tape) {
59
+ throw new Error('agent_task_primary_verb_tape_required');
60
+ }
61
+ const teachingTexts = readPrimaryVerbTeachingText(task);
62
+ const matchingText = teachingTexts.find((text) => (0, types_1.appPlaytestTeachingTextMatchesVerb)(text, tape.primaryVerb));
63
+ if (!matchingText) {
64
+ throw new Error(`agent_task_primary_verb_not_taught:${tape.primaryVerb}: show the player a concise instruction using the declared primary verb.`);
65
+ }
66
+ const competingVerbs = types_1.APP_PLAYTEST_PRIMARY_VERB_VALUES
67
+ .filter((verb) => verb !== tape.primaryVerb)
68
+ .filter((verb) => teachingTexts.some((text) => {
69
+ if (!(0, types_1.appPlaytestTeachingTextMatchesVerb)(text, verb))
70
+ return false;
71
+ return verb !== 'tap' || !/\btap\b[^\n]{0,30}\b(?:start|play|begin|continue|retry)\b/i.test(text);
72
+ }));
73
+ if (competingVerbs.length > 0) {
74
+ throw new Error(`agent_task_primary_verb_teaching_mismatch:${tape.primaryVerb}:${competingVerbs.join(',')}`);
75
+ }
76
+ return matchingText;
77
+ }
@@ -23,6 +23,7 @@ export type AppUploadOptions = {
23
23
  agentTaskId?: number;
24
24
  playtestProof?: AgentTaskPlaytestProof;
25
25
  mediaCaptureRequired?: boolean;
26
+ machineCausalGateRequired?: boolean;
26
27
  captureSession?: TaskCaptureSession | null;
27
28
  };
28
29
  export declare function uploadApp(client: ApiClient, task: AppTask, artifacts: AppBuildArtifacts | null, options?: AppUploadOptions): Promise<AppUploadResult>;
@@ -9,6 +9,7 @@ const node_path_1 = require("node:path");
9
9
  const http_1 = require("../http");
10
10
  const loadCheck_1 = require("./loadCheck");
11
11
  const listingPreflight_1 = require("../listingPreflight");
12
+ const playtestContract_1 = require("./playtestContract");
12
13
  const EXTENSION_TO_MIME = {
13
14
  '.png': 'image/png',
14
15
  '.jpg': 'image/jpeg',
@@ -353,6 +354,22 @@ async function uploadAppVersion(client, task, artifacts, options) {
353
354
  await client.recordAppUploadMediaCapture(creatorUsername, task.name, sessionId, mediaCaptureProof);
354
355
  }
355
356
  if (task.hostingMode !== 'EXTERNAL'
357
+ && !task.externalUrl
358
+ && options?.machineCausalGateRequired) {
359
+ const teachingText = (0, playtestContract_1.assertPrimaryVerbTeachingParity)(task);
360
+ await (0, loadCheck_1.runUploadedHostedCausalCheck)({
361
+ client,
362
+ creatorUsername,
363
+ task,
364
+ sessionId,
365
+ teachingText,
366
+ timeoutMs: options.loadCheckTimeoutMs,
367
+ token: options?.token ?? null,
368
+ currentUser: options?.user ?? null,
369
+ captureSession: options?.captureSession,
370
+ });
371
+ }
372
+ else if (task.hostingMode !== 'EXTERNAL'
356
373
  && !task.externalUrl
357
374
  && options?.runStagedUploadLoadCheck) {
358
375
  const loadCheck = await (0, loadCheck_1.runUploadedHostedLoadCheck)({
@@ -222,6 +222,7 @@ async function check(targetArg, options = {}) {
222
222
  });
223
223
  if (tapeSurface && playtestTape) {
224
224
  const idleScreenshotPath = buildComparisonScreenshotPath(screenshotPath, 'idle');
225
+ const startOnlyScreenshotPath = buildComparisonScreenshotPath(screenshotPath, 'start-only');
225
226
  const tapeScreenshotPath = buildComparisonScreenshotPath(screenshotPath, 'tape');
226
227
  console.log(`[check] Running ${playtestTape.durationMs} ms zero-input baseline on ${tapeSurface}.`);
227
228
  const idleResult = await runCheck({
@@ -234,6 +235,22 @@ async function check(targetArg, options = {}) {
234
235
  process.exitCode = 1;
235
236
  return;
236
237
  }
238
+ console.log(`[check] Reopening a clean run and executing only the first ${playtestTape.startOnlyEventCount} startup event(s).`);
239
+ const startOnlyTape = playtestTape.startOnlyEventCount > 0
240
+ ? { ...playtestTape, events: playtestTape.events.slice(0, playtestTape.startOnlyEventCount) }
241
+ : null;
242
+ const startOnlyResult = await runCheck({
243
+ screenshotPath: startOnlyScreenshotPath,
244
+ surface: tapeSurface,
245
+ ...(startOnlyTape
246
+ ? { tape: startOnlyTape }
247
+ : { postReadyWaitMs: playtestTape.durationMs }),
248
+ });
249
+ if (startOnlyResult.status !== 'PASSED') {
250
+ (0, messages_1.printErrorWithHelp)((0, loadCheck_1.formatHostedLoadCheckFailure)(appName, startOnlyResult, 'local'), startOnlyResult.screenshotPath ? [`Start-only screenshot: ${startOnlyResult.screenshotPath}`] : [], { command: 'project check' });
251
+ process.exitCode = 1;
252
+ return;
253
+ }
237
254
  console.log(`[check] Reopening a clean run and executing the ${tapeSurface} tape.`);
238
255
  const tapeResult = await runCheck({
239
256
  screenshotPath: tapeScreenshotPath,
@@ -245,10 +262,11 @@ async function check(targetArg, options = {}) {
245
262
  process.exitCode = 1;
246
263
  return;
247
264
  }
248
- console.log(`[check] Captured matched idle and tape runs for ${currentUsername}/${appName}.`);
265
+ console.log(`[check] Captured matched idle, start-only, and tape runs for ${currentUsername}/${appName}.`);
249
266
  console.log(`[check] Idle: ${idleResult.screenshotPath ?? idleScreenshotPath}`);
267
+ console.log(`[check] Start-only: ${startOnlyResult.screenshotPath ?? startOnlyScreenshotPath}`);
250
268
  console.log(`[check] Tape: ${tapeResult.screenshotPath ?? tapeScreenshotPath}`);
251
- console.log('[check] The tape passes only when visible evidence shows it meaningfully beats idle.');
269
+ console.log('[check] The tape passes only when visible evidence shows it meaningfully beats both controls.');
252
270
  for (const signal of playtestTape.successSignals) {
253
271
  console.log(`[check] Success signal ${signal.kind}: ${signal.description}`);
254
272
  }
@@ -1234,9 +1234,16 @@ function assertAgentNewGamePlaytestContract(task) {
1234
1234
  throw new Error('agent_task_playtest_tapes_required: NEW_GAME catalogue.json must declare one playtest tape per supported surface.');
1235
1235
  }
1236
1236
  for (const surface of task.surfaceTargets) {
1237
- if (!task.playtestTapes[surface]) {
1237
+ const tape = task.playtestTapes[surface];
1238
+ if (!tape) {
1238
1239
  throw new Error(`agent_task_playtest_tape_missing:${surface}`);
1239
1240
  }
1241
+ if (tape.startOnlyEventCount <= 0) {
1242
+ throw new Error(`agent_task_playtest_start_only_required:${surface}: the tape must include an explicit startup action.`);
1243
+ }
1244
+ if (tape.events[0]?.atMs !== 0) {
1245
+ throw new Error(`agent_task_playtest_ready_relative_timing_required:${surface}: the first startup event must run at 0 ms after sdk.host.ready().`);
1246
+ }
1240
1247
  }
1241
1248
  }
1242
1249
  const WORKER_SUPERVISOR_SOURCE_EXCLUDES = [
@@ -2025,6 +2032,7 @@ async function publishWorkerAppProject(input) {
2025
2032
  agentTaskId: input.taskId,
2026
2033
  playtestProof: playtestProof ?? undefined,
2027
2034
  mediaCaptureRequired: input.executionTarget === 'FIRST_PARTY',
2035
+ machineCausalGateRequired: input.kind === 'NEW_GAME',
2028
2036
  captureSession,
2029
2037
  });
2030
2038
  if (!uploadResult.versionCreated || !uploadResult.version) {
@@ -257,7 +257,14 @@ function readAndValidateInstrumentEvidenceManifest(input) {
257
257
  const captures = {};
258
258
  const screenshotIds = new Set();
259
259
  for (const name of types_1.INSTRUMENT_EVIDENCE_NAMES) {
260
- const capture = normalizeCapture(parsed.captures?.[name], name);
260
+ const rawCapture = parsed.captures?.[name];
261
+ if (!rawCapture) {
262
+ if (types_1.REQUIRED_INSTRUMENT_EVIDENCE_NAMES.includes(name)) {
263
+ throw new Error(`invalid_instrument_evidence_capture:${name}`);
264
+ }
265
+ continue;
266
+ }
267
+ const capture = normalizeCapture(rawCapture, name);
261
268
  if (capture.tabId !== controlledTabId) {
262
269
  throw new Error(`instrument_evidence_tab_mismatch:${controlledTabId}:${capture.tabId}`);
263
270
  }
@@ -5048,7 +5048,7 @@ async function submitEvalTask(options) {
5048
5048
  for (const name of ['first-frame', 'core', 'win', 'loss']) {
5049
5049
  const capture = manifest.captures[name];
5050
5050
  if (!capture) {
5051
- throw new Error(`missing_instrument_evidence_capture:${manifest.groupId}:${name}`);
5051
+ continue;
5052
5052
  }
5053
5053
  const filePath = node_path_1.default.join(node_path_1.default.resolve(evidenceDir), capture.fileName);
5054
5054
  evidenceFiles.push({
@@ -1,4 +1,4 @@
1
- import type { AbortAppUploadResponse, AbortAssetPackUploadResponse, AdInterstitialShowResponse, AdLoadRequest, AdLoadResponse, AdRewardedShowResponse, AdShowRequest, AdminAgentTaskDetailResponse, AdminAgentTaskTranscriptResponse, AdminAgentTasksResponse, AdminAgentWorkersStatusResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminAppsListResponse, AdminCreatorEngagementExecutionsResponse, AdminCreatorEngagementRulePreviewRequest, AdminCreatorEngagementRulePreviewResponse, AdminCreatorEngagementRulesResponse, AdminCreatorEngagementUserResponse, AdminCreditTransactionsResponse, AdminEnqueueNextAppVersionReviewTaskResponse, AdminFeedbackListResponse, AdminFeedbackResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminPaymentDetailResponse, AdminPaymentsListResponse, AdminPlatformAnalyticsRequest, AdminPlatformAnalyticsResponse, AdminRefundPaymentRequest, AdminRetirePlayerMetaDefinitionResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, AdminUpdateAppRequest, AdminUpdateAppResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, AdminUserResponse, AgentTaskAttachmentUploadResponse, AgentTaskAvailabilityResponse, AgentTaskCreatorFeedbackStatus, AgentTaskDetailResponse, AgentTaskEventsResponse, AgentTaskKind, AgentTaskNewGameSuggestionsResponse, AgentTasksResponse, AiGenerationDetailResponse, AiGenerationsResponse, ApiKeyLoginRequest, AppAccessTokenResponse, AppAgentTaskResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppLogEntryRequest, AppLogEntryResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, AppRuntimeAssetsResponse, AppSourceBundleResponse, AppType, AppUploadLaunchCheckPreviewResponse, AppVersionDetailResponse, AppVersionVisibility, AppVersionsListResponse, AppleCreditPackFulfillmentRequest, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePendingSignupResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, AppleSignInRequest, AppleSignInResponse, AppsListResponse, AssetBrowseKind, AssetCategoriesResponse, AssetCategory, AssetDetailResponse, AssetFormat, AssetListResponse, AssetListSort, AssetPackDetailResponse, AssetPackListResponse, AssetPackListSort, AssetPackSourceBundleResponse, AssetPackVersionsListResponse, AssetPurchaseRequest, AssetSourceBundleResponse, AssetSourceKind, AssetSpecAppsListResponse, AssetSpecAssetsListResponse, AssetSpecDetailResponse, AssetSpecListResponse, AssetSpecListSort, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetVersionsListResponse, AssetVisibility, BatchCreateUsersResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, BootstrapResponse, CancelAgentTaskResponse, ChangePasswordRequest, ChangePasswordResponse, ClaimFreeCreditRewardResponse, ClearAgentTaskResponse, CliApiKeyStatusResponse, CliApiTokenSecretResponse, CliApiTokensResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, CompleteXSignupRequest, CompleteXSignupResponse, NativeOAuthHandoffExchangeRequest, NativeOAuthHandoffExchangeResponse, NativeOAuthLinkStartRequest, NativeOAuthLinkStartResponse, ContentCommentsResponse, ContentLicense, ContentRemixRelationsResponse, CreateAdminGameCollectionRequest, CreateAppAgentTaskRequest, CreateAppAgentTaskResponse, CreateAppRequest, CreateAppResponse, CreateAssetSpecVersionResponse, CreateAssetVersionResponse, CreateCliApiKeyResponse, CreateCliApiTokenRequest, CreateCommentReportRequest, CreateContentCommentRequest, CreateContentCommentResponse, CreateContentReportRequest, CreateCreatorReportRequest, CreateGameEvalAgentTaskRequest, CreateGameEvalAgentTaskResponse, CreateNewGameAgentTaskRequest, CreateNewGameAgentTaskResponse, CreateRemixGameAgentTaskRequest, CreateRemixGameAgentTaskResponse, CreateTagGroupRequest, CreateTagRequest, CreateUserRequest, CreateUserResponse, CreatorAppsBrowseRequest, CreatorBlockMutationResponse, CreatorEarningsResponse, CreatorEngagementRuleExecutionStatus, CreatorFollowMutationResponse, CreatorGameCreatorFeedbackStatusResponse, CreatorGameShareResponse, CreatorGameStatusResponse, CreatorGameVersionsResponse, CreatorGamesListResponse, CreditPackCheckoutSessionRequest, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreditPackResponse, CreditPacksResponse, DeleteAdminGameCollectionResponse, DeleteAdminUserResponse, DeleteAppResponse, DeleteAssetPackResponse, DeleteAssetPackVersionResponse, DeleteAssetResponse, DeleteAssetSpecResponse, DeleteAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteCliApiTokenResponse, DeleteContentCommentResponse, DeleteFeedbackResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, DeleteOwnedCustomAssetResponse, DeleteVersionResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, DislikedItemMutationResponse, EmailPreferencesResponse, EmailUnsubscribeResponse, FeedbackCategory, FeedbackClient, FeedbackListResponse, FeedbackStatus, FetchHomeOptions, FinalizeAppUploadResponse, FinalizeAssetPackUploadResponse, FirebasePushDeviceRequest, FirebasePushDeviceStatusResponse, FollowingCreatorsResponse, FollowingFeedResponse, FollowingFeedSection, FollowingSort, FreeCreditsResponse, GetAchievementResponse, GetLeaderboardResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, GoogleCreditPackFulfillmentRequest, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, GooglePendingSignupResponse, HomeCollectionPageResponse, HomeResponse, IapPurchaseRequest, IapPurchaseResponse, IapReceiptListParams, IapReceiptResponse, IapReceiptsResponse, InitializeAppUploadRequest, InitializeAppUploadResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, InvitePreviewResponse, LibraryCategory, LibraryResponse, LibrarySort, LikedItemMutationResponse, ListAchievementsResponse, ListLeaderboardsResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, LoginRequest, LoginResponse, LogoutResponse, MeAgentWorkersResponse, MeResponse, MobileCreditPackFulfillmentResponse, ModerationReportResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, MyAchievementsResponse, MyAppsResponse, MyInviteCodeResponse, MyLeaderboardsResponse, NotificationReadResponse, NotificationStatus, NotificationsResponse, OwnedProfileAssetsResponse, PaymentProvider, PaymentStatus, PrivacyExportResponse, PublicEmailPreferencesResponse, PublicEmailPreferencesUpdateRequest, PurchaseResponse, PushSubscriptionMutationResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, RecordAppUploadMediaCaptureRequest, RecordAppUploadMediaCaptureResponse, RegisterRequest, RegisterResponse, RemixScaffoldResponse, ResolveRuntimeAssetsRequest, ResolveRuntimeAssetsResponse, RetryAgentTaskResponse, ReviewState, SavedItemKind, SavedItemMutationResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SetProfileAssetRequest, SetProfileAssetResponse, ShopResponse, StarterProfileAssetsResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, TagDetailResponse, TagDirectoryResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, TemplateScaffoldResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, TransactionsResponse, UnlinkGoogleResponse, UnlinkXResponse, UnlockAchievementResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, UpdateAdminUserRequest, UpdateAppRequest, UpdateAppResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetSpecVersionResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateProfileRequest, UpdateProfileResponse, UpdateTagGroupRequest, UpdateTagRequest, UpdateUserCommunicationPreferencesRequest, UpdateVersionResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, UpsertWebPushSubscriptionRequest, UserDetailResponse, UsernameAvailableResponse, UsersListResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, WorkerAgentTaskAttemptUnavailableRequest, WorkerAgentTaskAttemptUnavailableResponse, WorkerClaimAgentTaskRequest, WorkerClaimAgentTaskResponse, WorkerClaimAgentTaskSlugRequest, WorkerClaimAgentTaskSlugResponse, WorkerCompleteAgentTaskRequest, WorkerCreateAgentTaskEventRequest, WorkerCreateAgentTaskEventResponse, WorkerCreateAgentTaskTranscriptChunkRequest, WorkerCreateAgentTaskTranscriptChunkResponse, WorkerFailAgentTaskRequest, WorkerHeartbeatAgentTaskRequest, WorkerHeartbeatAgentTaskResponse, WorkerPresenceRequest, WorkerPresenceResponse, RegisterAgentBundleRequest, RegisterAgentBundleResponse, WorkerRecordAgentTaskRunTelemetryRequest, WorkerRecordAgentTaskRunTelemetryResponse, WorkerSubmitAgentTaskEvalRequest, WorkerSubmitAgentTaskEvalResponse, WorkerSubmitAgentTaskReviewRequest, WorkerSubmitAgentTaskReviewResponse, WorkerUpdateIntentRequest, WorkerUpdateIntentResponse, XPendingSignupResponse } from '@playdrop/types';
1
+ import type { AbortAppUploadResponse, AbortAssetPackUploadResponse, AdInterstitialShowResponse, AdLoadRequest, AdLoadResponse, AdRewardedShowResponse, AdShowRequest, AdminAgentTaskDetailResponse, AdminAgentTaskTranscriptResponse, AdminAgentTasksResponse, AdminAgentWorkersStatusResponse, AdminAppPlayerMetaResponse, AdminAppPlayerMetaUserResponse, AdminAppVersionReviewsListResponse, AdminAppsListResponse, AdminCreatorEngagementExecutionsResponse, AdminCreatorEngagementRulePreviewRequest, AdminCreatorEngagementRulePreviewResponse, AdminCreatorEngagementRulesResponse, AdminCreatorEngagementUserResponse, AdminCreditTransactionsResponse, AdminEnqueueNextAppVersionReviewTaskResponse, AdminFeedbackListResponse, AdminFeedbackResponse, AdminGameCollectionCandidatesResponse, AdminGameCollectionResponse, AdminGameCollectionsResponse, AdminMutateAchievementStateRequest, AdminMutateAchievementStateResponse, AdminMutateLeaderboardScoreRequest, AdminMutateLeaderboardScoreResponse, AdminPaymentDetailResponse, AdminPaymentsListResponse, AdminPlatformAnalyticsRequest, AdminPlatformAnalyticsResponse, AdminRefundPaymentRequest, AdminRetirePlayerMetaDefinitionResponse, AdminTagGroupResponse, AdminTagGroupsResponse, AdminTagResponse, AdminUpdateAppRequest, AdminUpdateAppResponse, AdminUpdateAppVersionReviewRequest, AdminUpdateAppVersionReviewResponse, AdminUserResponse, AgentTaskAttachmentUploadResponse, AgentTaskAvailabilityResponse, AgentTaskCreatorFeedbackStatus, AgentTaskDetailResponse, AgentTaskEventsResponse, AgentTaskKind, AgentTaskNewGameSuggestionsResponse, AgentTasksResponse, AiGenerationDetailResponse, AiGenerationsResponse, ApiKeyLoginRequest, AppAccessTokenResponse, AppAgentTaskResponse, AppBrowseRequest, AppDetailRequest, AppDetailResponse, AppLogEntryRequest, AppLogEntryResponse, AppMoreCollectionKey, AppMoreCollectionPage, AppMoreCollectionRequest, AppMoreCollectionsRequest, AppMoreCollectionsResponse, AppRuntimeAssetsResponse, AppSourceBundleResponse, AppType, AppUploadLaunchCheckPreviewResponse, AppVersionDetailResponse, AppVersionVisibility, AppVersionsListResponse, AppleCreditPackFulfillmentRequest, AppleLinkRequest, AppleLinkResponse, AppleNativeChallengeResponse, AppleOAuthHandoffExchangeRequest, AppleOAuthHandoffExchangeResponse, AppleOAuthLinkStartRequest, AppleOAuthLinkStartResponse, ApplePendingSignupResponse, ApplePushDeviceRequest, ApplePushDeviceStatusResponse, AppleSignInRequest, AppleSignInResponse, AppsListResponse, AssetBrowseKind, AssetCategoriesResponse, AssetCategory, AssetDetailResponse, AssetFormat, AssetListResponse, AssetListSort, AssetPackDetailResponse, AssetPackListResponse, AssetPackListSort, AssetPackSourceBundleResponse, AssetPackVersionsListResponse, AssetPurchaseRequest, AssetSourceBundleResponse, AssetSourceKind, AssetSpecAppsListResponse, AssetSpecAssetsListResponse, AssetSpecDetailResponse, AssetSpecListResponse, AssetSpecListSort, AssetSpecVersionResponseEnvelope, AssetSpecVersionsListResponse, AssetVersionsListResponse, AssetVisibility, BatchCreateUsersResponse, BatchUpsertNodeRelationsRequest, BatchUpsertNodeRelationsResponse, BoostActivationRequest, BoostBalanceResponse, BoostHistoryResponse, BoostPurchaseRequest, BoostPurchaseResponse, BoostReportQuery, BoostReportResponse, BoostStatusResponse, BootstrapResponse, CancelAgentTaskResponse, ChangePasswordRequest, ChangePasswordResponse, ClaimFreeCreditRewardResponse, ClearAgentTaskResponse, CliApiKeyStatusResponse, CliApiTokenSecretResponse, CliApiTokensResponse, CliLoginApproveRequest, CliLoginApproveResponse, CliLoginPollRequest, CliLoginPollResponse, CliLoginStartResponse, CliLoginStatusResponse, CliWebLaunchStartRequest, CliWebLaunchStartResponse, CompleteAppleSignupRequest, CompleteAppleSignupResponse, CompleteGoogleSignupRequest, CompleteGoogleSignupResponse, CompleteXSignupRequest, CompleteXSignupResponse, NativeOAuthHandoffExchangeRequest, NativeOAuthHandoffExchangeResponse, NativeOAuthLinkStartRequest, NativeOAuthLinkStartResponse, ContentCommentsResponse, ContentLicense, ContentRemixRelationsResponse, CreateAdminGameCollectionRequest, CreateAppAgentTaskRequest, CreateAppAgentTaskResponse, CreateAppRequest, CreateAppResponse, CreateAssetSpecVersionResponse, CreateAssetVersionResponse, CreateCliApiKeyResponse, CreateCliApiTokenRequest, CreateCommentReportRequest, CreateContentCommentRequest, CreateContentCommentResponse, CreateContentReportRequest, CreateCreatorReportRequest, CreateGameEvalAgentTaskRequest, CreateGameEvalAgentTaskResponse, CreateNewGameAgentTaskRequest, CreateNewGameAgentTaskResponse, CreateRemixGameAgentTaskRequest, CreateRemixGameAgentTaskResponse, CreateTagGroupRequest, CreateTagRequest, CreateUserRequest, CreateUserResponse, CreatorAppsBrowseRequest, CreatorBlockMutationResponse, CreatorEarningsResponse, CreatorEngagementRuleExecutionStatus, CreatorFollowMutationResponse, CreatorGameCreatorFeedbackStatusResponse, CreatorGameShareResponse, CreatorGameStatusResponse, CreatorGameVersionsResponse, CreatorGamesListResponse, CreditPackCheckoutSessionRequest, CreditPackCheckoutSessionResponse, CreditPackCheckoutStatusResponse, CreditPackResponse, CreditPacksResponse, DeleteAdminGameCollectionResponse, DeleteAdminUserResponse, DeleteAppResponse, DeleteAssetPackResponse, DeleteAssetPackVersionResponse, DeleteAssetResponse, DeleteAssetSpecResponse, DeleteAssetSpecVersionResponse, DeleteAssetVersionResponse, DeleteCliApiTokenResponse, DeleteContentCommentResponse, DeleteFeedbackResponse, DeleteMyAccountRequest, DeleteMyAccountResponse, DeleteOwnedCustomAssetResponse, DeleteVersionResponse, DevAppAccessTokenResponse, DevPlayerResetResponse, DislikedItemMutationResponse, EmailPreferencesResponse, EmailUnsubscribeResponse, FeedbackCategory, FeedbackClient, FeedbackListResponse, FeedbackStatus, FetchHomeOptions, FinalizeAppUploadResponse, FinalizeAssetPackUploadResponse, FirebasePushDeviceRequest, FirebasePushDeviceStatusResponse, FollowingCreatorsResponse, FollowingFeedResponse, FollowingFeedSection, FollowingSort, FreeCreditsResponse, GetAchievementResponse, GetLeaderboardResponse, GoogleAppleLinkRequest, GoogleAppleLinkResponse, GoogleAppleSignInRequest, GoogleAppleSignInResponse, GoogleCreditPackFulfillmentRequest, GoogleOneTapExchangeRequest, GoogleOneTapExchangeResponse, GooglePendingSignupResponse, HomeCollectionPageResponse, HomeResponse, IapPurchaseRequest, IapPurchaseResponse, IapReceiptListParams, IapReceiptResponse, IapReceiptsResponse, InitializeAppUploadRequest, InitializeAppUploadResponse, InitializeAssetPackUploadRequest, InitializeAssetPackUploadResponse, InvitePreviewResponse, LibraryCategory, LibraryResponse, LibrarySort, LikedItemMutationResponse, ListAchievementsResponse, ListLeaderboardsResponse, ListOwnedCustomAssetsResponse, LoadOwnedCustomAssetResponse, LoginRequest, LoginResponse, LogoutResponse, MeAgentWorkersResponse, MeResponse, MobileCreditPackFulfillmentResponse, ModerationReportResponse, MutateOwnedCustomAssetRequest, MutateOwnedCustomAssetResponse, MyAchievementsResponse, MyAppsResponse, MyInviteCodeResponse, MyLeaderboardsResponse, NotificationReadResponse, NotificationStatus, NotificationsResponse, OwnedProfileAssetsResponse, PaymentProvider, PaymentStatus, PrivacyExportResponse, PublicEmailPreferencesResponse, PublicEmailPreferencesUpdateRequest, PurchaseResponse, PushSubscriptionMutationResponse, RecordAppUploadLaunchCheckRequest, RecordAppUploadLaunchCheckResponse, RecordAppUploadCausalCheckRequest, RecordAppUploadCausalCheckResponse, RecordAppUploadMediaCaptureRequest, RecordAppUploadMediaCaptureResponse, RegisterRequest, RegisterResponse, RemixScaffoldResponse, ResolveRuntimeAssetsRequest, ResolveRuntimeAssetsResponse, RetryAgentTaskResponse, ReviewState, SavedItemKind, SavedItemMutationResponse, SearchRequest, SearchResponse, SearchSuggestRequest, SearchSuggestResponse, SetAchievementProgressRequest, SetAchievementProgressResponse, SetProfileAssetRequest, SetProfileAssetResponse, ShopResponse, StarterProfileAssetsResponse, SubmitFeedbackRequest, SubmitFeedbackResponse, SubmitLeaderboardScoreRequest, SubmitLeaderboardScoreResponse, TagDetailResponse, TagDirectoryResponse, TagGroupDetailResponse, TagGroupSort, TagListResponse, TemplateScaffoldResponse, TrackEngagementEventRequest, TrackEngagementEventResponse, TransactionsResponse, UnlinkGoogleResponse, UnlinkXResponse, UnlockAchievementResponse, UpdateAdminFeedbackRequest, UpdateAdminFeedbackResponse, UpdateAdminGameCollectionGamesRequest, UpdateAdminGameCollectionRequest, UpdateAdminUserRequest, UpdateAppRequest, UpdateAppResponse, UpdateAssetPackRequest, UpdateAssetPackResponse, UpdateAssetPackVersionRequest, UpdateAssetPackVersionResponse, UpdateAssetRequest, UpdateAssetResponse, UpdateAssetSpecVersionResponse, UpdateAssetVersionRequest, UpdateAssetVersionResponse, UpdateProfileRequest, UpdateProfileResponse, UpdateTagGroupRequest, UpdateTagRequest, UpdateUserCommunicationPreferencesRequest, UpdateVersionResponse, UploadAppSessionFileResponse, UploadAppSessionOwnedAssetResponse, UploadAssetPackSessionAssetResponse, UploadAssetPackSessionMediaResponse, UpsertWebPushSubscriptionRequest, UserDetailResponse, UsernameAvailableResponse, UsersListResponse, WebPushConfigResponse, WebPushSubscriptionStatusResponse, WorkerAgentTaskAttemptUnavailableRequest, WorkerAgentTaskAttemptUnavailableResponse, WorkerClaimAgentTaskRequest, WorkerClaimAgentTaskResponse, WorkerClaimAgentTaskSlugRequest, WorkerClaimAgentTaskSlugResponse, WorkerCompleteAgentTaskRequest, WorkerCreateAgentTaskEventRequest, WorkerCreateAgentTaskEventResponse, WorkerCreateAgentTaskTranscriptChunkRequest, WorkerCreateAgentTaskTranscriptChunkResponse, WorkerFailAgentTaskRequest, WorkerHeartbeatAgentTaskRequest, WorkerHeartbeatAgentTaskResponse, WorkerPresenceRequest, WorkerPresenceResponse, RegisterAgentBundleRequest, RegisterAgentBundleResponse, WorkerRecordAgentTaskRunTelemetryRequest, WorkerRecordAgentTaskRunTelemetryResponse, WorkerSubmitAgentTaskEvalRequest, WorkerSubmitAgentTaskEvalResponse, WorkerSubmitAgentTaskReviewRequest, WorkerSubmitAgentTaskReviewResponse, WorkerUpdateIntentRequest, WorkerUpdateIntentResponse, XPendingSignupResponse } from '@playdrop/types';
2
2
  import { type AdminAgentTaskTranscriptOptions, type ListAgentTasksOptions, type ListCreatorGamesOptions } from './domains/agent-tasks.js';
3
3
  export interface HttpMethod {
4
4
  GET: 'GET';
@@ -464,6 +464,7 @@ export interface ApiClient {
464
464
  finalizeAppUpload(creatorUsername: string, appName: string, sessionId: string): Promise<FinalizeAppUploadResponse>;
465
465
  fetchAppUploadLaunchCheckPreview(creatorUsername: string, appName: string, sessionId: string): Promise<AppUploadLaunchCheckPreviewResponse>;
466
466
  recordAppUploadLaunchCheck(creatorUsername: string, appName: string, sessionId: string, request: RecordAppUploadLaunchCheckRequest): Promise<RecordAppUploadLaunchCheckResponse>;
467
+ recordAppUploadCausalCheck(creatorUsername: string, appName: string, sessionId: string, request: RecordAppUploadCausalCheckRequest): Promise<RecordAppUploadCausalCheckResponse>;
467
468
  recordAppUploadMediaCapture(creatorUsername: string, appName: string, sessionId: string, request: RecordAppUploadMediaCaptureRequest): Promise<RecordAppUploadMediaCaptureResponse>;
468
469
  abortAppUpload(creatorUsername: string, appName: string, sessionId: string): Promise<AbortAppUploadResponse>;
469
470
  listAppVersions(creatorUsername: string, appName: string): Promise<AppVersionsListResponse>;