deepline 0.1.119 → 0.1.121

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 (148) hide show
  1. package/README.md +4 -0
  2. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +21 -0
  3. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +185 -0
  4. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-batch.ts +107 -0
  5. package/dist/{repo → bundling-sources}/sdk/src/client.ts +116 -12
  6. package/dist/bundling-sources/sdk/src/compat.ts +191 -0
  7. package/dist/bundling-sources/sdk/src/gtm.ts +146 -0
  8. package/dist/bundling-sources/sdk/src/helpers.ts +12 -0
  9. package/dist/{repo → bundling-sources}/sdk/src/index.ts +2 -1
  10. package/dist/{repo → bundling-sources}/sdk/src/play.ts +3 -1
  11. package/dist/{repo → bundling-sources}/sdk/src/plays/bundle-play-file.ts +17 -5
  12. package/dist/{repo → bundling-sources}/sdk/src/release.ts +2 -2
  13. package/dist/{repo → bundling-sources}/sdk/src/runs/observe-transport.ts +2 -3
  14. package/dist/bundling-sources/shared_libs/play-data-plane/index.ts +3 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +838 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5510 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +261 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +828 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/dataset-id.ts +10 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +50 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +20 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/event-wait-tools.ts +9 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +171 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-diagnosis.ts +321 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-target.ts +158 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +34 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +34 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +50 -0
  29. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +119 -0
  30. package/dist/{repo → bundling-sources}/shared_libs/play-runtime/map-row-identity.ts +1 -1
  31. package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +636 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/postgres-json.ts +9 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/progress-emitter.ts +197 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/projection.ts +262 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +143 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/public-play-contract.ts +42 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +40 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +178 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +4015 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constraints.ts +2 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +238 -0
  42. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +53 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +149 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +68 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/tool-batch-executor.ts +149 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +159 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/tracing.ts +33 -0
  48. package/dist/bundling-sources/shared_libs/play-runtime/waterfall-replay.ts +79 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +139 -0
  50. package/dist/bundling-sources/shared_libs/plays/artifact-transport.ts +14 -0
  51. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +49 -0
  52. package/dist/bundling-sources/shared_libs/plays/compiler-manifest.ts +41 -0
  53. package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +163 -0
  54. package/dist/bundling-sources/shared_libs/plays/definition.ts +267 -0
  55. package/dist/bundling-sources/shared_libs/plays/file-refs.ts +11 -0
  56. package/dist/bundling-sources/shared_libs/plays/input-contract.ts +146 -0
  57. package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +190 -0
  58. package/dist/bundling-sources/shared_libs/plays/runtime-validation.ts +417 -0
  59. package/dist/bundling-sources/shared_libs/plays/tool-codegen.ts +142 -0
  60. package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +274 -0
  61. package/dist/bundling-sources/shared_libs/temporal/preview-config.ts +150 -0
  62. package/dist/cli/index.js +811 -2207
  63. package/dist/cli/index.mjs +847 -2258
  64. package/dist/compiler-manifest-BjoRENv9.d.mts +227 -0
  65. package/dist/compiler-manifest-BjoRENv9.d.ts +227 -0
  66. package/dist/index.d.mts +8 -231
  67. package/dist/index.d.ts +8 -231
  68. package/dist/index.js +101 -15
  69. package/dist/index.mjs +101 -15
  70. package/dist/plays/bundle-play-file.d.mts +120 -0
  71. package/dist/plays/bundle-play-file.d.ts +120 -0
  72. package/dist/plays/bundle-play-file.mjs +1830 -0
  73. package/package.json +4 -9
  74. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/child-play-await.ts +0 -0
  75. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/child-play-submit.ts +0 -0
  76. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/coordinator-entry.ts +0 -0
  77. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/dedup-do.ts +0 -0
  78. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/entry.ts +0 -0
  79. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/csv-rows.ts +0 -0
  80. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/dataset-handles.ts +0 -0
  81. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +0 -0
  82. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/live-progress.ts +0 -0
  83. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +0 -0
  84. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/receipts.ts +0 -0
  85. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/row-isolation.ts +0 -0
  86. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/tool-http-errors.ts +0 -0
  87. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-instance-create.ts +0 -0
  88. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-retry-state.ts +0 -0
  89. /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-retry.ts +0 -0
  90. /package/dist/{repo → bundling-sources}/sdk/src/agent-runtime.ts +0 -0
  91. /package/dist/{repo → bundling-sources}/sdk/src/config.ts +0 -0
  92. /package/dist/{repo → bundling-sources}/sdk/src/errors.ts +0 -0
  93. /package/dist/{repo → bundling-sources}/sdk/src/http.ts +0 -0
  94. /package/dist/{repo → bundling-sources}/sdk/src/plays/harness-stub.ts +0 -0
  95. /package/dist/{repo → bundling-sources}/sdk/src/plays/local-file-discovery.ts +0 -0
  96. /package/dist/{repo → bundling-sources}/sdk/src/stream-reconnect.ts +0 -0
  97. /package/dist/{repo → bundling-sources}/sdk/src/tool-output.ts +0 -0
  98. /package/dist/{repo → bundling-sources}/sdk/src/types.ts +0 -0
  99. /package/dist/{repo → bundling-sources}/sdk/src/version.ts +0 -0
  100. /package/dist/{repo → bundling-sources}/sdk/src/worker-play-entry.ts +0 -0
  101. /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/cell-policy.ts +0 -0
  102. /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/column-names.ts +0 -0
  103. /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/sheet-contract.ts +0 -0
  104. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/backend.ts +0 -0
  105. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/batch-runtime.ts +0 -0
  106. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/batching-types.ts +0 -0
  107. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/cell-staleness.ts +0 -0
  108. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/coordinator-headers.ts +0 -0
  109. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/csv-rename.ts +0 -0
  110. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session-crypto.ts +0 -0
  111. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session-plan.ts +0 -0
  112. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session.ts +0 -0
  113. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/dedup-backend.ts +0 -0
  114. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/default-batch-strategies.ts +0 -0
  115. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/email-status.ts +0 -0
  116. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/execution-plan.ts +0 -0
  117. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/extractor-targets.ts +0 -0
  118. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/fullenrich-batching.ts +0 -0
  119. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +0 -0
  120. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/governor.ts +0 -0
  121. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/policy.ts +0 -0
  122. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/rate-state-backend.ts +0 -0
  123. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/live-events.ts +0 -0
  124. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/play-runtime-batching-registry.ts +0 -0
  125. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/profiles.ts +0 -0
  126. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/providers.ts +0 -0
  127. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-failure.ts +0 -0
  128. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-ledger.ts +0 -0
  129. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-snapshot-stream.ts +0 -0
  130. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/scheduler-backend.ts +0 -0
  131. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/secret-capability.ts +0 -0
  132. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/secret-redaction.ts +0 -0
  133. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/step-lifecycle-tracker.ts +0 -0
  134. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/step-program-dataset-builder.ts +0 -0
  135. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/submit-limits.ts +0 -0
  136. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/tool-result.ts +0 -0
  137. /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/work-receipts.ts +0 -0
  138. /package/dist/{repo → bundling-sources}/shared_libs/plays/bootstrap-routes.ts +0 -0
  139. /package/dist/{repo → bundling-sources}/shared_libs/plays/bundling/index.ts +0 -0
  140. /package/dist/{repo → bundling-sources}/shared_libs/plays/bundling/limits.ts +0 -0
  141. /package/dist/{repo → bundling-sources}/shared_libs/plays/contracts.ts +0 -0
  142. /package/dist/{repo → bundling-sources}/shared_libs/plays/dataset.ts +0 -0
  143. /package/dist/{repo → bundling-sources}/shared_libs/plays/row-identity.ts +0 -0
  144. /package/dist/{repo → bundling-sources}/shared_libs/plays/secret-guardrails.ts +0 -0
  145. /package/dist/{repo → bundling-sources}/shared_libs/plays/static-pipeline.ts +0 -0
  146. /package/dist/{repo → bundling-sources}/shared_libs/security/outbound-url-policy.ts +0 -0
  147. /package/dist/{repo → bundling-sources}/shared_libs/security/safe-fetch.ts +0 -0
  148. /package/dist/{repo → bundling-sources}/shared_libs/temporal/constants.ts +0 -0
