deepline 0.1.110 → 0.1.112

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/dist/index.js CHANGED
@@ -274,10 +274,10 @@ var SDK_RELEASE = {
274
274
  // skill on the sdk sync surface, and the people-search-to-email prebuilt.
275
275
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
276
276
  // the SDK enrich generator's one-second stale policy.
277
- version: "0.1.110",
277
+ version: "0.1.112",
278
278
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
279
279
  supportPolicy: {
280
- latest: "0.1.110",
280
+ latest: "0.1.112",
281
281
  minimumSupported: "0.1.53",
282
282
  deprecatedBelow: "0.1.53",
283
283
  commandMinimumSupported: [
package/dist/index.mjs CHANGED
@@ -196,10 +196,10 @@ var SDK_RELEASE = {
196
196
  // skill on the sdk sync surface, and the people-search-to-email prebuilt.
197
197
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
198
198
  // the SDK enrich generator's one-second stale policy.
199
- version: "0.1.110",
199
+ version: "0.1.112",
200
200
  apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
201
201
  supportPolicy: {
202
- latest: "0.1.110",
202
+ latest: "0.1.112",
203
203
  minimumSupported: "0.1.53",
204
204
  deprecatedBelow: "0.1.53",
205
205
  commandMinimumSupported: [
@@ -2898,150 +2898,181 @@ export class DynamicWorkflow extends WorkflowEntrypoint<
2898
2898
  },
2899
2899
  });
2900
2900
 
2901
- return dispatchWorkflow(
2902
- { env: this.env, ctx: this.ctx },
2903
- dispatchedEvent as Parameters<typeof dispatchWorkflow>[1],
2904
- step as Parameters<typeof dispatchWorkflow>[2],
2905
- async ({ metadata, env }) => {
2906
- const graphHash = readMetadataString(metadata, 'graphHash');
2907
- const artifactStorageKey = readMetadataString(
2908
- metadata,
2909
- 'artifactStorageKey',
2910
- );
2911
- const runIdForTrace =
2912
- typeof (metadata as Record<string, unknown>).runId === 'string'
2913
- ? ((metadata as Record<string, unknown>).runId as string)
2914
- : graphHash;
2915
- const loaderStartedAt = Date.now();
2916
- trace({
2917
- runId: runIdForTrace,
2918
- phase: 'coordinator.loader_callback_entry',
2919
- ms: 0,
2920
- graphHash,
2921
- extra: { artifactStorageKey },
2922
- });
2923
- const stub = loadDynamicPlayWorkerSync(
2924
- env,
2925
- {
2901
+ const dispatchWorkflowStartedAt = Date.now();
2902
+ try {
2903
+ return await dispatchWorkflow(
2904
+ { env: this.env, ctx: this.ctx },
2905
+ dispatchedEvent as Parameters<typeof dispatchWorkflow>[1],
2906
+ step as Parameters<typeof dispatchWorkflow>[2],
2907
+ async ({ metadata, env }) => {
2908
+ const graphHash = readMetadataString(metadata, 'graphHash');
2909
+ const artifactStorageKey = readMetadataString(
2910
+ metadata,
2911
+ 'artifactStorageKey',
2912
+ );
2913
+ const runIdForTrace =
2914
+ typeof (metadata as Record<string, unknown>).runId === 'string'
2915
+ ? ((metadata as Record<string, unknown>).runId as string)
2916
+ : graphHash;
2917
+ const loaderStartedAt = Date.now();
2918
+ trace({
2926
2919
  runId: runIdForTrace,
2920
+ phase: 'coordinator.loader_callback_entry',
2921
+ ms: 0,
2927
2922
  graphHash,
2928
- artifactStorageKey,
2929
- artifactHash:
2930
- typeof metadata.artifactHash === 'string'
2931
- ? metadata.artifactHash
2932
- : null,
2933
- dynamicWorkerCode:
2934
- typeof metadata.dynamicWorkerCode === 'string'
2935
- ? metadata.dynamicWorkerCode
2936
- : null,
2937
- packagedFiles: normalizePackagedFiles(metadata.packagedFiles),
2938
- },
2939
- trace,
2940
- );
2941
- const entrypoint = stub.getEntrypoint(
2942
- 'TenantWorkflow',
2943
- ) as unknown as WorkflowRunner;
2944
- trace({
2945
- runId: runIdForTrace,
2946
- phase: 'coordinator.loader_compile',
2947
- ms: Date.now() - loaderStartedAt,
2948
- graphHash,
2949
- });
2950
- // Wrap the entrypoint so its run() failure surfaces here rather
2951
- // than disappearing into the framework's silent rpcMethod=run
2952
- // exception path.
2953
- return {
2954
- run: async (innerEvent: unknown, innerStep: unknown) => {
2955
- const innerStartedAt = Date.now();
2956
- trace({
2923
+ extra: { artifactStorageKey },
2924
+ });
2925
+ const stub = loadDynamicPlayWorkerSync(
2926
+ env,
2927
+ {
2957
2928
  runId: runIdForTrace,
2958
- phase: 'coordinator.runner_run_start',
2959
- ms: 0,
2960
2929
  graphHash,
2961
- });
2962
- try {
2963
- const result = await (
2964
- entrypoint as unknown as {
2965
- run(e: unknown, s: unknown): Promise<unknown>;
2966
- }
2967
- ).run(innerEvent, innerStep);
2968
- const output = isRecord(result) ? result : null;
2969
- try {
2970
- await writeCoordinatorTerminalState(env, {
2971
- runId: runIdForTrace,
2972
- status: 'completed',
2973
- result: output?.result ?? result,
2974
- totalRows: output?.totalRows ?? output?.outputRows ?? null,
2975
- durationMs: output?.durationMs ?? null,
2976
- playName:
2977
- typeof output?.playName === 'string'
2978
- ? output.playName
2979
- : null,
2980
- liveLogs: sanitizeLiveLogLines(output?.liveLogs),
2981
- liveNodeProgress: output?.liveNodeProgress ?? null,
2982
- });
2983
- } catch (terminalError) {
2984
- console.warn(
2985
- '[coordinator] completed terminal cache write failed; preserving completed run',
2986
- {
2987
- graphHash,
2988
- runId: runIdForTrace,
2989
- message:
2990
- terminalError instanceof Error
2991
- ? terminalError.message
2992
- : String(terminalError),
2993
- },
2994
- );
2995
- }
2930
+ artifactStorageKey,
2931
+ artifactHash:
2932
+ typeof metadata.artifactHash === 'string'
2933
+ ? metadata.artifactHash
2934
+ : null,
2935
+ dynamicWorkerCode:
2936
+ typeof metadata.dynamicWorkerCode === 'string'
2937
+ ? metadata.dynamicWorkerCode
2938
+ : null,
2939
+ packagedFiles: normalizePackagedFiles(metadata.packagedFiles),
2940
+ },
2941
+ trace,
2942
+ );
2943
+ const entrypointStartedAt = Date.now();
2944
+ const entrypoint = stub.getEntrypoint(
2945
+ 'TenantWorkflow',
2946
+ ) as unknown as WorkflowRunner;
2947
+ trace({
2948
+ runId: runIdForTrace,
2949
+ phase: 'coordinator.loader_get_entrypoint',
2950
+ ms: Date.now() - entrypointStartedAt,
2951
+ graphHash,
2952
+ });
2953
+ trace({
2954
+ runId: runIdForTrace,
2955
+ phase: 'coordinator.loader_compile',
2956
+ ms: Date.now() - loaderStartedAt,
2957
+ graphHash,
2958
+ });
2959
+ // Wrap the entrypoint so its run() failure surfaces here rather
2960
+ // than disappearing into the framework's silent rpcMethod=run
2961
+ // exception path.
2962
+ return {
2963
+ run: async (innerEvent: unknown, innerStep: unknown) => {
2964
+ const innerStartedAt = Date.now();
2996
2965
  trace({
2997
2966
  runId: runIdForTrace,
2998
- phase: 'coordinator.runner_run',
2999
- ms: Date.now() - innerStartedAt,
2967
+ phase: 'coordinator.runner_run_start',
2968
+ ms: 0,
3000
2969
  graphHash,
3001
2970
  });
3002
- return result;
3003
- } catch (innerError) {
3004
- const failure = normalizePlayRunFailure(innerError);
3005
- console.error('[coordinator] DynamicWorkflow runner.run threw', {
3006
- graphHash,
3007
- message:
3008
- innerError instanceof Error
3009
- ? innerError.message
3010
- : String(innerError),
3011
- name: innerError instanceof Error ? innerError.name : null,
3012
- stack:
3013
- innerError instanceof Error &&
3014
- typeof innerError.stack === 'string'
3015
- ? innerError.stack.split('\n').slice(0, 12).join('\n')
3016
- : null,
3017
- });
3018
- await markWorkflowRuntimeFailure({
3019
- env,
3020
- event: innerEvent,
3021
- error: innerError,
3022
- }).catch((markError) => {
2971
+ try {
2972
+ const result = await (
2973
+ entrypoint as unknown as {
2974
+ run(e: unknown, s: unknown): Promise<unknown>;
2975
+ }
2976
+ ).run(innerEvent, innerStep);
2977
+ const output = isRecord(result) ? result : null;
2978
+ const terminalStartedAt = Date.now();
2979
+ try {
2980
+ await writeCoordinatorTerminalState(env, {
2981
+ runId: runIdForTrace,
2982
+ status: 'completed',
2983
+ result: output?.result ?? result,
2984
+ totalRows: output?.totalRows ?? output?.outputRows ?? null,
2985
+ durationMs: output?.durationMs ?? null,
2986
+ playName:
2987
+ typeof output?.playName === 'string'
2988
+ ? output.playName
2989
+ : null,
2990
+ liveLogs: sanitizeLiveLogLines(output?.liveLogs),
2991
+ liveNodeProgress: output?.liveNodeProgress ?? null,
2992
+ });
2993
+ trace({
2994
+ runId: runIdForTrace,
2995
+ phase: 'coordinator.terminal_state_write',
2996
+ ms: Date.now() - terminalStartedAt,
2997
+ graphHash,
2998
+ extra: { status: 'completed' },
2999
+ });
3000
+ } catch (terminalError) {
3001
+ console.warn(
3002
+ '[coordinator] completed terminal cache write failed; preserving completed run',
3003
+ {
3004
+ graphHash,
3005
+ runId: runIdForTrace,
3006
+ message:
3007
+ terminalError instanceof Error
3008
+ ? terminalError.message
3009
+ : String(terminalError),
3010
+ },
3011
+ );
3012
+ }
3013
+ trace({
3014
+ runId: runIdForTrace,
3015
+ phase: 'coordinator.runner_run',
3016
+ ms: Date.now() - innerStartedAt,
3017
+ graphHash,
3018
+ });
3019
+ return result;
3020
+ } catch (innerError) {
3021
+ const failure = normalizePlayRunFailure(innerError);
3023
3022
  console.error(
3024
- '[coordinator] failed to forward DynamicWorkflow runner error',
3023
+ '[coordinator] DynamicWorkflow runner.run threw',
3025
3024
  {
3026
3025
  graphHash,
3027
3026
  message:
3028
- markError instanceof Error
3029
- ? markError.message
3030
- : String(markError),
3027
+ innerError instanceof Error
3028
+ ? innerError.message
3029
+ : String(innerError),
3030
+ name: innerError instanceof Error ? innerError.name : null,
3031
+ stack:
3032
+ innerError instanceof Error &&
3033
+ typeof innerError.stack === 'string'
3034
+ ? innerError.stack.split('\n').slice(0, 12).join('\n')
3035
+ : null,
3031
3036
  },
3032
3037
  );
3033
- });
3034
- await writeCoordinatorTerminalState(env, {
3035
- runId: runIdForTrace,
3036
- status: 'failed',
3037
- error: failure.message,
3038
- }).catch(() => undefined);
3039
- throw innerError;
3040
- }
3041
- },
3042
- };
3043
- },
3044
- );
3038
+ await markWorkflowRuntimeFailure({
3039
+ env,
3040
+ event: innerEvent,
3041
+ error: innerError,
3042
+ }).catch((markError) => {
3043
+ console.error(
3044
+ '[coordinator] failed to forward DynamicWorkflow runner error',
3045
+ {
3046
+ graphHash,
3047
+ message:
3048
+ markError instanceof Error
3049
+ ? markError.message
3050
+ : String(markError),
3051
+ },
3052
+ );
3053
+ });
3054
+ await writeCoordinatorTerminalState(env, {
3055
+ runId: runIdForTrace,
3056
+ status: 'failed',
3057
+ error: failure.message,
3058
+ }).catch(() => undefined);
3059
+ throw innerError;
3060
+ }
3061
+ },
3062
+ };
3063
+ },
3064
+ );
3065
+ } finally {
3066
+ trace({
3067
+ runId: dispatchTrace.runId,
3068
+ phase: 'coordinator.dispatch_workflow_total',
3069
+ ms: Date.now() - dispatchWorkflowStartedAt,
3070
+ graphHash: dispatchTrace.graphHash,
3071
+ extra: {
3072
+ instanceId: dispatchTrace.instanceId,
3073
+ },
3074
+ });
3075
+ }
3045
3076
  }
3046
3077
  }
3047
3078
 
@@ -3426,6 +3457,18 @@ async function handleWorkflowRoute(input: {
3426
3457
  const parseStartedAt = Date.now();
3427
3458
  const params = (await request.json()) as PlayWorkflowParams;
3428
3459
  submittedRunId = params.runId ?? runId;
3460
+ recordSubmitTiming({
3461
+ phase: 'coordinator.submit_received',
3462
+ ms: Date.now() - submitStartedAt,
3463
+ graphHash: params.graphHash ?? null,
3464
+ extra: {
3465
+ hasDynamicWorkerCode: Boolean(params.dynamicWorkerCode),
3466
+ dynamicWorkerBytes:
3467
+ typeof params.dynamicWorkerCode === 'string'
3468
+ ? params.dynamicWorkerCode.length
3469
+ : 0,
3470
+ },
3471
+ });
3429
3472
  recordSubmitTiming({
3430
3473
  phase: 'coordinator.submit_parse_body',
3431
3474
  ms: Date.now() - parseStartedAt,
@@ -3555,6 +3598,12 @@ async function handleWorkflowRoute(input: {
3555
3598
  try {
3556
3599
  const dispatchStartedAt = Date.now();
3557
3600
  const createStartedAt = Date.now();
3601
+ recordSubmitTiming({
3602
+ phase: 'coordinator.workflow_create_start',
3603
+ ms: 0,
3604
+ graphHash: params.graphHash ?? null,
3605
+ extra: { instanceId: defaultInstanceId },
3606
+ });
3558
3607
  const createResult = await createOrAttachWorkflowInstance({
3559
3608
  create: () =>
3560
3609
  createDynamicWorkflowInstance({
@@ -401,6 +401,9 @@ function captureRuntimeApiBinding(env: WorkerEnv): void {
401
401
  }
402
402
 
403
403
  let cachedCoordinatorBinding: WorkerEnv['COORDINATOR'] | null = null;
404
+ const TRACE_FLUSH_MS = 1_000;
405
+ const pendingTraceForwardsByRun = new Map<string, Promise<void>>();
406
+
404
407
  function captureCoordinatorBinding(env: WorkerEnv): void {
405
408
  cachedCoordinatorBinding = env.COORDINATOR ?? null;
406
409
  }
@@ -685,32 +688,58 @@ function recordRunnerPerfTrace(input: {
685
688
  ms?: number;
686
689
  extra?: Record<string, unknown>;
687
690
  }): void {
691
+ // Benchmark note: these runner spans decompose the server watch's terminal
692
+ // wait. They are logged locally and forwarded to the coordinator so
693
+ // `/api/v2/plays/run --watch` benchmark exports can join them with
694
+ // `server.stream_scheduler_terminal_event` by runId.
688
695
  if (!input.req.runId || !input.phase) return;
696
+ const phase = input.phase.startsWith('runner.')
697
+ ? input.phase
698
+ : `runner.${input.phase}`;
689
699
  // Tool-level traces can fire once per row/provider step. Forwarding each one
690
700
  // through the coordinator binding can consume Cloudflare's subrequest budget
691
701
  // before large batched maps finish.
692
- if (input.phase.startsWith('runner.tool.')) {
702
+ if (phase.startsWith('runner.tool.')) {
693
703
  return;
694
704
  }
695
705
  const payload = {
696
706
  ts: Date.now(),
697
707
  source: 'dynamic_worker' as const,
698
708
  runId: input.req.runId,
699
- phase: `runner.${input.phase}`,
709
+ phase,
700
710
  ms: input.ms ?? 0,
701
711
  ...(input.extra ?? {}),
702
712
  };
703
713
  console.log(
704
714
  `[deepline-run:${input.req.runId}] [perf-trace] ${JSON.stringify(payload)}`,
705
715
  );
706
- cachedCoordinatorBinding
707
- ?.recordPerfTrace(input.req.runId, payload)
708
- .catch((error: unknown) => {
709
- const message = error instanceof Error ? error.message : String(error);
710
- console.warn(
711
- `[deepline-run:${input.req.runId}] failed to forward runner perf trace: ${message}`,
712
- );
713
- });
716
+ const binding = cachedCoordinatorBinding;
717
+ if (!binding) return;
718
+ const forward = binding
719
+ .recordPerfTrace(input.req.runId, payload)
720
+ .catch(() => undefined);
721
+ const previous = pendingTraceForwardsByRun.get(input.req.runId);
722
+ const pending = previous
723
+ ? previous.then(
724
+ () => forward,
725
+ () => forward,
726
+ )
727
+ : forward;
728
+ pendingTraceForwardsByRun.set(input.req.runId, pending);
729
+ void pending.finally(() => {
730
+ if (pendingTraceForwardsByRun.get(input.req.runId) === pending) {
731
+ pendingTraceForwardsByRun.delete(input.req.runId);
732
+ }
733
+ });
734
+ }
735
+
736
+ async function drainRunnerPerfTraces(req: RunRequest): Promise<void> {
737
+ const pending = pendingTraceForwardsByRun.get(req.runId);
738
+ if (!pending) return;
739
+ await Promise.race([
740
+ pending,
741
+ new Promise((resolve) => setTimeout(resolve, TRACE_FLUSH_MS)),
742
+ ]);
714
743
  }
715
744
 
716
745
  function makeRequestId(): string {
@@ -5962,7 +5991,17 @@ async function executeRunRequest(
5962
5991
  onToolFailed: (toolId, at) => stepLifecycle?.onToolFailed(toolId, at),
5963
5992
  };
5964
5993
 
5994
+ let hasEmittedRunnerEvent = false;
5965
5995
  const wrappedEmit = (event: RunnerEvent) => {
5996
+ if (!hasEmittedRunnerEvent) {
5997
+ hasEmittedRunnerEvent = true;
5998
+ recordRunnerPerfTrace({
5999
+ req,
6000
+ phase: 'first_event',
6001
+ ms: nowMs() - startedAt,
6002
+ extra: { eventType: event.type },
6003
+ });
6004
+ }
5966
6005
  if (event.type === 'log') {
5967
6006
  appendRunLogLine(event.message);
5968
6007
  flushLedgerEvents(false);
@@ -6133,6 +6172,14 @@ async function executeRunRequest(
6133
6172
  phase: 'runner.execute_total',
6134
6173
  ms: nowMs() - startedAt,
6135
6174
  });
6175
+ // The server-side watch path reads coordinator-buffered perf traces from
6176
+ // the same tail response that carries the terminal event. Runner traces are
6177
+ // forwarded asynchronously during execution so normal play latency is not
6178
+ // gated on observability writes; before returning terminal output, wait a
6179
+ // bounded interval for those writes to land. This keeps benchmark exports
6180
+ // able to decompose "terminal wait" into runner/dataset/ledger phases
6181
+ // without turning trace delivery into a correctness dependency.
6182
+ await drainRunnerPerfTraces(req);
6136
6183
  return {
6137
6184
  playName: req.playName,
6138
6185
  result: serializedResult,
@@ -6160,6 +6207,7 @@ async function executeRunRequest(
6160
6207
  appendRunLogLine(
6161
6208
  `${aborted ? '[cancelled]' : '[error]'} ${redactSecretsFromLogString(message)}`,
6162
6209
  );
6210
+ const terminalUpdateStartedAt = nowMs();
6163
6211
  await flushTerminalLedgerEvents({
6164
6212
  type: aborted ? 'run.cancelled' : 'run.failed',
6165
6213
  runId: req.runId,
@@ -6184,25 +6232,55 @@ async function executeRunRequest(
6184
6232
  ],
6185
6233
  },
6186
6234
  });
6235
+ recordRunnerPerfTrace({
6236
+ req,
6237
+ phase: aborted
6238
+ ? 'runner.terminal_ledger_append_cancelled'
6239
+ : 'runner.terminal_ledger_append_failed',
6240
+ ms: nowMs() - terminalUpdateStartedAt,
6241
+ extra: {
6242
+ errorCode: failure.code,
6243
+ errorPhase: failure.phase,
6244
+ },
6245
+ });
6246
+ const billingStartedAt = nowMs();
6187
6247
  await finalizeWorkerComputeBilling({
6188
6248
  req,
6189
6249
  success: false,
6190
6250
  actionEstimate: 4,
6191
- }).catch((finalizeError) => {
6192
- console.error(
6193
- `[play-harness] non-fatal compute billing finalize failed runId=${req.runId}: ${
6194
- finalizeError instanceof Error
6195
- ? finalizeError.message
6196
- : String(finalizeError)
6197
- }`,
6198
- );
6199
- });
6251
+ })
6252
+ .catch((finalizeError) => {
6253
+ console.error(
6254
+ `[play-harness] non-fatal compute billing finalize failed runId=${req.runId}: ${
6255
+ finalizeError instanceof Error
6256
+ ? finalizeError.message
6257
+ : String(finalizeError)
6258
+ }`,
6259
+ );
6260
+ })
6261
+ .finally(() => {
6262
+ recordRunnerPerfTrace({
6263
+ req,
6264
+ phase: 'runner.compute_billing_finalize_failed',
6265
+ ms: nowMs() - billingStartedAt,
6266
+ });
6267
+ });
6200
6268
  }
6201
6269
  await signalParentPlayTerminal({
6202
6270
  req,
6203
6271
  status: aborted ? 'cancelled' : 'failed',
6204
6272
  error: message,
6205
6273
  }).catch(() => null);
6274
+ recordRunnerPerfTrace({
6275
+ req,
6276
+ phase: aborted ? 'runner.execute_cancelled' : 'runner.execute_failed',
6277
+ ms: nowMs() - startedAt,
6278
+ extra: {
6279
+ errorCode: failure.code,
6280
+ errorPhase: failure.phase,
6281
+ },
6282
+ });
6283
+ await drainRunnerPerfTraces(req);
6206
6284
  throw error;
6207
6285
  } finally {
6208
6286
  clearTimeout(runtimeDeadlineTimer);
@@ -6631,14 +6709,29 @@ export class TenantWorkflow extends WorkflowEntrypoint<
6631
6709
  // Must run BEFORE any SDK call site that would reach into HARNESS,
6632
6710
  // i.e. before user play code is invoked. Idempotent within a run.
6633
6711
  captureHarnessBinding(this.env);
6712
+ recordRunnerPerfTrace({
6713
+ req,
6714
+ phase: 'tenant_workflow_entry',
6715
+ ms: 0,
6716
+ extra: {
6717
+ hasWorkflowStep: true,
6718
+ },
6719
+ });
6634
6720
  // Fire the one-time wiring probe (deduplicated across runs in the
6635
6721
  // same isolate). Awaited so the result is in the log before user code
6636
6722
  // begins. A missing or unhealthy HARNESS fails the run before user code
6637
6723
  // can accidentally take a slower fallback path.
6724
+ const probeStartedAt = nowMs();
6638
6725
  await probeHarnessOnce(this.env, runPrefix);
6726
+ recordRunnerPerfTrace({
6727
+ req,
6728
+ phase: 'tenant_workflow_probe_harness',
6729
+ ms: nowMs() - probeStartedAt,
6730
+ });
6639
6731
  const abortController = new AbortController();
6640
6732
  try {
6641
- return (await executeRunRequest(
6733
+ const executeStartedAt = nowMs();
6734
+ const output = (await executeRunRequest(
6642
6735
  req,
6643
6736
  this.env,
6644
6737
  (runnerEvent) => {
@@ -6669,6 +6762,12 @@ export class TenantWorkflow extends WorkflowEntrypoint<
6669
6762
  waitUntil: (promise) => this.ctx.waitUntil(promise),
6670
6763
  },
6671
6764
  )) as Record<string, unknown>;
6765
+ recordRunnerPerfTrace({
6766
+ req,
6767
+ phase: 'tenant_workflow_execute_request',
6768
+ ms: nowMs() - executeStartedAt,
6769
+ });
6770
+ return output;
6672
6771
  } catch (error) {
6673
6772
  // CF Workflows + the dynamic-workflows framework swallow the error
6674
6773
  // message and surface only "internal error; reference = <id>" via
@@ -99,10 +99,10 @@ export const SDK_RELEASE = {
99
99
  // skill on the sdk sync surface, and the people-search-to-email prebuilt.
100
100
  // 0.1.108 ships explicit dataset column/tool recompute policy and removes
101
101
  // the SDK enrich generator's one-second stale policy.
102
- version: '0.1.110',
102
+ version: '0.1.112',
103
103
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
104
104
  supportPolicy: {
105
- latest: '0.1.110',
105
+ latest: '0.1.112',
106
106
  minimumSupported: '0.1.53',
107
107
  deprecatedBelow: '0.1.53',
108
108
  commandMinimumSupported: [