deepline 0.1.181 → 0.1.183

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 (70) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/child-play-await.ts +203 -6
  2. package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +8 -1
  3. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +633 -107
  4. package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +3 -2
  5. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1932 -1838
  6. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +116 -0
  7. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +542 -78
  8. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/output-datasets.ts +124 -12
  9. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +292 -11
  10. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +519 -0
  11. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +2381 -0
  12. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +18 -5
  13. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +130 -0
  14. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +39 -0
  15. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +2 -0
  16. package/dist/bundling-sources/sdk/src/client.ts +48 -5
  17. package/dist/bundling-sources/sdk/src/http.ts +23 -8
  18. package/dist/bundling-sources/sdk/src/play.ts +200 -26
  19. package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +56 -3
  20. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  21. package/dist/bundling-sources/sdk/src/types.ts +2 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +132 -10
  23. package/dist/bundling-sources/shared_libs/play-runtime/auth-scope-resolver.ts +92 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +4 -4
  25. package/dist/bundling-sources/shared_libs/play-runtime/batching-types.ts +8 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +101 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1857 -314
  28. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +106 -6
  29. package/dist/bundling-sources/shared_libs/play-runtime/dedup-backend.ts +0 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +1 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +4 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +116 -16
  33. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +186 -20
  34. package/dist/bundling-sources/shared_libs/play-runtime/dynamic-worker-version.ts +2 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +133 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +245 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -5
  38. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +14 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts +52 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +41 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +3 -2
  42. package/dist/bundling-sources/shared_libs/play-runtime/query-result-dataset.ts +120 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/receipt-sql.ts +124 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +6 -2
  45. package/dist/bundling-sources/shared_libs/play-runtime/row-isolation.ts +48 -2
  46. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +158 -15
  47. package/dist/bundling-sources/shared_libs/play-runtime/run-terminal-source.ts +45 -0
  48. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +9 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +2037 -262
  50. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +42 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-attempt-state-machine.ts +183 -0
  52. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-errors.ts +88 -0
  53. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +8 -1
  54. package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +103 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +19 -1
  56. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +343 -0
  57. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +43 -0
  58. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +11 -0
  59. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +139 -17
  60. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-state-machine.ts +437 -0
  61. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +83 -6
  62. package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +24 -17
  63. package/dist/cli/index.js +85 -23
  64. package/dist/cli/index.mjs +85 -23
  65. package/dist/index.d.mts +4 -1
  66. package/dist/index.d.ts +4 -1
  67. package/dist/index.js +274 -35
  68. package/dist/index.mjs +274 -35
  69. package/dist/plays/bundle-play-file.mjs +2 -2
  70. package/package.json +1 -1
@@ -8,12 +8,16 @@ import type {
8
8
  PlayDatasetInput,
9
9
  PlayDatasetRow,
10
10
  } from '../plays/dataset';
11
- import type { PlayQueueHint } from './governor/rate-state-backend';
11
+ import type {
12
+ PlayQueueHint,
13
+ RateStateBackend,
14
+ } from './governor/rate-state-backend';
12
15
  import type { GovernanceSnapshot } from './governor/governor';
13
16
  import type { AnyBatchOperationStrategy } from './batching-types';
14
17
  import type { ToolResultMetadataInput } from './tool-result';
15
18
  import type { PreviousCell } from './cell-staleness';
16
19
  import type { MapRowOutcome } from './durability-store';
20
+ import type { WorkReceiptFailureKind } from './work-receipts';
17
21
 