package/README.md CHANGED
@@ -48,6 +48,10 @@ Only two cases require `npm run sdk:build`:
48
48
  cd sdk && bun run build # or: npm run sdk:build from root
49
49
  ```
50
50
 
51
+ The published npm package is intentionally thin: it ships the SDK, CLI, and the
52
+ minimal local play bundler required for `plays run --file`, `plays publish`, and
53
+ `enrich`, without shipping Convex or broad app/provider source snapshots.
54
+
51
55
  ## Env files
52
56
 
53
57
  | File | Set by | Contains |
@@ -0,0 +1,21 @@
1
+ # Per-play Worker Runtime
2
+
3
+ These modules are bundled into every `esm_workers` play artifact. Keep them:
4
+
5
+ - dependency-free, except for type-only imports
6
+ - small enough to understand in one sitting
7
+ - specific to the Cloudflare Worker execution path
8
+
9
+ Do not import broad `shared_libs/play-runtime/*` modules here. Shared runtime
10
+ modules are optimized for the Node/Temporal runner and often bring along code
11
+ that is correct but expensive in a per-graphHash Worker isolate.
12
+
13
+ If a helper needs zod, Neon, runtime API transport, validation registries, or
14
+ other heavy leaves, put it behind the `env.HARNESS` service binding in
15
+ `apps/play-harness-worker` and call it through `sdk/src/plays/harness-stub.ts`.
16
+
17
+ The intended split is:
18
+
19
+ - `entry.ts`: request/workflow orchestration and ctx construction
20
+ - `runtime/*`: tiny per-play primitives that must execute in the isolate
21
+ - `apps/play-harness-worker`: heavy shared leaves kept warm behind RPC
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Tiny batching helpers for the per-play Worker runtime.
3
+ *
4
+ * This mirrors the small subset of shared_libs/play-runtime batching that the
5
+ * Workers harness needs without importing the broader shared runtime graph.
6
+ */
7
+
8
+ import type { AnyBatchOperationStrategy } from '../../../../shared_libs/play-runtime/batching-types';
9
+
10
+ export type ChunkExecutionResult<TRequest, TResult> = {
11
+ request: TRequest;
12
+ result: TResult | null;
13
+ /**
14
+ * Present when this request's execution rejected. The request failed but
15
+ * its siblings in the chunk kept their results — one provider hiccup must
16
+ * stay a per-request failure, not a chunk-level abort that discards
17
+ * completed sibling work.
18
+ */
19
+ error?: unknown;
20
+ };
21
+
22
+ export async function executeChunkedRequests<TRequest, TResult>(input: {
23
+ requests: TRequest[];
24
+ batchSize: number;
25
+ execute: (request: TRequest) => Promise<TResult>;
26
+ onChunkComplete?: (
27
+ results: Array<ChunkExecutionResult<TRequest, TResult>>,
28
+ ) => void | Promise<void>;
29
+ }): Promise<Array<ChunkExecutionResult<TRequest, TResult>>> {
30
+ const results: Array<ChunkExecutionResult<TRequest, TResult>> = [];
31
+ for (let start = 0; start < input.requests.length; start += input.batchSize) {
32
+ const chunk = input.requests.slice(start, start + input.batchSize);
33
+ const settled = await Promise.allSettled(
34
+ chunk.map((request) => input.execute(request)),
35
+ );
36
+ for (let index = 0; index < chunk.length; index += 1) {
37
+ const request = chunk[index]!;
38
+ const outcome = settled[index]!;
39
+ if (outcome.status === 'rejected') {
40
+ results.push({ request, result: null, error: outcome.reason });
41
+ continue;
42
+ }
43
+ results.push({ request, result: outcome.value });
44
+ }
45
+ await input.onChunkComplete?.(results.slice(results.length - chunk.length));
46
+ }
47
+ return results;
48
+ }
49
+
50
+ export function compileRequestsWithStrategy<TRequest>(input: {
51
+ requests: TRequest[];
52
+ strategy: AnyBatchOperationStrategy;
53
+ getPayload: (request: TRequest) => Record<string, unknown>;
54
+ }): Array<{
55
+ batchOperation: string;
56
+ memberRequests: TRequest[];
57
+ batchPayload: Record<string, unknown>;
58
+ splitResults: (value: unknown) => Array<unknown | null>;
59
+ }> {
60
+ const compiledBatches: Array<{
61
+ batchOperation: string;
62
+ memberRequests: TRequest[];
63
+ batchPayload: Record<string, unknown>;
64
+ splitResults: (value: unknown) => Array<unknown | null>;
65
+ }> = [];
66
+ const bucketedRequests = new Map<string, TRequest[]>();
67
+ for (const request of input.requests) {
68
+ const payload = input.getPayload(request);
69
+ const bucketKey = String(input.strategy.toBucketKey(payload));
70
+ const bucket = bucketedRequests.get(bucketKey);
71
+ if (bucket) bucket.push(request);
72
+ else bucketedRequests.set(bucketKey, [request]);
73
+ }
74
+ for (const bucketRequests of bucketedRequests.values()) {
75
+ let currentBatch: TRequest[] = [];
76
+ const flushBatch = () => {
77
+ if (currentBatch.length === 0) return;
78
+ const memberRequests = [...currentBatch];
79
+ const compiled = input.strategy.compile(
80
+ memberRequests.map((request) => input.getPayload(request)),
81
+ );
82
+ compiledBatches.push({
83
+ batchOperation: compiled.batchOperation,
84
+ memberRequests,
85
+ batchPayload: compiled.batchPayload,
86
+ splitResults: (value: unknown) => {
87
+ const splitResults = input.strategy.splitResult(value, compiled);
88
+ return memberRequests.map(
89
+ (_, index) => splitResults[index]?.result ?? null,
90
+ );
91
+ },
92
+ });
93
+ currentBatch = [];
94
+ };
95
+ for (const request of bucketRequests) {
96
+ const payload = input.getPayload(request);
97
+ const canAppend =
98
+ currentBatch.length > 0 &&
99
+ currentBatch.length < input.strategy.maxBatchSize &&
100
+ currentBatch.every((existing) =>
101
+ input.strategy.canBatchWith(input.getPayload(existing), payload),
102
+ );
103
+ if (!canAppend && currentBatch.length > 0) flushBatch();
104
+ currentBatch.push(request);
105
+ }
106
+ flushBatch();
107
+ }
108
+ return compiledBatches;
109
+ }
110
+
111
+ export function getDefaultPlayRuntimeBatchStrategy(
112
+ operation: string | null | undefined,
113
+ ): AnyBatchOperationStrategy | null {
114
+ if (operation !== 'test_rate_limit') return null;
115
+ return {
116
+ sourceOperation: 'test_rate_limit',
117
+ batchOperation: 'test_batch_rate_limit',
118
+ kind: 'identifier_batch',
119
+ maxBatchSize: 200,
120
+ canBatchWith(left, right) {
121
+ return String(left.stage || '') === String(right.stage || '');
122
+ },
123
+ toBucketKey(payload) {
124
+ return `test_batch_rate_limit:${String(payload.stage || '')}`;
125
+ },
126
+ toItemKey(payload) {
127
+ return String(payload.lead_id || payload.row_number || payload.key || '');
128
+ },
129
+ compile(payloads) {
130
+ const stage = String(payloads[0]?.stage || '');
131
+ const simulatedDelayMs =
132
+ typeof payloads[0]?.simulated_delay_ms === 'number'
133
+ ? payloads[0].simulated_delay_ms
134
+ : undefined;
135
+ const items = payloads.map((payload, index) => ({
136
+ itemKey: String(
137
+ payload.lead_id || payload.row_number || `row_${index}`,
138
+ ),
139
+ payload,
140
+ }));
141
+ return {
142
+ batchOperation: 'test_batch_rate_limit',
143
+ batchPayload: {
144
+ key: 'batch',
145
+ ...(stage ? { stage } : {}),
146
+ ...(simulatedDelayMs !== undefined
147
+ ? { simulated_delay_ms: simulatedDelayMs }
148
+ : {}),
149
+ items,
150
+ },
151
+ items,
152
+ };
153
+ },
154
+ splitResult(fullResult, compiled) {
155
+ const container =
156
+ fullResult != null &&
157
+ typeof fullResult === 'object' &&
158
+ !Array.isArray(fullResult)
159
+ ? (fullResult as Record<string, unknown>)
160
+ : {};
161
+ const nestedData =
162
+ container.data != null &&
163
+ typeof container.data === 'object' &&
164
+ !Array.isArray(container.data)
165
+ ? (container.data as Record<string, unknown>)
166
+ : {};
167
+ const resultItems = Array.isArray(container.items)
168
+ ? (container.items as Array<{ itemKey?: string; result?: unknown }>)
169
+ : Array.isArray(nestedData.items)
170
+ ? (nestedData.items as Array<{ itemKey?: string; result?: unknown }>)
171
+ : [];
172
+ return compiled.items.map((item, index) => ({
173
+ itemKey: item.itemKey,
174
+ result:
175
+ index < resultItems.length
176
+ ? (resultItems[index]?.result ?? null)
177
+ : null,
178
+ rawResult:
179
+ index < resultItems.length
180
+ ? (resultItems[index]?.result ?? null)
181
+ : null,
182
+ }));
183
+ },
184
+ };
185
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Per-play Worker tool batch executor.
3
+ *
4
+ * This is intentionally local to the Worker bundle. Pulling the shared
5
+ * executor module back in would widen the bundle graph for a tiny helper.
6
+ */
7
+
8
+ export type ToolBatchRequest = {
9
+ runId: string;
10
+ orgId: string;
11
+ toolId: string;
12
+ operation: string;
13
+ provider: string;
14
+ items: Array<{
15
+ itemKey: string;
16
+ payload: Record<string, unknown>;
17
+ inputHash?: string | null;
18
+ }>;
19
+ waterfallId?: string | null;
20
+ stageId?: string | null;
21
+ fieldName?: string | null;
22
+ mapName?: string | null;
23
+ chunkIndex?: number | null;
24
+ userProvidedRateLimitKey?: string | null;
25
+ providerBatchSize: number;
26
+ };
27
+
28
+ type ToolBatchItemResult = {
29
+ itemKey: string;
30
+ result: unknown;
31
+ cached?: boolean;
32
+ };
33
+
34
+ export function createToolBatchExecutor(input: {
35
+ executeProviderBatch(batch: {
36
+ request: ToolBatchRequest;
37
+ batchIndex: number;
38
+ idempotencyKeys: string[];
39
+ rateLimitKey: string;
40
+ items: ToolBatchRequest['items'];
41
+ }): Promise<ToolBatchItemResult[]>;
42
+ }) {
43
+ return {
44
+ async executeToolBatch(request: ToolBatchRequest) {
45
+ const providerBatchSize = Math.max(
46
+ 1,
47
+ Math.floor(request.providerBatchSize),
48
+ );
49
+ const batches: Array<ToolBatchRequest['items']> = [];
50
+ for (
51
+ let index = 0;
52
+ index < request.items.length;
53
+ index += providerBatchSize
54
+ ) {
55
+ batches.push(request.items.slice(index, index + providerBatchSize));
56
+ }
57
+ const results: ToolBatchItemResult[] = [];
58
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
59
+ const items = batches[batchIndex]!;
60
+ results.push(
61
+ ...(await input.executeProviderBatch({
62
+ request,
63
+ batchIndex,
64
+ items,
65
+ rateLimitKey: [
66
+ request.orgId,
67
+ request.provider,
68
+ request.operation,
69
+ request.userProvidedRateLimitKey ?? '',
70
+ ].join(':'),
71
+ idempotencyKeys: items.map((item) =>
72
+ [
73
+ request.runId,
74
+ request.mapName ?? '',
75
+ request.chunkIndex ?? '',
76
+ item.itemKey,
77
+ request.fieldName ?? '',
78
+ request.waterfallId ?? '',
79
+ request.stageId ?? '',
80
+ item.inputHash ?? stableStringify(item.payload),
81
+ ].join(':'),
82
+ ),
83
+ })),
84
+ );
85
+ }
86
+ return {
87
+ runId: request.runId,
88
+ toolId: request.toolId,
89
+ operation: request.operation,
90
+ provider: request.provider,
91
+ batchCount: batches.length,
92
+ itemCount: request.items.length,
93
+ results,
94
+ };
95
+ },
96
+ };
97
+ }
98
+
99
+ function stableStringify(value: unknown): string {
100
+ if (value === null || typeof value !== 'object') return JSON.stringify(value);
101
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
102
+ const record = value as Record<string, unknown>;
103
+ return `{${Object.keys(record)
104
+ .sort()
105
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
106
+ .join(',')}}`;
107
+ }
@@ -2051,23 +2051,126 @@ export class DeeplineClient {
2051
2051
  }
