deepline 0.1.12 → 0.1.20

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 (82) hide show
  1. package/README.md +14 -6
  2. package/dist/cli/index.js +1346 -717
  3. package/dist/cli/index.mjs +1342 -713
  4. package/dist/index.d.mts +199 -23
  5. package/dist/index.d.ts +199 -23
  6. package/dist/index.js +221 -14
  7. package/dist/index.mjs +221 -14
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
  9. package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
  10. package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
  11. package/dist/repo/sdk/src/client.ts +237 -0
  12. package/dist/repo/sdk/src/config.ts +125 -8
  13. package/dist/repo/sdk/src/http.ts +29 -5
  14. package/dist/repo/sdk/src/play.ts +19 -36
  15. package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
  16. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  17. package/dist/repo/sdk/src/types.ts +25 -0
  18. package/dist/repo/sdk/src/version.ts +2 -2
  19. package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
  20. package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
  21. package/dist/repo/shared_libs/plays/dataset.ts +28 -0
  22. package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
  23. package/package.json +5 -4
  24. package/dist/cli/index.js.map +0 -1
  25. package/dist/cli/index.mjs.map +0 -1
  26. package/dist/index.js.map +0 -1
  27. package/dist/index.mjs.map +0 -1
  28. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  29. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  30. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  31. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  32. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  33. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  34. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  35. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  36. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  37. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  38. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
  39. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  40. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
  41. package/dist/repo/sdk/src/cli/index.ts +0 -148
  42. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  43. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  44. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  45. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  46. package/dist/repo/sdk/src/compat.ts +0 -77
  47. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  48. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  49. package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
  50. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  51. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
  52. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  53. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  54. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  55. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  56. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  57. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  58. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  59. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  60. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  61. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  62. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  63. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  64. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  65. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  66. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  67. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  68. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  69. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  70. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  71. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  72. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  73. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  74. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  75. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  76. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  77. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  78. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  79. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  80. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
  81. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  82. package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
