deepline 0.1.150 → 0.1.151

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 (23) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +157 -140
  2. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/csv-rows.ts +2 -19
  3. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/row-isolation.ts +5 -53
  4. package/dist/bundling-sources/sdk/src/config.ts +2 -2
  5. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  6. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +100 -158
  7. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +3 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +54 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/map-row-outcome.ts +167 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/pacing.ts +79 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +39 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +19 -86
  13. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-transition.ts +90 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-session.ts +43 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +142 -11
  16. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +3 -2
  17. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +20 -23
  18. package/dist/cli/index.js +35 -3
  19. package/dist/cli/index.mjs +35 -3
  20. package/dist/index.js +3 -3
  21. package/dist/index.mjs +3 -3
  22. package/dist/plays/bundle-play-file.mjs +22 -19
  23. package/package.json +1 -1
@@ -0,0 +1,167 @@
1
+ import type { MapRowOutcome, MapRowOutcomeStatus } from './durability-store';
2
+
3
+ export const MAP_ROW_OUTCOME_RUNTIME_FIELDS = {
4
+ rowKey: '__deeplineRowKey',
5
+ cellMetaPatch: '__deeplineCellMetaPatch',
6
+ rowStatus: '__deeplineRowStatus',
7
+ rowError: '__deeplineRowError',
8
+ } as const;
9
+
10
+ const MAP_ROW_OUTCOME_RUNTIME_FIELD_SET = new Set<string>(
11
+ Object.values(MAP_ROW_OUTCOME_RUNTIME_FIELDS),
12
+ );
13
+
14
+ export type MapRowOutcomeRuntimeRow = Record<string, unknown>;
15
+
16
+ function isRecord(value: unknown): value is Record<string, unknown> {
17
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
18
+ }
19
+
20
+ export function resolveMapRowOutcomeKey(
21
+ row: MapRowOutcomeRuntimeRow,
22
+ ): string | null {
23
+ const key = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowKey];
24
+ return typeof key === 'string' && key.length > 0 ? key : null;
25
+ }
26
+
27
+ export function mapRowOutcomeRuntimeFields(input: {
28
+ key: string;
29
+ cellMetaPatch?: Record<string, unknown> | null;
30
+ status?: MapRowOutcomeStatus | null;
31
+ error?: string | null;
32
+ }): Record<string, unknown> {
33
+ return {
34
+ [MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowKey]: input.key,
35
+ ...(input.cellMetaPatch
36
+ ? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.cellMetaPatch]: input.cellMetaPatch }
37
+ : {}),
38
+ ...(input.status
39
+ ? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowStatus]: input.status }
40
+ : {}),
41
+ ...(input.error !== undefined
42
+ ? { [MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowError]: input.error }
43
+ : {}),
44
+ };
45
+ }
46
+
47
+ export function completedMapRowOutcome(input: {
48
+ key: string;
49
+ inputIndex?: number | null;
50
+ data: Record<string, unknown>;
51
+ cellMetaPatch?: Record<string, unknown> | null;
52
+ }): MapRowOutcome {
53
+ return {
54
+ key: input.key,
55
+ ...(input.inputIndex !== undefined ? { inputIndex: input.inputIndex } : {}),
56
+ data: input.data,
57
+ ...(input.cellMetaPatch ? { cellMetaPatch: input.cellMetaPatch } : {}),
58
+ };
59
+ }
60
+
61
+ export function failedMapRowOutcome(input: {
62
+ key: string;
63
+ inputIndex?: number | null;
64
+ data: Record<string, unknown>;
65
+ cellMetaPatch?: Record<string, unknown> | null;
66
+ error?: string | null;
67
+ }): MapRowOutcome {
68
+ return {
69
+ ...completedMapRowOutcome(input),
70
+ status: 'failed',
71
+ error: input.error ?? null,
72
+ };
73
+ }
74
+
75
+ export function mapRowOutcomeRuntimeRow(
76
+ outcome: MapRowOutcome,
77
+ ): Record<string, unknown> {
78
+ return {
79
+ ...outcome.data,
80
+ ...mapRowOutcomeRuntimeFields({
81
+ key: outcome.key,
82
+ cellMetaPatch: outcome.cellMetaPatch,
83
+ status: outcome.status,
84
+ error: outcome.error,
85
+ }),
86
+ };
87
+ }
88
+
89
+ export function copyMapRowOutcomeRuntimeFields<
90
+ TTarget extends MapRowOutcomeRuntimeRow,
91
+ >(target: TTarget, row: MapRowOutcomeRuntimeRow): TTarget {
92
+ const writableTarget = target as MapRowOutcomeRuntimeRow;
93
+ for (const runtimeField of MAP_ROW_OUTCOME_RUNTIME_FIELD_SET) {
94
+ if (runtimeField in row) {
95
+ writableTarget[runtimeField] = row[runtimeField];
96
+ }
97
+ }
98
+ return target;
99
+ }
100
+
101
+ export function stripMapRowOutcomeRuntimeFields<
102
+ TRow extends MapRowOutcomeRuntimeRow,
103
+ >(row: TRow): TRow {
104
+ const data: MapRowOutcomeRuntimeRow = {};
105
+ for (const fieldName of Reflect.ownKeys(row)) {
106
+ if (
107
+ typeof fieldName === 'string' &&
108
+ MAP_ROW_OUTCOME_RUNTIME_FIELD_SET.has(fieldName)
109
+ ) {
110
+ continue;
111
+ }
112
+ const descriptor = Object.getOwnPropertyDescriptor(row, fieldName);
113
+ if (!descriptor) continue;
114
+ Object.defineProperty(data, fieldName, descriptor);
115
+ }
116
+ return data as TRow;
117
+ }
118
+
119
+ export function mapRowOutcomeFromRuntimeRow(
120
+ row: MapRowOutcomeRuntimeRow,
121
+ ): MapRowOutcome | null {
122
+ if (typeof row.key === 'string' && isRecord(row.data)) {
123
+ return {
124
+ key: row.key,
125
+ data: row.data,
126
+ ...(isRecord(row.cellMetaPatch)
127
+ ? { cellMetaPatch: row.cellMetaPatch }
128
+ : {}),
129
+ ...(row.status === 'failed'
130
+ ? {
131
+ status: 'failed',
132
+ error: typeof row.error === 'string' ? row.error : null,
133
+ }
134
+ : {}),
135
+ };
136
+ }
137
+
138
+ const key = resolveMapRowOutcomeKey(row);
139
+ if (!key) return null;
140
+ const cellMetaPatch = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.cellMetaPatch];
141
+ const rowStatus = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowStatus];
142
+ const rowError = row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.rowError];
143
+ return {
144
+ key,
145
+ data: stripMapRowOutcomeRuntimeFields(row),
146
+ ...(isRecord(cellMetaPatch) ? { cellMetaPatch } : {}),
147
+ ...(rowStatus === 'failed'
148
+ ? {
149
+ status: 'failed',
150
+ error: typeof rowError === 'string' ? rowError : null,
151
+ }
152
+ : {}),
153
+ };
154
+ }
155
+
156
+ export function requireMapRowOutcomeFromRuntimeRow(
157
+ row: MapRowOutcomeRuntimeRow,
158
+ input: { tableNamespace: string },
159
+ ): MapRowOutcome {
160
+ const outcome = mapRowOutcomeFromRuntimeRow(row);
161
+ if (!outcome) {
162
+ throw new Error(
163
+ `persistCompletedMapRows received a row without a key for tableNamespace=${input.tableNamespace}.`,
164
+ );
165
+ }
166
+ return outcome;
167
+ }
@@ -0,0 +1,79 @@
1
+ import { resolveBuiltinPacing } from './builtin-pacing';
2
+ import type { PacingRule, PlayQueueHint } from './governor/rate-state-backend';
3
+
4
+ export type ResolvedPacingPolicy = {
5
+ provider: string;
6
+ rules: PacingRule[];
7
+ };
8
+
9
+ type PacingPolicyQueueHint = Pick<
10
+ PlayQueueHint,
11
+ 'provider' | 'ruleId' | 'requestsPerWindow' | 'windowMs' | 'maxConcurrency'
12
+ >;
13
+
14
+ function isFinitePositiveNumber(value: unknown): value is number {
15
+ return typeof value === 'number' && Number.isFinite(value) && value > 0;
16
+ }
17
+
18
+ export function pacingPolicyFromQueueHints(
19
+ hints: readonly PacingPolicyQueueHint[],
20
+ ): ResolvedPacingPolicy | null {
21
+ if (hints.length === 0) return null;
22
+ const provider = hints[0]?.provider?.trim();
23
+ if (!provider) return null;
24
+ const rules = hints.flatMap((hint) => {
25
+ if (
26
+ typeof hint.ruleId !== 'string' ||
27
+ !hint.ruleId.trim() ||
28
+ !isFinitePositiveNumber(hint.requestsPerWindow) ||
29
+ !isFinitePositiveNumber(hint.windowMs)
30
+ ) {
31
+ return [];
32
+ }
33
+ return [
34
+ {
35
+ ruleId: hint.ruleId,
36
+ requestsPerWindow: hint.requestsPerWindow,
37
+ windowMs: hint.windowMs,
38
+ maxConcurrency:
39
+ typeof hint.maxConcurrency === 'number' &&
40
+ Number.isFinite(hint.maxConcurrency) &&
41
+ hint.maxConcurrency > 0
42
+ ? hint.maxConcurrency
43
+ : null,
44
+ } satisfies PacingRule,
45
+ ];
46
+ });
47
+ return rules.length > 0 ? { provider, rules } : null;
48
+ }
49
+
50
+ export function pacingPolicyFromUnknownQueueHints(
51
+ value: unknown,
52
+ ): ResolvedPacingPolicy | null {
53
+ if (!Array.isArray(value)) return null;
54
+ return pacingPolicyFromQueueHints(
55
+ value
56
+ .filter((hint): hint is Record<string, unknown> =>
57
+ Boolean(hint && typeof hint === 'object' && !Array.isArray(hint)),
58
+ )
59
+ .map((hint) => ({
60
+ provider: typeof hint.provider === 'string' ? hint.provider : '',
61
+ ruleId: typeof hint.ruleId === 'string' ? hint.ruleId : '',
62
+ requestsPerWindow:
63
+ typeof hint.requestsPerWindow === 'number'
64
+ ? hint.requestsPerWindow
65
+ : Number.NaN,
66
+ windowMs:
67
+ typeof hint.windowMs === 'number' ? hint.windowMs : Number.NaN,
68
+ maxConcurrency:
69
+ typeof hint.maxConcurrency === 'number' ? hint.maxConcurrency : null,
70
+ })),
71
+ );
72
+ }
73
+
74
+ export function pacingPolicyForTool(
75
+ toolId: string,
76
+ hints: readonly PlayQueueHint[],
77
+ ): ResolvedPacingPolicy | null {
78
+ return resolveBuiltinPacing(toolId) ?? pacingPolicyFromQueueHints(hints);
79
+ }
@@ -0,0 +1,39 @@
1
+ import { isHardBillingToolHttpError } from './tool-http-errors';
2
+
3
+ /**
4
+ * Thrown by runner Adapters when a Play Run is externally cancelled. Row
5
+ * isolation must let this escape so in-flight user code stops cooperatively.
6
+ */
7
+ export class WorkflowAbortError extends Error {
8
+ override readonly name = 'WorkflowAbort';
9
+ constructor(message = 'Play run cancelled.') {
10
+ super(message);
11
+ }
12
+ }
13
+
14
+ export function isAbortLikeError(error: unknown): boolean {
15
+ if (!error) return false;
16
+ if (error instanceof WorkflowAbortError) return true;
17
+ if (error instanceof Error) {
18
+ if (error.name === 'WorkflowAbort' || error.name === 'AbortError') {
19
+ return true;
20
+ }
21
+ return /\b(cancell?ed|aborted|terminate[d]?)\b/i.test(error.message);
22
+ }
23
+ return false;
24
+ }
25
+
26
+ /**
27
+ * Errors that must stay run-fatal even under default map row failure isolation.
28
+ *
29
+ * Provider/tool HTTP failures, including exhausted 429/5xx retries, are row
30
+ * outcomes after the tool-call Adapter spends its local retry budget. Abort,
31
+ * Governor budget exhaustion, and hard billing failures escape row isolation.
32
+ */
33
+ export function isRowIsolationExemptError(error: unknown): boolean {
34
+ if (isAbortLikeError(error)) return true;
35
+ if (error instanceof Error && error.name === 'GovernorBudgetError') {
36
+ return true;
37
+ }
38
+ return isHardBillingToolHttpError(error);
39
+ }
@@ -60,6 +60,15 @@ import {
60
60
  import { stringifyPostgresJson } from './postgres-json';
61
61
  import { RECEIPT_STATUS_CODE, receiptStatusFromCode } from './receipt-status';
62
62
  import type { MapRowOutcome } from './durability-store';
63
+ import {
64
+ prepareRuntimeSheetRowTransitions,
65
+ type RuntimePreparedCompletedRow,
66
+ type RuntimePreparedFailedRow,
67
+ } from './runtime-sheet-row-transition';
68
+ import {
69
+ mapRowOutcomeRuntimeFields,
70
+ resolveMapRowOutcomeKey,
71
+ } from './map-row-outcome';
63
72
  import {
64
73
  DEEPLINE_CELL_META_FIELD,
65
74
  cellPolicyFields,
@@ -92,18 +101,6 @@ type RuntimeDatasetRowEntry = {
92
101
  row: Record<string, unknown>;
93
102
  inputIndex: number;
94
103
  };
95
- type RuntimePreparedCompletedRow = {
96
- key: string;
97
- input_index: number | null;
98
- data_patch: Record<string, unknown>;
99
- cell_meta_patch: Record<string, unknown>;
100
- };
101
-
102
- type RuntimePreparedFailedRow = RuntimePreparedCompletedRow & {
103
- /** Row-level error persisted to `_error`; never empty. */
104
- error: string;
105
- };
106
-
107
104
  const dbSessionCache = new Map<string, DbSessionCacheEntry>();
108
105
  const dbSessionInFlight = new Map<string, Promise<CreateDbSessionResponse>>();
109
106
  const postgresPools = new Map<string, RuntimePool>();
@@ -1680,35 +1677,6 @@ function cachedRuntimeCellMetaPatch(runId: string): Record<string, unknown> {
1680
1677
  };
1681
1678
  }
1682
1679
 
1683
- function completedRuntimeCellMetaPatch(input: {
1684
- runId: string;
1685
- outputFields: readonly string[];
1686
- rowPatch?: Record<string, unknown>;
1687
- }): Record<string, unknown> {
1688
- const patch: Record<string, unknown> = {};
1689
- const completedAt = Date.now();
1690
- for (const field of input.outputFields) {
1691
- const existing =
1692
- input.rowPatch?.[field] &&
1693
- typeof input.rowPatch[field] === 'object' &&
1694
- !Array.isArray(input.rowPatch[field])
1695
- ? (input.rowPatch[field] as Record<string, unknown>)
1696
- : {};
1697
- patch[field] = {
1698
- status: 'completed',
1699
- runId: input.runId,
1700
- completedAt,
1701
- ...existing,
1702
- };
1703
- }
1704
- for (const [field, meta] of Object.entries(input.rowPatch ?? {})) {
1705
- if (!Object.hasOwn(patch, field)) {
1706
- patch[field] = meta;
1707
- }
1708
- }
1709
- return patch;
1710
- }
1711
-
1712
1680
  function cachedRuntimeCellMetaUpdateSql(
1713
1681
  tableAlias: string,
1714
1682
  outputFields: readonly string[],
@@ -2554,7 +2522,7 @@ async function buildRuntimeSheetDatasetStartResult(
2554
2522
  ),
2555
2523
  sheetContract: input.sheetContract,
2556
2524
  }),