2052
2052
 
2053
2053
  /**
2054
- * Observe one run's live events through the Convex Run Snapshot
2055
- * subscription transport (ADR-0008). Yields the same `play.*` event
2056
- * envelopes as {@link streamPlayRunEvents} and ends after the terminal
2057
- * snapshot. Throws {@link RunObserveTransportUnavailableError} when this
2058
- * server cannot serve the transport (older server, unconfigured grants, or
2059
- * unreachable Convex) — callers fall back to the SSE stream with a notice.
2054
+ * Observe one run's live events. Uses the Convex Run Snapshot subscription
2055
+ * transport first (ADR-0008), then falls back to the canonical SSE stream
2056
+ * when the subscription transport or its optional client modules are not
2057
+ * available. Pass `fallback: 'none'` to receive
2058
+ * {@link RunObserveTransportUnavailableError} instead.
2060
2059
  */
2061
- observeRunEvents(
2060
+ async *observeRunEvents(
2061
+ runId: string,
2062
+ options?: {
2063
+ signal?: AbortSignal;
2064
+ onNotice?: (message: string) => void;
2065
+ fallback?: 'sse' | 'none';
2066
+ },
2067
+ ): AsyncGenerator<PlayLiveEvent> {
2068
+ let yieldedObserveEvent = false;
2069
+ try {
2070
+ for await (const event of observeRunEvents({
2071
+ http: this.http,
2072
+ runId,
2073
+ signal: options?.signal,
2074
+ onNotice: options?.onNotice,
2075
+ }) as AsyncGenerator<PlayLiveEvent>) {
2076
+ yieldedObserveEvent = true;
2077
+ yield event;
2078
+ }
2079
+ } catch (error) {
2080
+ if (
2081
+ !(error instanceof RunObserveTransportUnavailableError) ||
2082
+ yieldedObserveEvent ||
2083
+ options?.fallback === 'none'
2084
+ ) {
2085
+ throw error;
2086
+ }
2087
+ options?.onNotice?.(
2088
+ `[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`,
2089
+ );
2090
+ yield* this.streamPlayRunEventsUntilTerminal(runId, options);
2091
+ }
2092
+ }
2093
+
2094
+ private async *streamPlayRunEventsUntilTerminal(
2062
2095
  runId: string,
2063
2096
  options?: { signal?: AbortSignal; onNotice?: (message: string) => void },
2064
2097
  ): AsyncGenerator<PlayLiveEvent> {
2065
- return observeRunEvents({
2066
- http: this.http,
2098
+ const state: PlayLiveStatusState = {
2067
2099
  runId,
2068
- signal: options?.signal,
2069
- onNotice: options?.onNotice,
2070
- }) as AsyncGenerator<PlayLiveEvent>;
2100
+ status: 'running',
2101
+ logs: [],
2102
+ lastLogSeq: 0,
2103
+ latest: null,
2104
+ };
2105
+ let lastEventId: string | undefined;
2106
+ let reconnectAttempt = 0;
2107
+
2108
+ for (;;) {
2109
+ if (options?.signal?.aborted) {
2110
+ return;
2111
+ }
2112
+ const connectedAt = Date.now();
2113
+ let sawEvent = false;
2114
+ let endedReason = 'stream window ended before a terminal event';
2115
+ try {
2116
+ for await (const event of this.streamPlayRunEvents(runId, {
2117
+ mode: 'cli',
2118
+ signal: options?.signal,
2119
+ ...(lastEventId ? { lastEventId } : {}),
2120
+ })) {
2121
+ sawEvent = true;
2122
+ if (event.cursor?.trim()) {
2123
+ lastEventId = event.cursor;
2124
+ }
2125
+ yield event;
2126
+ const status = updatePlayLiveStatusState(state, event);
2127
+ if (status && TERMINAL_PLAY_STATUSES.has(status.status)) {
2128
+ return;
2129
+ }
2130
+ }
2131
+ } catch (error) {
2132
+ if (options?.signal?.aborted) {
2133
+ return;
2134
+ }
2135
+ if (!isTransientPlayStreamError(error)) {
2136
+ throw error;
2137
+ }
2138
+ endedReason = error instanceof Error ? error.message : String(error);
2139
+ }
2140
+
2141
+ let refreshed: PlayStatus | null = null;
2142
+ try {
2143
+ refreshed = await this.getRunStatus(runId);
2144
+ } catch (error) {
2145
+ if (!isTransientPlayStreamError(error)) {
2146
+ throw error;
2147
+ }
2148
+ }
2149
+ if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
2150
+ yield {
2151
+ cursor: String(Date.now()),
2152
+ streamId: `sse-fallback:${runId}`,
2153
+ scope: 'play',
2154
+ type: 'play.run.status',
2155
+ at: new Date().toISOString(),
2156
+ payload: refreshed as unknown as Record<string, unknown>,
2157
+ };
2158
+ return;
2159
+ }
2160
+
2161
+ if (
2162
+ sawEvent ||
2163
+ Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS
2164
+ ) {
2165
+ reconnectAttempt = 0;
2166
+ }
2167
+ const delayMs = streamReconnectDelayMs(reconnectAttempt);
2168
+ reconnectAttempt += 1;
2169
+ options?.onNotice?.(
2170
+ `[observe] SSE tail window ended before terminal status (${endedReason}); reconnecting to run ${runId}`,
2171
+ );
2172
+ await sleep(delayMs);
2173
+ }
2071
2174
  }