@@ -156,6 +156,7 @@ interface CoordinatorEnv {
156
156
  DEEPLINE_API_BASE_URL: string;
157
157
  DEEPLINE_INTERNAL_TOKEN?: string;
158
158
  DEEPLINE_TAIL_LOG_TOKEN?: string;
159
+ DEEPLINE_COORDINATOR_DEPLOY_MARKER?: string;
159
160
  VERCEL_PROTECTION_BYPASS_TOKEN?: string;
160
161
  DEEPLINE_PLAY_PREVIEW_SLUG?: string;
161
162
  /**
@@ -367,6 +368,9 @@ const WORKFLOW_POOL_TTL_MS = 8 * 60 * 1000;
367
368
  const WORKFLOW_POOL_TARGET_SIZE = 2;
368
369
  const WORKFLOW_POOL_READY_TIMEOUT_MS = 1_500;
369
370
  const WORKFLOW_POOL_READY_POLL_MS = 250;
371
+ const WORKFLOW_POOL_REFILL_ON_MISS_TIMEOUT_MS = 2_500;
372
+ const WORKFLOW_POOL_REFILL_ON_MISS_MIN_AVAILABLE = 1;
373
+ const WORKFLOW_POOL_CONTROL_TIMEOUT_MS = 750;
370
374
 
371
375
  function buildDynamicWorkflowMetadata(
372
376
  params: PlayWorkflowParams,
@@ -467,26 +471,48 @@ function workflowPoolDurableObject(env: CoordinatorEnv): DurableObjectStub {
467
471
  async function callWorkflowPool<T>(
468
472
  env: CoordinatorEnv,
469
473
  path: string,
470
- init?: RequestInit,
474
+ init?: RequestInit & { timeoutMs?: number },
471
475
  ): Promise<T> {
472
- const response = await workflowPoolDurableObject(env).fetch(
473
- `https://deepline.workflow-pool.internal${path}`,
474
- {
475
- ...init,
476
- headers: {
477
- 'content-type': 'application/json',
478
- ...(init?.headers ?? {}),
479
- },
480
- },
476
+ const timeoutMs = Math.max(
477
+ 1,
478
+ Math.floor(init?.timeoutMs ?? WORKFLOW_POOL_CONTROL_TIMEOUT_MS),
481
479
  );
482
- if (!response.ok) {
483
- throw new Error(
484
- `workflow pool ${path} failed ${response.status}: ${(
485
- await response.text().catch(() => '')
486
- ).slice(0, 400)}`,
480
+ const controller = new AbortController();
481
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
482
+ try {
483
+ const fetchInit: RequestInit = { ...(init ?? {}) };
484
+ delete (fetchInit as { timeoutMs?: number }).timeoutMs;
485
+ delete fetchInit.signal;
486
+ const response = await workflowPoolDurableObject(env).fetch(
487
+ `https://deepline.workflow-pool.internal${path}`,
488
+ {
489
+ ...fetchInit,
490
+ signal: controller.signal,
491
+ headers: {
492
+ 'content-type': 'application/json',
493
+ ...(init?.headers ?? {}),
494
+ },
495
+ },
487
496
  );
497
+ if (!response.ok) {
498
+ throw new Error(
499
+ `workflow pool ${path} failed ${response.status}: ${(
500
+ await response.text().catch(() => '')
501
+ ).slice(0, 400)}`,
502
+ );
503
+ }
504
+ return (await response.json()) as T;
505
+ } catch (error) {
506
+ if (
507
+ error instanceof Error &&
508
+ (error.name === 'AbortError' || error.message.includes('aborted'))
509
+ ) {
510
+ throw new Error(`workflow pool ${path} timed out after ${timeoutMs}ms`);
511
+ }
512
+ throw error;
513
+ } finally {
514
+ clearTimeout(timer);
488
515
  }
489
- return (await response.json()) as T;
490
516
  }
491
517
 
492
518
  type WorkflowPoolCounts = {
@@ -901,7 +927,11 @@ async function submitViaPooledWorkflow(input: {
901
927
  return null;
902
928
  }
903
929
  const leaseStartedAt = Date.now();
904
- const pooledInstanceId = await leaseWorkflowPoolId(input.env);
930
+ let leaseError: string | null = null;
931
+ let pooledInstanceId = await leaseWorkflowPoolId(input.env).catch((error) => {
932
+ leaseError = error instanceof Error ? error.message : String(error);
933
+ return null;
934
+ });
905
935
  const missCounts = pooledInstanceId
906
936
  ? null
907
937
  : await workflowPoolCount(input.env).catch(() => null);
@@ -911,6 +941,7 @@ async function submitViaPooledWorkflow(input: {
911
941
  graphHash: input.params.graphHash ?? null,
912
942
  extra: {
913
943
  pooled: Boolean(pooledInstanceId),
944
+ ...(leaseError ? { error: leaseError } : {}),
914
945
  ...(missCounts
915
946
  ? {
916
947
  availableAfterMiss: missCounts.available,
@@ -919,43 +950,79 @@ async function submitViaPooledWorkflow(input: {
919
950
  : {}),
920
951
  },
921
952
  });
953
+
954
+ if (!pooledInstanceId) {
955
+ // A pool miss is often a timing gap rather than a true lack of warm
956
+ // capacity. Wait briefly for refill/promotion, then retry lease once
957
+ // before falling back to a cold workflow create.
958
+ const refillStartedAt = Date.now();
959
+ const refillResult = await refillWorkflowPool(input.env, {
960
+ waitReady: true,
961
+ minAvailable: WORKFLOW_POOL_REFILL_ON_MISS_MIN_AVAILABLE,
962
+ waitTimeoutMs: WORKFLOW_POOL_REFILL_ON_MISS_TIMEOUT_MS,
963
+ }).catch(() => null);
964
+ input.recordSubmitTiming({
965
+ phase: 'coordinator.workflow_pool_refill_on_miss',
966
+ ms: Date.now() - refillStartedAt,
967
+ graphHash: input.params.graphHash ?? null,
968
+ extra:
969
+ refillResult === null
970
+ ? { ok: false }
971
+ : {
972
+ ok: true,
973
+ available: refillResult.available,
974
+ warming: refillResult.warming,
975
+ created: refillResult.created,
976
+ promoted: refillResult.promoted,
977
+ removed: refillResult.removed,
978
+ waitedMs: refillResult.waitedMs,
979
+ waitIterations: refillResult.waitIterations,
980
+ },
981
+ });
982
+ if (refillResult?.available) {
983
+ const retryStartedAt = Date.now();
984
+ let retryLeaseError: string | null = null;
985
+ pooledInstanceId = await leaseWorkflowPoolId(input.env).catch((error) => {
986
+ retryLeaseError = error instanceof Error ? error.message : String(error);
987
+ return null;
988
+ });
989
+ input.recordSubmitTiming({
990
+ phase: 'coordinator.workflow_pool_lease_retry',
991
+ ms: Date.now() - retryStartedAt,
992
+ graphHash: input.params.graphHash ?? null,
993
+ extra: {
994
+ pooled: Boolean(pooledInstanceId),
995
+ ...(retryLeaseError ? { error: retryLeaseError } : {}),
996
+ },
997
+ });
998
+ }
999
+ }
1000
+
922
1001
  if (!pooledInstanceId) {
923
1002
  return null;
924
1003
  }
925
1004
 
926
1005
  const instance = await input.env.PLAY_WORKFLOW.get(pooledInstanceId);
1006
+ const readyCheckStartedAt = Date.now();
1007
+ const status = await instance.status().catch(() => null);
1008
+ const statusName = workflowStatusName(status);
1009
+ input.recordSubmitTiming({
1010
+ phase: 'coordinator.workflow_pool_ready_check',
1011
+ ms: Date.now() - readyCheckStartedAt,
1012
+ graphHash: input.params.graphHash ?? null,
1013
+ extra: { instanceId: pooledInstanceId, status: statusName },
1014
+ });
1015
+ if (!workflowPoolStatusIsReady(statusName)) {
1016
+ await instance.terminate().catch(() => undefined);
1017
+ disposeRpcStub(instance);
1018
+ return null;
1019
+ }
1020
+ const sendStartedAt = Date.now();
927
1021
  try {
928
- const readyCheckStartedAt = Date.now();
929
- const status = await instance.status().catch(() => null);
930
- const statusName = workflowStatusName(status);
931
- input.recordSubmitTiming({
932
- phase: 'coordinator.workflow_pool_ready_check',
933
- ms: Date.now() - readyCheckStartedAt,
934
- graphHash: input.params.graphHash ?? null,
935
- extra: { instanceId: pooledInstanceId, status: statusName },
936
- });
937
- if (!workflowPoolStatusIsReady(statusName)) {
938
- await instance.terminate().catch(() => undefined);
939
- disposeRpcStub(instance);
940
- return null;
941
- }
942
- const sendStartedAt = Date.now();
943
1022
  await instance.sendEvent({
944
1023
  type: WORKFLOW_POOL_START_EVENT_TYPE,
945
1024
  payload: buildDispatcherEnvelope(input.params),
946
1025
  });
947
- await mapRunToWorkflowInstance({
948
- env: input.env,
949
- runId: input.params.runId,
950
- instanceId: pooledInstanceId,
951
- });
952
- input.recordSubmitTiming({
953
- phase: 'coordinator.workflow_pool_send_event',
954
- ms: Date.now() - sendStartedAt,
955
- graphHash: input.params.graphHash ?? null,
956
- extra: { instanceId: pooledInstanceId },
957
- });
958
- return instance;
959
1026
  } catch (error) {
960
1027
  disposeRpcStub(instance);
961
1028
  console.warn('[coordinator.workflow_pool] sendEvent failed; falling back', {
@@ -965,6 +1032,27 @@ async function submitViaPooledWorkflow(input: {
965
1032
  });
966
1033
  return null;
967
1034
  }
1035
+ try {
1036
+ await mapRunToWorkflowInstance({
1037
+ env: input.env,
1038
+ runId: input.params.runId,
1039
+ instanceId: pooledInstanceId,
1040
+ });
1041
+ } catch (error) {
1042
+ disposeRpcStub(instance);
1043
+ throw new Error(
1044
+ `workflow pool mapRunToWorkflowInstance failed after pooled workflow start for ${input.params.runId}: ${
1045
+ error instanceof Error ? error.message : String(error)
1046
+ }`,
1047
+ );
1048
+ }
1049
+ input.recordSubmitTiming({
1050
+ phase: 'coordinator.workflow_pool_send_event',
1051
+ ms: Date.now() - sendStartedAt,
1052
+ graphHash: input.params.graphHash ?? null,
1053
+ extra: { instanceId: pooledInstanceId },
1054
+ });
1055
+ return instance;
968
1056
  }
969
1057
 
970
1058
  function readWorkflowPayload(event: unknown): Record<string, unknown> | null {
@@ -1986,6 +2074,29 @@ const coordinatorEntrypoint = {
1986
2074
  if (authError) return authError;
1987
2075
  return await handleCoordinatorWarmup(request, env, ctx);
1988
2076
  }
2077
+ if (url.pathname === '/tail-log-token/probe') {
2078
+ const authError = authorizeCoordinatorControlRequest({ request, env });
2079
+ if (authError) return authError;
2080
+ const expectedTailLogToken = env.DEEPLINE_TAIL_LOG_TOKEN?.trim();
2081
+ if (!expectedTailLogToken) {
2082
+ return Response.json(
2083
+ { ok: false, error: 'tail log token is not configured' },
2084
+ { status: 503 },
2085
+ );
2086
+ }
2087
+ const actualTailLogToken =
2088
+ request.headers.get('x-deepline-tail-log-token')?.trim() ?? '';
2089
+ if (actualTailLogToken !== expectedTailLogToken) {
2090
+ return Response.json(
2091
+ { ok: false, error: 'tail log token mismatch' },
2092
+ { status: 401 },
2093
+ );
2094
+ }
2095
+ return Response.json({
2096
+ ok: true,
2097
+ deployMarker: env.DEEPLINE_COORDINATOR_DEPLOY_MARKER ?? null,
2098
+ });
2099
+ }
1989
2100
  if (url.pathname === '/workflow-pool/refill') {
1990
2101
  const internalAuthError = authorizeCoordinatorControlRequest({
1991
2102
  request,
@@ -2755,7 +2866,7 @@ function loadDynamicPlayWorkerSync(
2755
2866
  }
2756
2867
  const artifactIdentity =
2757
2868
  metadata.artifactHash?.trim() || stableHash(artifactStorageKey);
2758
- const workerCacheKey = `play:${graphHash}:${artifactIdentity}:harness=h4-runtime-api`;
2869
+ const workerCacheKey = `play:${graphHash}:${artifactIdentity}:harness=h5-runtime-api-coordinator`;
2759
2870
  const runIdForTrace = metadata.runId ?? graphHash;
2760
2871
  const loaderGetStartedAt = Date.now();
2761
2872
  const stub = env.LOADER.get(workerCacheKey, async () => {
@@ -2781,10 +2892,15 @@ function loadDynamicPlayWorkerSync(
2781
2892
  // which case the per-play stub throws a loud error on first use
2782
2893
  // (no silent fallbacks — see harness-stub.ts → requireBinding).
2783
2894
  HARNESS: env.HARNESS,
2895
+ VERCEL_PROTECTION_BYPASS_TOKEN: env.VERCEL_PROTECTION_BYPASS_TOKEN,
2784
2896
  // In-process runtime API bridge used by the play harness for status,
2785
2897
  // tool execution, DB session, and artifact callbacks. This avoids a
2786
- // public fetch hop and is required by apps/play-runner-workers/src/entry.ts.
2787
- RUNTIME_API: makeRuntimeApiBinding(),
2898
+ // public fetch hop when Cloudflare exposes the RuntimeApi export.
2899
+ ...makeRuntimeApiEnvBinding(),
2900
+ // In-process coordinator control bridge used by ctx.runPlay and
2901
+ // parent terminal signals. This keeps scalar child plays inline with
2902
+ // the parent instead of round-tripping through nested Workflow waits.
2903
+ ...makeCoordinatorControlBinding(),
2788
2904
  // NOTE: We intentionally do NOT pass `env.PLAYS_BUCKET` (an R2Bucket
2789
2905
  // binding) through to the per-play Worker's env. Including a raw
2790
2906
  // R2Bucket in the dynamically-loaded Worker's env makes Cloudflare
@@ -2836,7 +2952,7 @@ async function loadDynamicPlayWorker(
2836
2952
  }
2837
2953
  const artifactIdentity =
2838
2954
  metadata.artifactHash?.trim() || stableHash(artifactStorageKey);
2839
- const workerCacheKey = `play:${graphHash}:${artifactIdentity}:harness=h4-runtime-api`;
2955
+ const workerCacheKey = `play:${graphHash}:${artifactIdentity}:harness=h5-runtime-api-coordinator`;
2840
2956
  const runIdForTrace = metadata.runId ?? graphHash;
2841
2957
  const loaderGetStartedAt = Date.now();
2842
2958
  const stub = env.LOADER.get(workerCacheKey, async () => {
@@ -2858,10 +2974,11 @@ async function loadDynamicPlayWorker(
2858
2974
  // Mirror of the sync loader (above) — see that copy for the
2859
2975
  // architectural rationale. The dynamic worker env is intentionally
2860
2976
  // minimal; runtime callbacks use RUNTIME_API, file reads go through
2861
- // HARNESS, and child workflow control uses the coordinator URL in the
2862
- // run request.
2977
+ // HARNESS, and child workflow control uses the COORDINATOR binding.
2863
2978
  HARNESS: env.HARNESS,
2864
- RUNTIME_API: makeRuntimeApiBinding(),
2979
+ VERCEL_PROTECTION_BYPASS_TOKEN: env.VERCEL_PROTECTION_BYPASS_TOKEN,
2980
+ ...makeRuntimeApiEnvBinding(),
2981
+ ...makeCoordinatorControlBinding(),
2865
2982
  },
2866
2983
  };
2867
2984
  });
@@ -3169,11 +3286,16 @@ async function handleCoordinatorWarmup(
3169
3286
  * structured-cloneable. WorkerEntrypoint stubs ARE cloneable — same trick
3170
3287
  * `makePlayAssetsBinding` already uses.
3171
3288
  *
3172
- * Falls back transparently in legacy coordinators: if the binding isn't
3173
- * present in `env`, the harness uses its existing `fetch(req.baseUrl + path)`
3174
- * path, which still works (just slower).
3289
+ * Falls back transparently when Cloudflare does not expose module exports in
3290
+ * the current execution path: if the binding is omitted from `env`, the play
3291
+ * worker uses its existing `fetch(req.baseUrl + path)` transport.
3175
3292
  */
3176
- function makeRuntimeApiBinding(): { fetch(req: Request): Promise<Response> } {
3293
+ let loggedMissingRuntimeApiExport = false;
3294
+ let loggedMissingCoordinatorControlExport = false;
3295
+
3296
+ function makeRuntimeApiEnvBinding():
3297
+ | { RUNTIME_API: { fetch(req: Request): Promise<Response> } }
3298
+ | Record<string, never> {
3177
3299
  const exports = workersExports as unknown as {
3178
3300
  RuntimeApi?: (init: { props: undefined }) => {
3179
3301
  fetch(req: Request): Promise<Response>;
@@ -3181,27 +3303,35 @@ function makeRuntimeApiBinding(): { fetch(req: Request): Promise<Response> } {
3181
3303
  };
3182
3304
  const ctor = exports.RuntimeApi;
3183
3305
  if (typeof ctor !== 'function') {
3184
- throw new Error(
3185
- 'RuntimeApi is not registered on cloudflare:workers exports.',
3186
- );
3306
+ if (!loggedMissingRuntimeApiExport) {
3307
+ loggedMissingRuntimeApiExport = true;
3308
+ console.warn(
3309
+ '[coordinator] RuntimeApi is not registered on cloudflare:workers exports; using public runtime API transport.',
3310
+ );
3311
+ }
3312
+ return {};
3187
3313
  }
3188
- return ctor({ props: undefined });
3314
+ return { RUNTIME_API: ctor({ props: undefined }) };
3189
3315
  }
3190
3316
 
3191
- function makeCoordinatorControlBinding(): {
3192
- submitChild(
3193
- parentRunId: string,
3194
- body: Record<string, unknown>,
3195
- ): Promise<{ workflowId?: string; runId?: string; error?: unknown }>;
3196
- signal(
3197
- runId: string,
3198
- body: Record<string, unknown>,
3199
- ): Promise<Record<string, unknown>>;
3200
- recordPerfTrace(
3201
- runId: string,
3202
- payload: CoordinatorPerfTracePayload,
3203
- ): Promise<void>;
3204
- } {
3317
+ function makeCoordinatorControlBinding():
3318
+ | {
3319
+ COORDINATOR: {
3320
+ submitChild(
3321
+ parentRunId: string,
3322
+ body: Record<string, unknown>,
3323
+ ): Promise<{ workflowId?: string; runId?: string; error?: unknown }>;
3324
+ signal(
3325
+ runId: string,
3326
+ body: Record<string, unknown>,
3327
+ ): Promise<Record<string, unknown>>;
3328
+ recordPerfTrace(
3329
+ runId: string,
3330
+ payload: CoordinatorPerfTracePayload,
3331
+ ): Promise<void>;
3332
+ };
3333
+ }
3334
+ | Record<string, never> {
3205
3335
  const exports = workersExports as unknown as {
3206
3336
  CoordinatorControl?: (init: { props: undefined }) => {
3207
3337
  submitChild(
@@ -3220,11 +3350,15 @@ function makeCoordinatorControlBinding(): {
3220
3350
  };
3221
3351
  const ctor = exports.CoordinatorControl;
3222
3352
  if (typeof ctor !== 'function') {
3223
- throw new Error(
3224
- 'CoordinatorControl is not registered on cloudflare:workers exports.',
3225
- );
3353
+ if (!loggedMissingCoordinatorControlExport) {
3354
+ loggedMissingCoordinatorControlExport = true;
3355
+ console.warn(
3356
+ '[coordinator] CoordinatorControl is not registered on cloudflare:workers exports; using public coordinator transport.',
3357
+ );
3358
+ }
3359
+ return {};
3226
3360
  }
3227
- return ctor({ props: undefined });
3361
+ return { COORDINATOR: ctor({ props: undefined }) };
3228
3362
  }
3229
3363
 
3230
3364
  async function loadStoredPlayArtifactFromR2(
@@ -3261,6 +3395,9 @@ function mapWorkflowResult(
3261
3395
  : null;
3262
3396
  return {
3263
3397
  runId,
3398
+ ...(typeof output?.playName === 'string' && output.playName.trim()
3399
+ ? { playName: output.playName.trim() }
3400
+ : {}),
3264
3401
  status: mapped,
3265
3402
  result: output?.result ?? status.output ?? null,
3266
3403
  error,