18
22
  export interface RowState {
19
23
  results: Map<string, unknown>;
@@ -22,8 +26,12 @@ export interface RowState {
22
26
  export interface ToolCallRequest {
23
27
  callId: string;
24
28
  cacheKey: string;
29
+ cacheable?: boolean;
25
30
  receiptKey?: string | null;
31
+ executionAuthScopeDigest?: string | null;
32
+ receiptLeaseId?: string | null;
26
33
  force?: boolean;
34
+ forceFailedRefresh?: boolean;
27
35
  rowId: number;
28
36
  fieldName?: string;
29
37
  toolId: string;
@@ -45,7 +53,12 @@ export interface RuntimeStepReceipt {
45
53
  status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
46
54
  output?: unknown;
47
55
  error?: string;
56
+ failureKind?: WorkReceiptFailureKind | null;
48
57
  runId?: string | null;
58
+ leaseId?: string | null;
59
+ leaseOwnerRunId?: string | null;
60
+ leaseOwnerAttempt?: number | null;
61
+ leaseExpiresAt?: string | null;
49
62
  claimState?: 'claimed' | 'existing';
50
63
  }
51
64
 
@@ -56,25 +69,42 @@ export interface GetRuntimeStepReceiptInput {
56
69
  export interface ClaimRuntimeStepReceiptInput {
57
70
  key: string;
58
71
  runId: string;
72
+ runAttempt?: number | null;
73
+ leaseAware?: boolean;
59
74
  reclaimRunning?: boolean;
60
75
  forceRefresh?: boolean;
76
+ forceFailedRefresh?: boolean;
61
77
  }
62
78
 
63
79
  export interface CompleteRuntimeStepReceiptInput {
64
80
  key: string;
65
81
  runId: string;
82
+ runAttempt?: number | null;
83
+ leaseId?: string | null;
66
84
  output: unknown | null;
67
85
  }
68
86
 
87
+ export interface ReleaseRuntimeStepReceiptInput {
88
+ key: string;
89
+ runId: string;
90
+ runAttempt?: number | null;
91
+ leaseId?: string | null;
92
+ }
93
+
69
94
  export interface FailRuntimeStepReceiptInput {
70
95
  key: string;
71
96
  runId: string;
97
+ runAttempt?: number | null;
98
+ leaseId?: string | null;
72
99
  error: string;
100
+ failureKind?: WorkReceiptFailureKind | null;
73
101
  }
74
102
 
75
103
  export interface SkipRuntimeStepReceiptInput {
76
104
  key: string;
77
105
  runId: string;
106
+ runAttempt?: number | null;
107
+ leaseId?: string | null;
78
108
  output?: unknown | null;
79
109
  }
80
110
 
@@ -85,14 +115,19 @@ export interface GetRuntimeStepReceiptsInput {
85
115
  export interface ClaimRuntimeStepReceiptsInput {
86
116
  keys: string[];
87
117
  runId: string;
118
+ runAttempt?: number | null;
119
+ leaseAware?: boolean;
88
120
  reclaimRunning?: boolean;
89
121
  forceRefresh?: boolean;
122
+ forceFailedRefresh?: boolean;
90
123
  }
91
124
 
92
125
  export interface CompleteRuntimeStepReceiptsInput {
93
126
  receipts: Array<{
94
127
  key: string;
95
128
  runId: string;
129
+ runAttempt?: number | null;
130
+ leaseId?: string | null;
96
131
  output: unknown | null;
97
132
  }>;
98
133
  }
@@ -101,10 +136,20 @@ export interface FailRuntimeStepReceiptsInput {
101
136
  receipts: Array<{
102
137
  key: string;
103
138
  runId: string;
139
+ runAttempt?: number | null;
140
+ leaseId?: string | null;
104
141
  error: string;
142
+ failureKind?: WorkReceiptFailureKind | null;
105
143
  }>;
106
144
  }
107
145
 
146
+ export interface HeartbeatRuntimeStepReceiptsInput {
147
+ keys: string[];
148
+ runId: string;
149
+ runAttempt?: number | null;
150
+ leaseId: string;
151
+ }
152
+
108
153
  export interface CsvOptions {
109
154
  description?: string;
110
155
  columns?: Record<string, string | readonly string[]>;
@@ -270,6 +315,10 @@ export interface BatchRequest {
270
315
  export interface MapStartResult {
271
316
  /** Rows that need processing for this run. Previous values may be included for context. */
272
317
  pendingRows: Record<string, unknown>[];
318
+ /** Rows already terminal for this run and safe to count as completed. */
319
+ completedRows?: Record<string, unknown>[];
320
+ /** Rows leased by another active attempt and not executable by this attempt. */
321
+ blockedRows?: Record<string, unknown>[];
273
322
  /** Resolved table namespace. */
274
323
  tableNamespace: string;
275
324
  }
@@ -402,6 +451,7 @@ export interface ContextOptions {
402
451
  graphHash?: string | null;
403
452
  artifactHash?: string | null;
404
453
  convexUrl?: string;
454
+ runtimeSchedulerSchema?: string | null;
405
455
  staticPipeline?: PlayStaticPipeline | null;
406
456
  workflowId?: string;
407
457
  sessionId?: string;
@@ -410,9 +460,15 @@ export interface ContextOptions {
410
460
  checkpoint?: PlayCheckpoint;
411
461
  /** Enables durable boundary replay such as ctx.sleep() resumptions. */
412
462
  durableBoundaries?: boolean;
413
- /** Run-level cache policy. forceToolRefresh bypasses ctx.tools.execute receipts. */
463
+ /**
464
+ * Run-level cache policy.
465
+ * forceToolRefresh bypasses all ctx.tools.execute receipts.
466
+ * forceFailedToolRefresh reclaims only failed receipts, preserving completed
467
+ * provider-call idempotency while allowing forced repair runs to make progress.
468
+ */
414
469
  cachePolicy?: {
415
470
  forceToolRefresh?: boolean;
471
+ forceFailedToolRefresh?: boolean;
416
472
  };
417
473
  /** Called after each durable batch completes — for Temporal heartbeating. */
418
474
  onBatchComplete?: (checkpoint: PlayCheckpoint) => void;
@@ -438,6 +494,7 @@ export interface ContextOptions {
438
494
  playName?: string;
439
495
  playId?: string;
440
496
  runId?: string;
497
+ executorToken?: string;
441
498
  staticPipeline?: PlayStaticPipeline | null;
442
499
  forceRefresh?: boolean;
443
500
  },
@@ -452,6 +509,7 @@ export interface ContextOptions {
452
509
  playName?: string;
453
510
  playId?: string;
454
511
  runId?: string;
512
+ executorToken?: string;
455
513
  tableNamespace: string;
456
514
  rows: MapRowOutcome[];
457
515
  outputFields: string[];
@@ -459,12 +517,25 @@ export interface ContextOptions {
459
517
  }) => Promise<void>;
460
518
  playId?: string;
461
519
  runId?: string;
520
+ runAttempt?: number | null;
521
+ /**
522
+ * Optional shared provider-rate state for fleet substrates. Local/test
523
+ * contexts omit this and use the in-process backend.
524
+ */
525
+ rateState?: RateStateBackend;
526
+ /**
527
+ * Loud guard for production Node substrates: when true, constructing a
528
+ * context without a shared rate-state backend is a configuration error.
529
+ */
530
+ requireSharedRateState?: boolean;
462
531
  resolvePlay?: (playRef: string) => Promise<ResolvedPlayExecution | null>;
463
532
  getToolQueueHints?: (toolId: string) => Promise<readonly PlayQueueHint[]>;
464
533
  getToolRetryPolicy?: (toolId: string) => Promise<{
465
534
  retrySafeTransientHttp?: boolean;
466
535
  } | null>;
467
536
  getToolActionCacheVersion?: (toolId: string) => Promise<string> | string;
537
+ getToolAuthScopeDigest?: (toolId: string) => Promise<string> | string;
538
+ invalidateToolAuthScopeDigest?: (toolId: string) => void;
468
539
  getToolTargetGetters?: (
469
540
  toolId: string,
470
541
  output: string,
@@ -501,25 +572,41 @@ export interface ContextOptions {
501
572
  ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
502
573
  getRuntimeStepReceipts?: (
503
574
  input: GetRuntimeStepReceiptsInput,
504
- ) => Promise<RuntimeStepReceipt[]> | RuntimeStepReceipt[];
575
+ ) =>
576
+ | Promise<Array<RuntimeStepReceipt | null>>
577
+ | Array<RuntimeStepReceipt | null>;
505
578
  claimRuntimeStepReceipt?: (
506
579
  input: ClaimRuntimeStepReceiptInput,
507
580
  ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
508
581
  claimRuntimeStepReceipts?: (
509
582
  input: ClaimRuntimeStepReceiptsInput,
510
- ) => Promise<RuntimeStepReceipt[]> | RuntimeStepReceipt[];
583
+ ) =>
584
+ | Promise<Array<RuntimeStepReceipt | null>>
585
+ | Array<RuntimeStepReceipt | null>;
511
586
  completeRuntimeStepReceipt?: (
512
587
  input: CompleteRuntimeStepReceiptInput,
513
588
  ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
589
+ releaseRuntimeStepReceipt?: (
590
+ input: ReleaseRuntimeStepReceiptInput,
591
+ ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
514
592
  completeRuntimeStepReceipts?: (
515
593
  input: CompleteRuntimeStepReceiptsInput,
516
- ) => Promise<RuntimeStepReceipt[]> | RuntimeStepReceipt[];
594
+ ) =>
595
+ | Promise<Array<RuntimeStepReceipt | null>>
596
+ | Array<RuntimeStepReceipt | null>;
517
597
  failRuntimeStepReceipt?: (
518
598
  input: FailRuntimeStepReceiptInput,
519
599
  ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
520
600
  failRuntimeStepReceipts?: (
521
601
  input: FailRuntimeStepReceiptsInput,
522
- ) => Promise<RuntimeStepReceipt[]> | RuntimeStepReceipt[];
602
+ ) =>
603
+ | Promise<Array<RuntimeStepReceipt | null>>
604
+ | Array<RuntimeStepReceipt | null>;
605
+ heartbeatRuntimeStepReceipts?: (
606
+ input: HeartbeatRuntimeStepReceiptsInput,
607
+ ) =>
608
+ | Promise<Array<RuntimeStepReceipt | null>>
609
+ | Array<RuntimeStepReceipt | null>;
523
610
  skipRuntimeStepReceipt?: (
524
611
  input: SkipRuntimeStepReceiptInput,
525
612
  ) => Promise<RuntimeStepReceipt | null> | RuntimeStepReceipt | null;
@@ -579,6 +666,15 @@ export interface PlayCheckpoint {
579
666
  output?: unknown;
580
667
  completedAt?: number;
581
668
  }
669
+ | {
670
+ kind: 'child_play';
671
+ childRunId: string;
672
+ childPlayName: string;
673
+ status: 'completed' | 'failed' | 'cancelled';
674
+ output?: unknown;
675
+ error?: string;
676
+ completedAt?: number;
677
+ }
582
678
  >;
583
679
  /** Per-map execution frames keyed by invocation id. */
584
680
  mapFrames?: Record<string, MapExecutionFrame>;
@@ -782,6 +878,10 @@ export interface PlayRowUpdate {
782
878
  key: string;
783
879
  rowId: number;
784
880
  tableNamespace?: string | null;
881
+ attemptId?: string | null;
882
+ attemptOwnerRunId?: string | null;
883
+ attemptSeq?: number | null;
884
+ attemptExpiresAt?: string | null;
785
885
  status?: 'running' | 'completed' | 'failed';
786
886
  stage?: string | null;
787
887
  provider?: string | null;
@@ -81,6 +81,7 @@ const testRateLimitBatchStrategy: BatchOperationStrategy<
81
81
  batchOperation: 'test_batch_rate_limit',
82
82
  kind: 'identifier_batch',
83
83
  maxBatchSize: 200,
84
+ bucketKeyPayloadFields: ['stage'],
84
85
  canBatchWith(left, right) {
85
86
  return String(left.stage || '') === String(right.stage || '');
86
87
  },
@@ -3,6 +3,7 @@ import type {
3
3
  CompleteRuntimeStepReceiptInput,
4
4
  FailRuntimeStepReceiptInput,
5
5
  GetRuntimeStepReceiptInput,
6
+ ReleaseRuntimeStepReceiptInput,
6
7
  RuntimeStepReceipt,
7
8
  SkipRuntimeStepReceiptInput,
8
9
  } from './ctx-types';
@@ -50,6 +51,9 @@ export interface PlayDurabilityStore {
50
51
  completeRuntimeStepReceipt(
51
52
  input: CompleteRuntimeStepReceiptInput,
52
53
  ): Promise<RuntimeStepReceipt | null>;
54
+ releaseRuntimeStepReceipt(
55
+ input: ReleaseRuntimeStepReceiptInput,
56
+ ): Promise<RuntimeStepReceipt | null>;
53
57
  failRuntimeStepReceipt(
54
58
  input: FailRuntimeStepReceiptInput,
55
59
  ): Promise<RuntimeStepReceipt | null>;
@@ -47,37 +47,36 @@ export const durableCallStaleBucket = durableCacheStaleBucket;
47
47
 
48
48
  export function buildDurableToolCallCacheKey(input: {
49
49
  orgId?: string | null;
50
- playId: string;
51
50
  toolId: string;
52
51
  requestInput: Record<string, unknown>;
53
52
  authScopeDigest?: string | null;
54
53
  providerActionVersion?: string | null;
55
54
  cachePolicyVersion?: string | null;
56
55
  staleAfterSeconds?: number | null;
56
+ playLocalScope?: string | null;
57
57
  }): string {
58
58
  const orgId = input.orgId?.trim() || 'org';
59
- const playId = input.playId.trim();
60
- if (!playId) {
61
- throw new Error('Missing durable tool play id.');
62
- }
63
59
  const toolId = input.toolId.trim();
64
60
  if (!toolId) {
65
- throw new Error('Missing durable tool id.');
61
+ throw new Error('Durable tool call cache key requires a non-empty toolId.');
66
62
  }
67
- const p = input.providerActionVersion?.trim();
68
- if (!p) {
69
- throw new Error('Missing durable tool action version.');
63
+ const providerActionVersion = input.providerActionVersion?.trim();
64
+ if (!providerActionVersion) {
65
+ throw new Error(
66
+ 'Durable tool call cache key requires a provider action version.',
67
+ );
70
68
  }
69
+ const playLocalScope = input.playLocalScope?.trim() || 'play';
71
70
  const digest = sha256Hex(
72
71
  stableStringify({
73
72
  kind: 'tool' satisfies DurableCallKind,
74
73
  orgId,
75
- playId,
74
+ playLocalScope,
76
75
  toolId,
77
76
  normalizedToolId: normalizeTableNamespace(toolId),
78
77
  requestInput: input.requestInput,
79
78
  authScopeDigest: input.authScopeDigest ?? null,
80
- providerActionVersion: p,
79
+ providerActionVersion,
81
80
  cachePolicyVersion:
82
81
  input.cachePolicyVersion ?? DURABLE_CALL_CACHE_POLICY_VERSION,
83
82
  staleBucket: durableCacheStaleBucket({
@@ -101,9 +100,7 @@ export function buildDurableCtxCallCacheKey(input: {
101
100
  const playId = input.playId?.trim() || 'play';
102
101
  const id = input.id.trim();
103
102
  if (!id) {
104
- throw new Error(
105
- `Durable ${input.kind} call cache key requires a non-empty id.`,
106
- );
103
+ throw new Error(`Durable ${input.kind} key needs id.`);
107
104
  }
108
105
  const normalizedId = normalizeTableNamespace(id);
109
106
  const digest = sha256Hex(
@@ -124,16 +121,119 @@ export function buildDurableCtxCallCacheKey(input: {
124
121
  return `ctx:${orgId}:call:${input.kind}:${normalizedId}:${digest}`;
125
122
  }
126
123
 
124
+ export function buildDurableRunPlaySemanticKey(input: {
125
+ childPlayName: string;
126
+ input: Record<string, unknown>;
127
+ childRevisionFingerprint?: string | null;
128
+ rowScope?: Record<string, unknown> | null;
129
+ }): string {
130
+ return sha256Hex(
131
+ stableStringify({
132
+ childPlayName: input.childPlayName.trim(),
133
+ childRevisionFingerprint: input.childRevisionFingerprint ?? null,
134
+ input: input.input,
135
+ rowScope: input.rowScope ?? null,
136
+ }),
137
+ );
138
+ }
139
+
127
140
  export function buildDurableToolCallAuthScopeDigest(input: {
128
141
  orgId?: string | null;
129
- userEmail?: string | null;
130
142
  toolId: string;
143
+ executionAuthScopeDigest?: string | null;
131
144
  }): string {
132
145
  return sha256Hex(
133
146
  stableStringify({
134
147
  orgId: input.orgId?.trim() || 'org',
135
- actor: input.userEmail?.trim().toLowerCase() || 'workspace',
136
148
  toolId: input.toolId.trim(),
149
+ executionAuthScopeDigest: input.executionAuthScopeDigest?.trim() || null,
137
150
  }),
138
151
  );
139
152
  }
153
+
154
+ export function buildDurableToolReceiptPrefix(input: {
155
+ orgId?: string | null;
156
+ toolId: string;
157
+ }): string {
158
+ const orgId = input.orgId?.trim() || 'org';
159
+ const toolId = input.toolId.trim();
160
+ if (!toolId) {
161
+ throw new Error('Durable tool receipt prefix requires a non-empty toolId.');
162
+ }
163
+ return `ctx:${orgId}:call:tool:${normalizeTableNamespace(toolId)}:`;
164
+ }
165
+
166
+ export function buildDurableToolProviderIdempotencyKey(input: {
167
+ receiptKey: string;
168
+ force?: boolean;
169
+ receiptLeaseId?: string | null;
170
+ fallbackAttemptId?: string | null;
171
+ }): string {
172
+ const receiptKey = input.receiptKey.trim();
173
+ if (!receiptKey) {
174
+ throw new Error('Need receipt key.');
175
+ }
176
+ if (input.force !== true) {
177
+ return receiptKey;
178
+ }
179
+ const attemptId =
180
+ input.receiptLeaseId?.trim() || input.fallbackAttemptId?.trim() || null;
181
+ if (!attemptId) {
182
+ throw new Error('Forced idempotency needs lease id.');
183
+ }
184
+ return `${receiptKey}:force:${attemptId}`;
185
+ }
186
+
187
+ export function buildDurableToolAggregateReceiptKey(input: {
188
+ receiptKeys: string[];
189
+ prefix?: 'batch' | null;
190
+ aggregateReceiptPrefix?: string | null;
191
+ }): string {
192
+ const receiptKeys = input.receiptKeys.map((key) => key.trim()).sort();
193
+ const digest = sha256Hex(stableStringify(receiptKeys));
194
+ if (input.prefix !== 'batch') {
195
+ return digest;
196
+ }
197
+ const scopedPrefix =
198
+ input.aggregateReceiptPrefix?.trim() ||
199
+ commonDurableToolReceiptPrefix(receiptKeys);
200
+ if (!scopedPrefix) {
201
+ throw new Error('Batch receipt key needs same-tool members.');
202
+ }
203
+ return `${scopedPrefix}batch:${digest}`;
204
+ }
205
+
206
+ function commonDurableToolReceiptPrefix(
207
+ receiptKeys: readonly string[],
208
+ ): string {
209
+ let commonPrefix: string | null = null;
210
+ for (const receiptKey of receiptKeys) {
211
+ const match = /^(ctx:[^:]+:call:tool:[^:]+:)/.exec(receiptKey);
212
+ if (!match) {
213
+ return '';
214
+ }
215
+ const prefix = match[1] ?? '';
216
+ if (!commonPrefix) {
217
+ commonPrefix = prefix;
218
+ continue;
219
+ }
220
+ if (commonPrefix !== prefix) {
221
+ return '';
222
+ }
223
+ }
224
+ return commonPrefix ?? '';
225
+ }
226
+
227
+ export function buildDurableToolAggregateProviderIdempotencyKey(input: {
228
+ aggregateReceiptKey: string;
229
+ receiptKeys: string[];
230
+ providerIdempotencyKeys: string[];
231
+ }): string {
232
+ const receiptKeys = input.receiptKeys.map((key) => key.trim());
233
+ const providerIdempotencyKeys = input.providerIdempotencyKeys.map((key) =>
234
+ key.trim(),
235
+ );
236
+ return `${input.aggregateReceiptKey}:ordered:${sha256Hex(
237
+ stableStringify({ receiptKeys, providerIdempotencyKeys }),
238
+ )}`;
239
+ }