2557
- __deeplineRowKey: entry.key,
2525
+ ...mapRowOutcomeRuntimeFields({ key: entry.key }),
2558
2526
  })),
2559
2527
  tableNamespace: input.tableNamespace,
2560
2528
  };
@@ -2671,7 +2639,7 @@ async function buildRuntimeSheetDatasetStartResult(
2671
2639
  ),
2672
2640
  sheetContract: input.sheetContract,
2673
2641
  }),
2674
- __deeplineRowKey: entry.key,
2642
+ ...mapRowOutcomeRuntimeFields({ key: entry.key }),
2675
2643
  }));
2676
2644
  return {
2677
2645
  inserted: input.inserted,
@@ -2683,7 +2651,7 @@ async function buildRuntimeSheetDatasetStartResult(
2683
2651
  completedData: existingPendingRowsByKey.get(entry.key) ?? {},
2684
2652
  sheetContract: input.sheetContract,
2685
2653
  }),
2686
- __deeplineRowKey: entry.key,
2654
+ ...mapRowOutcomeRuntimeFields({ key: entry.key }),
2687
2655
  })),
2688
2656
  completedRows,
2689
2657
  tableNamespace: input.tableNamespace,
@@ -3061,9 +3029,8 @@ export async function startRuntimeSheetDataset(
3061
3029
  // non-enumerable alias fields on the JSON payload boundary.
3062
3030
  const cleanedRow = toSerializableCsvAliasedRow(row);
3063
3031
  const key =
3064
- typeof row.__deeplineRowKey === 'string'
3065
- ? row.__deeplineRowKey
3066
- : derivePlayRowIdentity(cleanedRow, input.tableNamespace);
3032
+ resolveMapRowOutcomeKey(row) ??
3033
+ derivePlayRowIdentity(cleanedRow, input.tableNamespace);
3067
3034
  if (key && !uniqueRows.has(key)) {
3068
3035
  uniqueRows.set(key, cleanedRow);
3069
3036
  }
@@ -3517,14 +3484,6 @@ async function failRuntimeMapRowChunks(
3517
3484
  return { updated };
3518
3485
  }
3519
3486
 
3520
- function normalizeRuntimeMapInputIndex(value: unknown): number | null {
3521
- if (typeof value !== 'number' || !Number.isFinite(value)) {
3522
- return null;
3523
- }
3524
- const normalized = Math.floor(value);
3525
- return normalized >= 0 ? normalized : null;
3526
- }
3527
-
3528
3487
  /**
3529
3488
  * Mark map rows terminal in the per-run scoped Postgres sheet table by key.
3530
3489
  * Mirrors server-side `store.completeSheetRows` semantics: UPDATE-by-key with
@@ -3603,37 +3562,11 @@ export async function completeRuntimeMapRows(
3603
3562
  .join(',\n ')}`
3604
3563
  : '';
3605
3564
 
3606
- const completedRows: RuntimePreparedCompletedRow[] = [];
3607
- const failedRows: RuntimePreparedFailedRow[] = [];
3608
- for (const [key, row] of uniqueRows.entries()) {
3609
- if (row.status === 'failed') {
3610
- // Failed rows persist only the cell meta the caller recorded (completed
3611
- // sibling cells + the failed cell). Defaulting every output field to
3612
- // 'completed' here would lie about the failed cell and break re-run
3613
- // recompute.
3614
- failedRows.push({
3615
- key,
3616
- input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
3617
- data_patch: row.data,
3618
- cell_meta_patch: row.cellMetaPatch ?? {},
3619
- error:
3620
- typeof row.error === 'string' && row.error.trim()
3621
- ? row.error
3622
- : 'Row execution failed.',
3623
- });
3624
- continue;
3625
- }
3626
- completedRows.push({
3627
- key,
3628
- input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
3629
- data_patch: row.data,
3630
- cell_meta_patch: completedRuntimeCellMetaPatch({
3631
- runId: input.runId,
3632
- outputFields: input.outputFields ?? [],
3633
- rowPatch: row.cellMetaPatch,
3634
- }),
3635
- });
3636
- }
3565
+ const { completedRows, failedRows } = prepareRuntimeSheetRowTransitions({
3566
+ rows: uniqueRows.values(),
3567
+ runId: input.runId,
3568
+ outputFields: input.outputFields ?? [],
3569
+ });
3637
3570
  const chunks = chunkValues(completedRows, DIRECT_POSTGRES_BATCH_SIZE);
3638
3571
  const failedChunks = chunkValues(failedRows, DIRECT_POSTGRES_BATCH_SIZE);
3639
3572
  const needsTransaction = chunks.length + failedChunks.length > 1;
@@ -0,0 +1,90 @@
1
+ import type { MapRowOutcome } from './durability-store';
2
+
3
+ export type RuntimePreparedCompletedRow = {
4
+ key: string;
5
+ input_index: number | null;
6
+ data_patch: Record<string, unknown>;
7
+ cell_meta_patch: Record<string, unknown>;
8
+ };
9
+
10
+ export type RuntimePreparedFailedRow = RuntimePreparedCompletedRow & {
11
+ /** Row-level error persisted to `_error`; never empty. */
12
+ error: string;
13
+ };
14
+
15
+ export function normalizeRuntimeMapInputIndex(value: unknown): number | null {
16
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
17
+ return null;
18
+ }
19
+ const normalized = Math.floor(value);
20
+ return normalized >= 0 ? normalized : null;
21
+ }
22
+
23
+ export function completedRuntimeCellMetaPatch(input: {
24
+ runId: string;
25
+ outputFields: readonly string[];
26
+ rowPatch?: Record<string, unknown>;
27
+ nowMs?: number;
28
+ }): Record<string, unknown> {
29
+ const patch: Record<string, unknown> = {};
30
+ const completedAt = input.nowMs ?? Date.now();
31
+ for (const field of input.outputFields) {
32
+ const existing =
33
+ input.rowPatch?.[field] &&
34
+ typeof input.rowPatch[field] === 'object' &&
35
+ !Array.isArray(input.rowPatch[field])
36
+ ? (input.rowPatch[field] as Record<string, unknown>)
37
+ : {};
38
+ patch[field] = {
39
+ status: 'completed',
40
+ runId: input.runId,
41
+ completedAt,
42
+ ...existing,
43
+ };
44
+ }
45
+ for (const [field, meta] of Object.entries(input.rowPatch ?? {})) {
46
+ if (!Object.hasOwn(patch, field)) {
47
+ patch[field] = meta;
48
+ }
49
+ }
50
+ return patch;
51
+ }
52
+
53
+ export function prepareRuntimeSheetRowTransitions(input: {
54
+ rows: Iterable<MapRowOutcome>;
55
+ runId: string;
56
+ outputFields: readonly string[];
57
+ }): {
58
+ completedRows: RuntimePreparedCompletedRow[];
59
+ failedRows: RuntimePreparedFailedRow[];
60
+ } {
61
+ const completedRows: RuntimePreparedCompletedRow[] = [];
62
+ const failedRows: RuntimePreparedFailedRow[] = [];
63
+ for (const row of input.rows) {
64
+ if (!row.key) continue;
65
+ if (row.status === 'failed') {
66
+ failedRows.push({
67
+ key: row.key,
68
+ input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
69
+ data_patch: row.data,
70
+ cell_meta_patch: row.cellMetaPatch ?? {},
71
+ error:
72
+ typeof row.error === 'string' && row.error.trim()
73
+ ? row.error
74
+ : 'Row execution failed.',
75
+ });
76
+ continue;
77
+ }
78
+ completedRows.push({
79
+ key: row.key,
80
+ input_index: normalizeRuntimeMapInputIndex(row.inputIndex),
81
+ data_patch: row.data,
82
+ cell_meta_patch: completedRuntimeCellMetaPatch({
83
+ runId: input.runId,
84
+ outputFields: input.outputFields,
85
+ rowPatch: row.cellMetaPatch,
86
+ }),
87
+ });
88
+ }
89
+ return { completedRows, failedRows };
90
+ }
@@ -0,0 +1,43 @@
1
+ export type RuntimeSheetSessionScope<TPreloadedSession = unknown> = {
2
+ baseUrl: string;
3
+ executorToken: string;
4
+ orgId: string;
5
+ preloadedDbSessions?: TPreloadedSession[] | null;
6
+ playName: string;
7
+ runId: string;
8
+ userEmail?: string | null;
9
+ };
10
+
11
+ function requireRuntimeSheetSessionField(
12
+ fieldName: string,
13
+ value: string,
14
+ ): string {
15
+ const trimmed = value.trim();
16
+ if (!trimmed) {
17
+ throw new Error(`Runtime Sheet Session requires ${fieldName}.`);
18
+ }
19
+ return trimmed;
20
+ }
21
+
22
+ export function runtimeSheetSessionScope<TPreloadedSession = unknown>(input: {
23
+ baseUrl: string;
24
+ executorToken: string;
25
+ orgId: string;
26
+ preloadedDbSessions?: TPreloadedSession[] | null;
27
+ playName: string;
28
+ runId: string;
29
+ userEmail?: string | null;
30
+ }): RuntimeSheetSessionScope<TPreloadedSession> {
31
+ return {
32
+ baseUrl: requireRuntimeSheetSessionField('baseUrl', input.baseUrl),
33
+ executorToken: requireRuntimeSheetSessionField(
34
+ 'executorToken',
35
+ input.executorToken,
36
+ ),
37
+ orgId: requireRuntimeSheetSessionField('orgId', input.orgId),
38
+ preloadedDbSessions: input.preloadedDbSessions ?? null,
39
+ playName: requireRuntimeSheetSessionField('playName', input.playName),
40
+ runId: requireRuntimeSheetSessionField('runId', input.runId),
41
+ userEmail: input.userEmail ?? null,
42
+ };
43
+ }