deepline 0.3.285 → 0.3.287

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 (45) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +48 -26
  2. package/dist/bundling-sources/sdk/src/index.ts +4 -0
  3. package/dist/bundling-sources/sdk/src/release.ts +7 -1
  4. package/dist/bundling-sources/sdk/src/types.ts +36 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/cell-provenance.ts +16 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +21 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +2 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +18 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +16 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +38 -3
  11. package/dist/bundling-sources/shared_libs/play-runtime/run-observation.ts +12 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +3 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +43 -1
  14. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +28 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +88 -1
  16. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +243 -90
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +53 -25
  18. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +40 -7
  19. package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +83 -1
  20. package/dist/bundling-sources/shared_libs/play-runtime/test-async-batching.ts +4 -1
  21. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
  23. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +10 -1
  24. package/dist/bundling-sources/shared_libs/plays/enrich-compat-adapter.ts +2 -0
  25. package/dist/cli/index.js +30615 -34474
  26. package/dist/cli/index.mjs +30310 -34181
  27. package/dist/{client-T2jVnZrq.d.ts → client-FR_u0dxT.d.ts} +40 -4
  28. package/dist/{client-Cl-zj2oS.d.mts → client-phK1Ixcl.d.mts} +40 -4
  29. package/dist/{compiler-manifest-B1zo3igW.d.mts → compiler-manifest-9SQtUuKb.d.mts} +1 -1
  30. package/dist/{compiler-manifest-B1zo3igW.d.ts → compiler-manifest-9SQtUuKb.d.ts} +1 -1
  31. package/dist/index.d.mts +4 -4
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +57 -28
  34. package/dist/index.mjs +57 -28
  35. package/dist/install-integrity.json +4 -4
  36. package/dist/plays/bundle-play-file.d.mts +2 -2
  37. package/dist/plays/bundle-play-file.d.ts +2 -2
  38. package/dist/plays/bundle-play-file.mjs +8 -1
  39. package/dist/plays/run-request.d.mts +2 -2
  40. package/dist/plays/run-request.d.ts +2 -2
  41. package/dist/release.d.mts +5 -1
  42. package/dist/release.d.ts +5 -1
  43. package/dist/release.js +6 -1
  44. package/dist/release.mjs +6 -1
  45. package/package.json +1 -1
@@ -117,6 +117,8 @@ import type {
117
117
  DeeplineAgentModelDescription,
118
118
  InferenceQuote,
119
119
  ChildRunSummary,
120
+ SearchOptions,
121
+ SearchResult,
120
122
  } from './types.js';
121
123
  import { isStopAllPlayRunsDryRunResult } from './types.js';
122
124
  import type { MonitorDefinition } from './monitors.js';
@@ -610,6 +612,29 @@ function chunkRegisterPlayArtifacts<T>(artifacts: T[]): T[][] {
610
612
  return chunks;
611
613
  }
612
614
 
615
+ /** Query parameters shared by every search route. */
616
+ function searchParams(options: ToolSearchOptions): URLSearchParams {
617
+ const params = new URLSearchParams();
618
+ params.set('q', options.query?.trim() ?? '');
619
+ params.set(
620
+ 'include_search_debug',
621
+ options.includeSearchDebug ? 'true' : 'false',
622
+ );
623
+ params.set('search_mode', options.searchMode ?? 'v2');
624
+ if (options.limit !== undefined) params.set('limit', String(options.limit));
625
+ if (options.offset !== undefined) {
626
+ params.set('offset', String(options.offset));
627
+ }
628
+ if (options.categories?.trim()) {
629
+ params.set('categories', options.categories.trim());
630
+ }
631
+ if (options.searchTerms?.trim()) {
632
+ params.set('search_terms', options.searchTerms.trim());
633
+ }
634
+ if (options.task?.trim()) params.set('task', options.task.trim());
635
+ return params;
636
+ }
637
+
613
638
  /** Filters for `client.runs.list(...)`. */