2072
2175
 
2073
2176
  /**
@@ -2088,6 +2191,7 @@ export class DeeplineClient {
2088
2191
  for await (const event of this.observeRunEvents(runId, {
2089
2192
  signal: options?.signal,
2090
2193
  onNotice: options?.onNotice,
2194
+ fallback: 'none',
2091
2195
  })) {
2092
2196
  const status = updatePlayLiveStatusState(state, event);
2093
2197
  if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
@@ -0,0 +1,191 @@
1
+ import { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ export type SdkCompatibilityStatus =
7
+ | 'current'
8
+ | 'update_available'
9
+ | 'deprecated'
10
+ | 'unsupported';
11
+
12
+ export type SdkCompatibilityResponse = {
13
+ ok: boolean;
14
+ status: SdkCompatibilityStatus;
15
+ current: string | null;
16
+ latest: string;
17
+ minimum_supported: string;
18
+ deprecated_below: string;
19
+ api_contract: string;
20
+ update_available: boolean;
21
+ update_required: boolean;
22
+ message: string;
23
+ update_command: string;
24
+ command?: string | null;
25
+ auto_update?: {
26
+ should_auto_update: boolean;
27
+ required: boolean;
28
+ reason: 'required' | 'patch_lag' | 'rollback_forced' | null;
29
+ patch_lag: number | null;
30
+ patch_lag_threshold: number;
31
+ update_command: string;
32
+ };
33
+ cli_family?: {
34
+ action: 'force_python';
35
+ reason: 'rollback_forced';
36
+ };
37
+ };
38
+
39
+ const CHECK_TIMEOUT_MS = 2_000;
40
+ const COMPAT_CACHE_TTL_MS = 5 * 60 * 1000;
41
+
42
+ type CompatCacheFile = {
43
+ entries?: Record<
44
+ string,
45
+ {
46
+ savedAt: number;
47
+ response: SdkCompatibilityResponse;
48
+ }
49
+ >;
50
+ };
51
+
52
+ function shouldSkipCompatibilityCheck(): boolean {
53
+ const value =
54
+ process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
55
+ return value === '1' || value === 'true' || value === 'yes';
56
+ }
57
+
58
+ function compatibilityCachePath(): string {
59
+ return join(homedir(), '.cache', 'deepline', 'sdk-compat-cache.json');
60
+ }
61
+
62
+ function compatibilityCacheKey(
63
+ baseUrl: string,
64
+ command: string | null | undefined,
65
+ ): string {
66
+ return JSON.stringify({
67
+ baseUrl: baseUrl.replace(/\/$/, ''),
68
+ version: SDK_VERSION,
69
+ apiContract: SDK_API_CONTRACT,
70
+ command: command?.trim() || null,
71
+ });
72
+ }
73
+
74
+ function readCachedCompatibility(
75
+ baseUrl: string,
76
+ command: string | null | undefined,
77
+ ): SdkCompatibilityResponse | null {
78
+ try {
79
+ const path = compatibilityCachePath();
80
+ if (!existsSync(path)) {
81
+ return null;
82
+ }
83
+ const parsed = JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile;
84
+ const entry = parsed.entries?.[compatibilityCacheKey(baseUrl, command)];
85
+ if (!entry || Date.now() - entry.savedAt > COMPAT_CACHE_TTL_MS) {
86
+ return null;
87
+ }
88
+ return entry.response;
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+
94
+ function writeCachedCompatibility(
95
+ baseUrl: string,
96
+ command: string | null | undefined,
97
+ response: SdkCompatibilityResponse,
98
+ ): void {
99
+ try {
100
+ const path = compatibilityCachePath();
101
+ const existing = existsSync(path)
102
+ ? (JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile)
103
+ : {};
104
+ const entries = existing.entries ?? {};
105
+ entries[compatibilityCacheKey(baseUrl, command)] = {
106
+ savedAt: Date.now(),
107
+ response,
108
+ };
109
+ mkdirSync(dirname(path), { recursive: true });
110
+ writeFileSync(path, `${JSON.stringify({ entries }, null, 2)}\n`);
111
+ } catch {
112
+ // Compatibility checks are advisory unless the server explicitly responds
113
+ // with a blocking policy. Cache failures must not block normal CLI usage.
114
+ }
115
+ }
116
+
117
+ export async function checkSdkCompatibility(
118
+ baseUrl: string,
119
+ options: { command?: string | null } = {},
120
+ ): Promise<{
121
+ response: SdkCompatibilityResponse | null;
122
+ error: Error | null;
123
+ }> {
124
+ if (shouldSkipCompatibilityCheck()) {
125
+ return { response: null, error: null };
126
+ }
127
+ const controller = new AbortController();
128
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
129
+ try {
130
+ const url = new URL('/api/v2/sdk/compat', baseUrl);
131
+ url.searchParams.set('version', SDK_VERSION);
132
+ if (options.command?.trim()) {
133
+ url.searchParams.set('command', options.command.trim());
134
+ }
135
+ const response = await fetch(url, {
136
+ method: 'GET',
137
+ headers: {
138
+ 'User-Agent': `deepline-ts-sdk/${SDK_VERSION}`,
139
+ 'X-Deepline-SDK-Version': SDK_VERSION,
140
+ 'X-Deepline-API-Contract': SDK_API_CONTRACT,
141
+ },
142
+ signal: controller.signal,
143
+ });
144
+ const data = (await response
145
+ .json()
146
+ .catch(() => null)) as SdkCompatibilityResponse | null;
147
+ if (data) {
148
+ writeCachedCompatibility(baseUrl, options.command, data);
149
+ }
150
+ return { response: data, error: null };
151
+ } catch (error) {
152
+ const cached = readCachedCompatibility(baseUrl, options.command);
153
+ if (cached?.ok === false || cached?.status === 'unsupported') {
154
+ return { response: cached, error: null };
155
+ }
156
+ return {
157
+ response: null,
158
+ error: error instanceof Error ? error : new Error(String(error)),
159
+ };
160
+ } finally {
161
+ clearTimeout(timeout);
162
+ }
163
+ }
164
+
165
+ export function enforceSdkCompatibilityResponse(
166
+ response: SdkCompatibilityResponse | null,
167
+ ): void {
168
+ if (!response) {
169
+ return;
170
+ }
171
+ if (response.update_required) {
172
+ throw new Error(response.message);
173
+ }
174
+ if (
175
+ response.status === 'deprecated' ||
176
+ response.status === 'update_available'
177
+ ) {
178
+ process.stderr.write(`${response.message}\n`);
179
+ }
180
+ }
181
+
182
+ export async function enforceSdkCompatibility(
183
+ baseUrl: string,
184
+ options: { command?: string | null } = {},
185
+ ): Promise<void> {
186
+ const { response, error } = await checkSdkCompatibility(baseUrl, options);
187
+ if (error || !response) {
188
+ return;
189
+ }
190
+ enforceSdkCompatibilityResponse(response);
191
+ }