614
639
  export type RunsListOptions = {
615
640
  play?: string;
@@ -3195,34 +3220,32 @@ export class DeeplineClient {
3195
3220
  async searchTools(
3196
3221
  options: ToolSearchOptions = {},
3197
3222
  ): Promise<ToolSearchResult> {
3198
- const params = new URLSearchParams();
3199
- const query = options.query?.trim() ?? '';
3200
- params.set('q', query);
3201
- params.set(
3202
- 'include_search_debug',
3203
- options.includeSearchDebug ? 'true' : 'false',
3204
- );
3205
- params.set('search_mode', options.searchMode ?? 'v2');
3206
- if (options.limit !== undefined) {
3207
- params.set('limit', String(options.limit));
3208
- }
3209
- if (options.offset !== undefined) {
3210
- params.set('offset', String(options.offset));
3211
- }
3212
- if (options.categories?.trim()) {
3213
- params.set('categories', options.categories.trim());
3214
- }
3215
- if (options.searchTerms?.trim()) {
3216
- params.set('search_terms', options.searchTerms.trim());
3217
- }
3218
- if (options.task?.trim()) {
3219
- params.set('task', options.task.trim());
3220
- }
3221
3223
  return this.http.get<ToolSearchResult>(
3222
- `/api/v2/tools/search?${params.toString()}`,
3224
+ `/api/v2/tools/search?${searchParams(options).toString()}`,
3223
3225
  );
3224
3226
  }
3225
3227
 
3228
+ /**
3229
+ * Search everything Deepline can run or deploy in one ranked list: tools,
3230
+ * monitor types, prebuilt plays, and your workspace's own plays.
3231
+ *
3232
+ * @example
3233
+ * ```typescript
3234
+ * const { results } = await client.search({
3235
+ * query: 'find work email',
3236
+ * types: ['tools', 'prebuilts'],
3237
+ * });
3238
+ * ```
3239
+ */
3240
+ async search(options: SearchOptions = {}): Promise<SearchResult> {
3241
+ const params = searchParams(options);
3242
+ if (options.types?.length) params.set('types', options.types.join(','));
3243
+ if (options.exclude?.length) {
3244
+ params.set('exclude', options.exclude.join(','));
3245
+ }
3246
+ return this.http.get<SearchResult>(`/api/v2/search?${params.toString()}`);
3247
+ }
3248
+
3226
3249
  /**
3227
3250
  * Get detailed metadata for a single tool.
3228
3251
  *
@@ -3681,8 +3704,7 @@ export class DeeplineClient {
3681
3704
  /**
3682
3705
  * Compile legacy enrich command arguments into a runtime plan.
3683
3706
  *
3684
- * This is primarily used by CLI compatibility paths that translate older
3685
- * enrichment commands onto the play runtime.
3707
+ * @deprecated Retained for older clients. Use Plays for new enrichment workflows.
3686
3708
  */
3687
3709
  async compileEnrichPlan(input: {
3688
3710
  plan_args?: string[];
@@ -313,6 +313,10 @@ export type {
313
313
  StopPlayRunResult,
314
314
  ToolSearchOptions,
315
315
  ToolSearchResult,
316
+ SearchOptions,
317
+ SearchResult,
318
+ SearchResultItem,
319
+ SearchType,
316
320
  } from './types.js';
317
321
 
318
322
  export type {
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
202
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
203
- version: '0.3.285',
203
+ version: '0.3.287',
204
204
  updateSummary:
205
205
  'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
206
206
  packageCapabilities: {
@@ -235,6 +235,12 @@ export const SDK_RELEASE = {
235
235
  minimumSupported: '0.1.53',
236
236
  deprecatedBelow: '0.3.1',
237
237
  commandIntroducedIn: [
238
+ {
239
+ command: 'search',
240
+ introducedIn: '0.3.91',
241
+ reason:
242
+ 'deepline search was added in SDK CLI 0.3.91 as the one search across tools, monitors, and plays. On an older version run deepline tools search or deepline plays search, or update.',
243
+ },
238
244
  {
239
245
  command: 'notifications',
240
246
  introducedIn: '0.2.40',
@@ -488,6 +488,42 @@ export interface ToolSearchOptions {
488
488
  includeSearchDebug?: boolean;
489
489
  }
490
490
 
491
+ /** What a search can be narrowed to. `plays` is `prebuilts` plus `custom`. */
492
+ export type SearchType =
493
+ | 'tools'
494
+ | 'plays'
495
+ | 'monitors'
496
+ | 'prebuilts'
497
+ | 'custom';
498
+
499
+ /** Options for {@link DeeplineClient.search}. */
500
+ export interface SearchOptions extends ToolSearchOptions {
501
+ /** Kinds of result to search. Omit to search everything. */
502
+ types?: readonly SearchType[];
503
+ /** Kinds of result to leave out; the opposite of `types`. */
504
+ exclude?: readonly SearchType[];
505
+ }
506
+
507
+ /** One ranked search result; `kind` says which fields accompany it. */
508
+ export interface SearchResultItem {
509
+ /** Tool id for tools and monitor types, or play reference. */
510
+ id: string;
511
+ kind: 'tool' | 'monitor_type' | 'prebuilt_play' | 'custom_play';
512
+ displayName?: string;
513
+ description?: string;
514
+ [field: string]: unknown;
515
+ }
516
+
517
+ /** Ranked results across tools, monitor types, and plays. */
518
+ export interface SearchResult extends Omit<
519
+ ToolSearchResult,
520
+ 'tools' | 'omitted_plays_hint' | 'commandTemplates'
521
+ > {
522
+ results: SearchResultItem[];
523
+ /** The result kinds this search covered. */
524
+ kinds: SearchResultItem['kind'][];
525
+ }
526
+
491
527
  /**
492
528
  * Ranked tool/provider discovery response.
493
529
  *
@@ -45,6 +45,22 @@ export interface PlayCellDecision {
45
45
  reads: PlayCellReadRef[];
46
46
  }
47
47
 
48
+ /** One persisted outcome per row and predicate, never evaluation attempts. */
49
+ export type PlayConditionCounts = { true: number; false: number };
50
+
51
+ export function normalizePlayConditionCounts(
52
+ value: unknown,
53
+ ): PlayConditionCounts | undefined {
54
+ if (!value || typeof value !== 'object') return undefined;
55
+ const counts = value as PlayConditionCounts;
56
+ return Number.isSafeInteger(counts.true) &&
57
+ counts.true >= 0 &&
58
+ Number.isSafeInteger(counts.false) &&
59
+ counts.false >= 0
60
+ ? { true: counts.true, false: counts.false }
61
+ : undefined;
62
+ }
63
+
48
64
  /** Row-grain birth: the source row the runtime held while building this row. */
49
65
  export type PlayRowBornFromRow = { table: string; rowKey: string };
50
66
 
@@ -160,6 +160,7 @@ import {
160
160
  attachRelayPublishedToolResultReference,
161
161
  attachToolResultListDataset,
162
162
  createToolExecuteResult,
163
+ projectNativeBatchToolResponse,
163
164
  parseToolExecuteResponse,
164
165
  isToolExecuteResult,
165
166
  isSerializedToolExecuteResult,
@@ -1449,6 +1450,18 @@ function publicToolResponseEnvelope(value: unknown): {
1449
1450
  };
1450
1451
  }
1451
1452
 
1453
+ function nativeBatchResponseMeta(result: unknown): Record<string, unknown> {
1454
+ return {
1455
+ nativeBatchResponseView:
1456
+ result &&
1457
+ typeof result === 'object' &&
1458
+ !Array.isArray(result) &&
1459
+ Object.prototype.hasOwnProperty.call(result, 'data')
1460
+ ? 'data'
1461
+ : 'rawV2',
1462
+ };
1463
+ }
1464
+
1452
1465
  /**
1453
1466
  * A batched provider request returns one envelope for several logical source
1454
1467
  * tool calls. Each source call persists only its own item-shaped canonical
@@ -7362,6 +7375,10 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
7362
7375
  private materializeToolExecutionResult(
7363
7376
  result: ToolExecuteResult,
7364
7377
  ): ToolExecuteResult {
7378
+ result = projectNativeBatchToolResponse(
7379
+ result,
7380
+ this.currentRuntimeContract.semantics.tools.nativeBatchResponse,
7381
+ );
7365
7382
  if (
7366
7383
  this.currentToolResponseContract === RAW_V2_TOOL_RESPONSE_CONTRACT ||
7367
7384
  !this.hasPinnedToolResponseContract ||
@@ -16157,6 +16174,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
16157
16174
  toolId,
16158
16175
  status: aggregate.status,
16159
16176
  result: item,
16177
+ meta: nativeBatchResponseMeta(item),
16160
16178
  toolResponse: publicToolResponseForBatchedItem(
16161
16179
  aggregate,
16162
16180
  item,
@@ -16601,6 +16619,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
16601
16619
  request,
16602
16620
  result: splitResults[index] ?? null,
16603
16621
  status: batchExecution?.execution.status,
16622
+ meta: nativeBatchResponseMeta(
16623
+ splitResults[index] ?? null,
16624
+ ),
16604
16625
  toolResponse:
16605
16626
  batchExecution == null
16606
16627
  ? undefined
@@ -1,6 +1,7 @@
1
1
  import type { PlayRunLifecycleStatus } from './run-lifecycle-policy';
2
2
  import type { PlayDocflowNodeIoState } from './docflow-node-io';
3
3
  import type { RunStopCause } from './run-execution-state';
4
+ import type { PlayConditionCounts } from './cell-provenance';
4
5
  import type {
5
6
  PlayActivityObservation,
6
7
  PlayRunActivityProjection,
@@ -31,6 +32,7 @@ export type PlayVisualNodeProgressSnapshot = {
31
32
  completedAt?: number | null;
32
33
  artifactTableNamespace?: string | null;
33
34
  nodeIo?: PlayDocflowNodeIoState;
35
+ conditionCounts?: PlayConditionCounts;
34
36
  };
35
37
 
36
38
  /**
@@ -57,6 +57,24 @@ export function createSandboxCleanupJob(
57
57
  return await scheduleRetry(ports, input, deletion.detail);
58
58
  }
59
59
 
60
+ const expectedRoutingDomain =
61
+ input.resource.routingDomain?.trim() || null;
62
+ const observedRoutingDomain = deletion.routingDomain?.trim() || null;
63
+ // Confirmed deletion can establish a missing legacy domain; absence cannot.
64
+ const confirmedRoutingDomain =
65
+ deletion.kind === 'deleted'
66
+ ? expectedRoutingDomain === null ||
67
+ expectedRoutingDomain === observedRoutingDomain
68
+ : expectedRoutingDomain !== null &&
69
+ expectedRoutingDomain === observedRoutingDomain;
70
+ if (!confirmedRoutingDomain) {
71
+ return await scheduleRetry(
72
+ ports,
73
+ input,
74
+ `${input.resource.provider} sandbox ${input.resource.sandboxId} cleanup confirmation used the wrong routing domain.`,
75
+ );
76
+ }
77
+
60
78
  // A control-plane retry must never keep a provider-confirmed-deleted
61
79
  // resource consuming capacity. This precedes cleanup row completion.
62
80
  await ports.store.releaseCapacity({ resource: input.resource });
@@ -42,6 +42,9 @@ export type PlayRuntimeSemantics = Readonly<{
42
42
  */
43
43
  runtimeOnly: boolean;
44
44
  }>;
45
+ tools: Readonly<{
46
+ nativeBatchResponse: 'legacy_envelope' | 'logical_response';
47
+ }>;
45
48
  slackHitl: Readonly<{
46
49
  defaultTimeoutMs: number;
47
50
  }>;
@@ -60,6 +63,7 @@ function definePlayRuntimeSemantics(
60
63
  secrets: Object.freeze({ ...semantics.secrets }),
61
64
  fetch: Object.freeze({ ...semantics.fetch }),
62
65
  inlineChild: Object.freeze({ ...semantics.inlineChild }),
66
+ tools: Object.freeze({ ...semantics.tools }),
63
67
  slackHitl: Object.freeze({ ...semantics.slackHitl }),
64
68
  });
65
69
  }
@@ -73,6 +77,7 @@ const LEGACY_SEMANTICS = {
73
77
  receiptIdentityEdition: null,
74
78
  },
75
79
  inlineChild: { runtimeOnly: false },
80
+ tools: { nativeBatchResponse: 'legacy_envelope' },
76
81
  } as const;
77
82
 
78
83
  const STRICT_SEMANTICS = {
@@ -164,6 +169,17 @@ const PLAY_RUNTIME_SEMANTICS_BY_AUTHORING = {
164
169
  inlineChild: { runtimeOnly: true },
165
170
  slackHitl: WEEK_LONG_SLACK_HITL_SEMANTICS,
166
171
  },
172
+ 9: {
173
+ ...PLAINTEXT_SECRET_SEMANTICS,
174
+ fetch: {
175
+ httpFailure: 'throw_typed_error',
176
+ persistHttpFailure: false,
177
+ receiptIdentityEdition: FETCH_RECEIPT_IDENTITIES.typedHttpFailure,
178
+ },
179
+ inlineChild: { runtimeOnly: true },
180
+ tools: { nativeBatchResponse: 'logical_response' },
181
+ slackHitl: WEEK_LONG_SLACK_HITL_SEMANTICS,
182
+ },
167
183
  } satisfies Record<PlayAuthoringContractEdition, PlayRuntimeSemanticsTemplate>;
168
184
 
169
185
  const PLAY_RUNTIME_SEMANTICS = Object.fromEntries(
@@ -20,6 +20,8 @@ import {
20
20
  mergeDatasetBornFrom,
21
21
  normalizeDatasetBornFrom,
22
22
  type PlayDatasetBornFrom,
23
+ normalizePlayConditionCounts,
24
+ type PlayConditionCounts,
23
25
  } from './cell-provenance';
24
26
  import { isLedgerTerminalResultRef } from './output-size-limits';
25
27
  import type { PlayLogContext } from './log-context';
@@ -98,6 +100,7 @@ export type PlayRunLedgerStepProgress = {
98
100
  completedAt?: number | null;
99
101
  updatedAt?: number | null;
100
102
  nodeIo?: PlayDocflowNodeIoState;
103
+ conditionCounts?: PlayConditionCounts;
101
104
  };
102
105
 
103
106
  export type PlayRunLedgerStepSnapshot = {
@@ -1400,6 +1403,9 @@ function normalizeStepProgress(
1400
1403
  ...(normalizePlayDocflowNodeIoState(value.nodeIo)
1401
1404
  ? { nodeIo: normalizePlayDocflowNodeIoState(value.nodeIo) }
1402
1405
  : {}),
1406
+ ...(normalizePlayConditionCounts(value.conditionCounts)
1407
+ ? { conditionCounts: normalizePlayConditionCounts(value.conditionCounts) }
1408
+ : {}),
1403
1409
  };
1404
1410
  }
1405
1411
 
@@ -1760,11 +1766,36 @@ export function reducePlayRunLedgerEvent(
1760
1766
  // Detached runners can race their best-effort progress transport against the
1761
1767
  // terminal outbox, and gateway processes can restart or split that traffic.
1762
1768
  // The Play Run Ledger is the durable projection authority, so late
1763
- // `step.*` facts must be a true no-op here rather than relying on a gateway
1764
- // process to remember terminal state.
1769
+ // lifecycle facts must be a true no-op here rather than relying on a gateway
1770
+ // process to remember terminal state. Settled condition counts may arrive
1771
+ // after that claim and fill an absent summary without changing lifecycle.
1765
1772
  if (isTerminalPlayRunLedgerStatus(snapshot.status)) {
1766
1773
  if (event.type === RUN_EVENT_KIND.stepProgress) {
1767
- return refineTerminalSyntheticStepStart(snapshot, event) ?? snapshot;
1774
+ const refined =
1775
+ refineTerminalSyntheticStepStart(snapshot, event) ?? snapshot;
1776
+ const current = refined.stepsById[event.stepId];
1777
+ const conditionCounts = normalizePlayConditionCounts(
1778
+ event.progress.conditionCounts,
1779
+ );
1780
+ // Sheet reconciliation follows the fenced terminal claim. Accept its
1781
+ // first settled predicate pair without reopening lifecycle or timestamps.
1782
+ if (conditionCounts && !current?.progress?.conditionCounts) {
1783
+ return {
1784
+ ...refined,
1785
+ orderedStepIds: appendOrderedStepId(refined, event.stepId),
1786
+ stepsById: {
1787
+ ...refined.stepsById,
1788
+ [event.stepId]: {
1789
+ ...(current ?? {
1790
+ stepId: event.stepId,
1791
+ status: 'completed' as const,
1792
+ }),
1793
+ progress: { ...current?.progress, conditionCounts },
1794
+ },
1795
+ },
1796
+ };
1797
+ }
1798
+ return refined;
1768
1799
  }
1769
1800
  if (isStepLifecycleEvent(event)) return snapshot;
1770
1801
  }
@@ -2300,6 +2331,7 @@ function progressSignature(
2300
2331
  startedAt: progress?.startedAt ?? null,
2301
2332
  completedAt: progress?.completedAt ?? null,
2302
2333
  nodeIo: progress?.nodeIo ?? null,
2334
+ conditionCounts: progress?.conditionCounts ?? null,
2303
2335
  });
2304
2336
  }
2305
2337
 
@@ -2621,6 +2653,9 @@ export function buildPlayRunLedgerEventsFromStatusPatch(input: {
2621
2653
  ? { completedAt: progress.completedAt }
2622
2654
  : {}),
2623
2655
  ...(progress.nodeIo !== undefined ? { nodeIo: progress.nodeIo } : {}),
2656
+ ...(progress.conditionCounts !== undefined
2657
+ ? { conditionCounts: progress.conditionCounts }
2658
+ : {}),
2624
2659
  updatedAt: progress.updatedAt ?? checkpointAt,
2625
2660
  };
2626
2661
  if (
@@ -9,6 +9,10 @@ import {
9
9
  isTerminalPlayRunLifecycleStatus,
10
10
  type PlayRunPublicStatus,
11
11
  } from './run-lifecycle-policy';
12
+ import {
13
+ normalizePlayConditionCounts,
14
+ type PlayConditionCounts,
15
+ } from './cell-provenance';
12
16
  import type { RunStopCause } from './run-execution-state';
13
17
 
14
18
  /**
@@ -64,6 +68,7 @@ export type RunObservationNode = {
64
68
  * Run Snapshot fact `deriveNodeExecutionState` reads. Absent otherwise.
65
69
  */
66
70
  stoppedBy?: RunStopCause;
71
+ conditionCounts?: PlayConditionCounts;
67
72
  };
68
73
 
69
74
  export type RunObservation = {
@@ -204,6 +209,13 @@ function projectNode(node: PlayRunStreamNodeState): RunObservationNode {
204
209
  total: finite(node.progress?.total),
205
210
  settled: nonNegative(node.progress?.completed),
206
211
  failed: nonNegative(node.progress?.failed),
212
+ ...(normalizePlayConditionCounts(node.progress?.conditionCounts)
213
+ ? {
214
+ conditionCounts: normalizePlayConditionCounts(
215
+ node.progress?.conditionCounts,
216
+ ),
217
+ }
218
+ : {}),
207
219
  ...(node.stoppedBy ? { stoppedBy: node.stoppedBy } : {}),
208
220
  };
209
221
  }
@@ -18,6 +18,7 @@ import {
18
18
  } from './run-lifecycle-policy';
19
19
  import type { PlayDocflowNodeIoState } from './docflow-node-io';
20
20
  import type { RunStopCause } from './run-execution-state';
21
+ import type { PlayConditionCounts } from './cell-provenance';
21
22
  import type { PlayVisualNodeUsageSnapshot } from './live-state-contract';
22
23
  import {
23
24
  projectPlayRunActivity,
@@ -53,6 +54,7 @@ export type PlayRunStreamNodeProgress = {
53
54
  completedAt?: number | null;
54
55
  artifactTableNamespace?: string | null;
55
56
  nodeIo?: PlayDocflowNodeIoState;
57
+ conditionCounts?: PlayConditionCounts;
56
58
  };
57
59
 
58
60
  export type PlayRunStreamNodeState = {
@@ -250,6 +252,7 @@ function buildSnapshotFromLedger(
250
252
  completedAt: step.completedAt ?? null,
251
253
  updatedAt: step.progress.updatedAt ?? step.updatedAt ?? null,
252
254
  nodeIo: step.progress.nodeIo,
255
+ conditionCounts: step.progress.conditionCounts,
253
256
  }
254
257
  : null,
255
258
  startedAt: step.startedAt ?? null,
@@ -116,6 +116,7 @@ import {
116
116
  resolveMapRowOutcomeKey,
117
117
  } from './map-row-outcome';
118
118
  import { DEEPLINE_CELL_META_FIELD } from '../plays/cell-staleness';
119
+ import type { PlayConditionCounts } from './cell-provenance';
119
120
  import {
120
121
  LEGACY_RUNTIME_RECEIPT_WIRE_FORMAT,
121
122
  LEGACY_RUNTIME_RECEIPT_WIRE_HEADER,
@@ -9207,6 +9208,7 @@ export type RuntimeSheetRowOutcomeCounts = {
9207
9208
  completed: number;
9208
9209
  failed: number;
9209
9210
  total: number;
9211
+ conditions?: Record<string, PlayConditionCounts>;
9210
9212
  };
9211
9213
 
9212
9214
  /**
@@ -9231,6 +9233,7 @@ export async function countRuntimeSheetRowOutcomes(
9231
9233
  runId: string;
9232
9234
  playName?: string;
9233
9235
  tableNamespace?: string;
9236
+ includeConditions?: boolean;
9234
9237
  },
9235
9238
  ): Promise<RuntimeSheetRowOutcomeCounts> {
9236
9239
  const { rows } = await client.query<{
@@ -9247,13 +9250,51 @@ export async function countRuntimeSheetRowOutcomes(
9247
9250
  [input.runId],
9248
9251
  );
9249
9252
  const row = rows[0];
9253
+ const conditions = input.includeConditions
9254
+ ? await countRuntimeSheetConditionOutcomes(client, input)
9255
+ : undefined;
9250
9256
  return {
9251
9257
  completed: Math.max(0, Number(row?.completed ?? 0) || 0),
9252
9258
  failed: Math.max(0, Number(row?.failed ?? 0) || 0),
9253
9259
  total: Math.max(0, Number(row?.total ?? 0) || 0),
9260
+ ...(conditions ? { conditions } : {}),
9254
9261
  };
9255
9262
  }
9256
9263
 
9264
+ /** Aggregate durable witnesses, including false/skipped and true/failed cells. */
9265
+ export async function countRuntimeSheetConditionOutcomes(
9266
+ client: {
9267
+ query<Row extends Record<string, unknown> = Record<string, unknown>>(
9268
+ sql: string,
9269
+ params?: unknown[],
9270
+ ): Promise<{ rows: Row[] }>;
9271
+ },
9272
+ input: { schema: string; table: string; runId: string },
9273
+ ): Promise<Record<string, PlayConditionCounts>> {
9274
+ const { rows } = await client.query<{
9275
+ field: string;
9276
+ true_count: number | string;
9277
+ false_count: number | string;
9278
+ }>(
9279
+ `SELECT cell.key AS field,
9280
+ count(*) FILTER (WHERE cell.value #>> '{decide,branch}' = 'run') AS true_count,
9281
+ count(*) FILTER (WHERE cell.value #>> '{decide,branch}' = 'else') AS false_count
9282
+ FROM ${quoteIdentifier(input.schema)}.${quoteIdentifier(input.table)} AS sheet
9283
+ CROSS JOIN LATERAL jsonb_each(coalesce(sheet._cell_meta, '{}'::jsonb)) AS cell(key, value)
9284
+ WHERE sheet._run_id = $1::text
9285
+ AND cell.value ->> 'runId' = $1::text
9286
+ AND cell.value #>> '{decide,branch}' IN ('run', 'else')
9287
+ GROUP BY cell.key`,
9288
+ [input.runId],
9289
+ );
9290
+ return Object.fromEntries(
9291
+ rows.map((row) => [
9292
+ row.field,
9293
+ { true: Number(row.true_count), false: Number(row.false_count) },
9294
+ ]),
9295
+ );
9296
+ }
9297
+
9257
9298
  /**
9258
9299
  * Read the settled row-outcome counts for a dataset's runtime sheet through the
9259
9300
  * run's preloaded DB session. Used by the scheduler worker at terminal finalize
@@ -9265,7 +9306,7 @@ export async function countRuntimeSheetRowOutcomes(
9265
9306
  */
9266
9307
  export async function readRuntimeSheetRowOutcomeCounts(
9267
9308
  context: RuntimeApiContext & { playName: string; runId: string },
9268
- input: { tableNamespace: string },
9309
+ input: { tableNamespace: string; includeConditions?: boolean },
9269
9310
  ): Promise<RuntimeSheetRowOutcomeCounts> {
9270
9311
  if (context.dbSessionStrategy === 'gateway_only') {
9271
9312
  throw new Error(
@@ -9290,6 +9331,7 @@ export async function readRuntimeSheetRowOutcomeCounts(
9290
9331
  runId: context.runId,
9291
9332
  playName: context.playName,
9292
9333
  tableNamespace: input.tableNamespace,
9334
+ includeConditions: input.includeConditions,
9293
9335
  }),
9294
9336
  );
9295
9337
  }
@@ -1947,6 +1947,10 @@ async function readTerminalSheetRowOutcomes(input: {
1947
1947
  completed: number;
1948
1948
  failed: number;
1949
1949
  total: number;
1950
+ conditions?: Record<
1951
+ string,
1952
+ import('../cell-provenance').PlayConditionCounts
1953
+ >;
1950
1954
  }>
1951
1955
  > {
1952
1956
  const sheetNamespaces = [
@@ -1981,11 +1985,26 @@ async function readTerminalSheetRowOutcomes(input: {
1981
1985
  completed: number;
1982
1986
  failed: number;
1983
1987
  total: number;
1988
+ conditions?: Record<
1989
+ string,
1990
+ import('../cell-provenance').PlayConditionCounts
1991
+ >;
1984
1992
  }> = [];
1985
1993
  for (const tableNamespace of sheetNamespaces) {
1986
1994
  try {
1987
1995
  const counts = await readRuntimeSheetRowOutcomeCounts(runtimeContext, {
1988
1996
  tableNamespace,
1997
+ includeConditions: Boolean(
1998
+ (
1999
+ input.launch.contractSnapshot as {
2000
+ staticPipeline?: {
2001
+ docflow?: { nodes?: Array<{ kind: string; sketch?: boolean }> };
2002
+ };
2003
+ } | null
2004
+ )?.staticPipeline?.docflow?.nodes?.some(
2005
+ (node) => node.kind === 'decision' && !node.sketch,
2006
+ ),
2007
+ ),
1989
2008
  });
1990
2009
  outcomes.push({ tableNamespace, ...counts });
1991
2010
  } catch (error) {
@@ -4102,6 +4121,15 @@ async function executeAssignedAbsurdAttempt(input: {
4102
4121
  runnerResource.kind,
4103
4122
  ),
4104
4123
  sandboxId: runnerResource.sandboxId,
4124
+ environment:
4125
+ runnerResource.runtimeEnvironment ??
4126
+ runnerResource.daytonaEnvironment ??
4127
+ 'production',
4128
+ routingDomain:
4129
+ runnerResource.kind === 'daytona_sandbox'
4130
+ ? runnerResource.daytonaOrganizationId?.trim() ||
4131
+ null
4132
+ : runnerResource.modalAppId?.trim() || null,
4105
4133
  },
4106
4134
  options,
4107
4135
  );