deepline 0.3.238 → 0.3.239

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.
@@ -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.238',
203
+ version: '0.3.239',
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: {
@@ -263,10 +263,40 @@ function targetLabel(target: PlayActivityTarget): string {
263
263
  case 'step':
264
264
  return target.label ?? target.stepId;
265
265
  case 'capacity':
266
- return target.label ?? `${target.pool} capacity`;
266
+ return target.pool === 'provider'
267
+ ? providerCapacityLabel(target.label)
268
+ : (target.label ?? `${target.pool} capacity`);
267
269
  }
268
270
  }
269
271
 
272
+ /** Older waits carried a rate scope rather than a customer-facing provider. */
273
+ export function providerCapacityLabel(label?: string): string {
274
+ if (!label) return 'The provider';
275
+ if (label.startsWith('org:')) return label.split(':')[1] || 'The provider';
276
+ return label.includes(':') ? 'The provider' : label;
277
+ }
278
+
279
+ function publicProviderCapacityObservation(
280
+ observation: PlayActivityObservation,
281
+ ): PlayActivityObservation {
282
+ if (
283
+ observation.target.kind !== 'capacity' ||
284
+ observation.target.pool !== 'provider'
285
+ )
286
+ return observation;
287
+ if (
288
+ !observation.target.label?.includes(':') &&
289
+ !observation.activityId.startsWith('capacity:provider:org:')
290
+ )
291
+ return observation;
292
+ const label = providerCapacityLabel(observation.target.label);
293
+ return {
294
+ ...observation,
295
+ activityId: `capacity:provider:${label}`,
296
+ target: { ...observation.target, label },
297
+ };
298
+ }
299
+
270
300
  export function summarizePlayActivity(
271
301
  observation: PlayActivityObservation,
272
302
  ): string {
@@ -297,10 +327,10 @@ export function summarizePlayActivity(
297
327
  if (state.reason.kind === 'lease') {
298
328
  return `Contended: waiting for ${state.reason.resourceKind} lease ${state.reason.resourceLabel}.`;
299
329
  }
300
- return `Paused: ${label}; the legacy producer did not report a typed reason.`;
330
+ return `Waiting: ${label}.`;
301
331
  case 'retrying':
302
332
  if (state.reason === 'rate_limit') {
303
- return `Waiting for ${label} request capacity; the run will retry automatically.`;
333
+ return `Waiting for ${label}'s rate limit to allow more calls. Completed work is saved; remaining calls will continue automatically.`;
304
334
  }
305
335
  return `Delayed: ${label} will retry automatically.`;
306
336
  case 'scheduled':
@@ -385,6 +415,7 @@ export function projectPlayRunActivity(input: {
385
415
  status: string;
386
416
  updatedAt?: number | null;
387
417
  waitKind?: string | null;
418
+ waitBoundaryId?: string | null;
388
419
  waitUntil?: number | null;
389
420
  eventKey?: string | null;
390
421
  runtimeBackend?: string | null;
@@ -410,17 +441,24 @@ export function projectPlayRunActivity(input: {
410
441
  const latestExplicit = [...(input.explicit ?? [])]
411
442
  .filter((candidate) => !isTerminalActivityState(candidate.state))
412
443
  .sort((left, right) => right.observedAt - left.observedAt)[0];
413
- // A provider wait is an observation, not a permanent run state. Once its
414
- // deadline has passed, use the current lifecycle/step facts until the next
415
- // producer observation arrives instead of claiming the old hold persists.
444
+ // Retry-After is the earliest retry, not evidence that execution resumed.
445
+ // Retain the reason while its provider boundary is still waiting, including
446
+ // lagging readers with no wait projection. A different durable wait wins.
447
+ const stillWaitingForProvider =
448
+ normalizedStatus === 'waiting' &&
449
+ (!input.waitKind ||
450
+ (input.waitKind === 'sleep' &&
451
+ input.waitBoundaryId?.includes(':provider-backpressure-') === true));
416
452
  const explicit =
453
+ !stillWaitingForProvider &&
417
454
  latestExplicit?.target.kind === 'capacity' &&
418
455
  latestExplicit.target.pool === 'provider' &&
419
456
  latestExplicit.state.kind === 'retrying' &&
420
457
  latestExplicit.state.retryAt < now
421
458
  ? null
422
459
  : latestExplicit;
423
- if (explicit) return projection(explicit, now);
460
+ if (explicit)
461
+ return projection(publicProviderCapacityObservation(explicit), now);
424
462
 
425
463
  if (input.waitKind === 'detached_runner') {
426
464
  return projection(
@@ -57,6 +57,7 @@ import type {
57
57
  } from './governor/rate-state-backend';
58
58
  import type { MapRowOutcome } from './durability-store';
59
59
  import { stringifyPostgresJson } from './postgres-json';
60
+ import { providerCapacityLabel } from './activity-observation';
60
61
  import { RUNTIME_ROW_OUTPUT_NOT_JSON_SERIALIZABLE } from './runtime-sheet-row-transition';
61
62
  import {
62
63
  RuntimeSheetRowWriter,
@@ -204,7 +205,7 @@ import {
204
205
  TOOL_EXECUTE_RUNNER_ACCEPTS_VALUE,
205
206
  TOOL_EXECUTE_PROVIDER_RATE_SCOPE_HEADER,
206
207
  TOOL_EXECUTE_PROVIDER_RATE_RETRY_SNAPSHOT_HEADER,
207
- TOOL_EXECUTE_PROVIDER_THROTTLE_HEADER,
208
+ isAttestedProvider429Response,
208
209
  classifyToolExecuteHttpFailure,
209
210
  createToolExecuteHttpFailureAttemptTracker,
210
211
  parseToolExecuteRetryAfterMs,
@@ -9143,6 +9144,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9143
9144
 
9144
9145
  successfulCount = Math.max(0, successfulCount - staleCompletedKeys.size);
9145
9146
  failedCount = Math.max(0, failedCount - staleFailedKeys.size);
9147
+ this.#options.onMapRowsSettled?.({
9148
+ playName: this.#options.playName,
9149
+ runId: this.#options.runId,
9150
+ tableNamespace: resolvedTableNamespace,
9151
+ succeededRows: successfulCount,
9152
+ failedRows: failedCount,
9153
+ });
9146
9154
 
9147
9155
  // Isolated failures are a partial result only when at least one admitted
9148
9156
  // row completed. A fully failed admission run is systemic: preserve the
@@ -9995,6 +10003,15 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
9995
10003
  });
9996
10004
  }
9997
10005
 
10006
+ this.#options.onMapRowsSettled?.({
10007
+ playName: this.#options.playName,
10008
+ runId: this.#options.runId,
10009
+ tableNamespace: resolvedTableNamespace,
10010
+ succeededRows:
10011
+ previouslyCompletedRowKeys.length + visibleCompletedRows.length,
10012
+ failedRows: durableFailedRows,
10013
+ });
10014
+
9998
10015
  // This map's terminal rows have already crossed the persistence barrier
9999
10016
  // above. Keep that verified snapshot as the authored return value instead
10000
10017
  // of immediately re-reading the Runtime Sheet projection. A second read
@@ -14532,11 +14549,12 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
14532
14549
  retryAtMs: number,
14533
14550
  ): void {
14534
14551
  const observedAt = Date.now();
14552
+ const label = providerCapacityLabel(provider);
14535
14553
  this.emitExecutionEvent({
14536
14554
  type: 'activity.observed',
14537
14555
  observation: {
14538
- activityId: `capacity:provider:${provider}`,
14539
- target: { kind: 'capacity', pool: 'provider', label: provider },
14556
+ activityId: `capacity:provider:${label}`,
14557
+ target: { kind: 'capacity', pool: 'provider', label },
14540
14558
  state: {
14541
14559
  kind: 'retrying',
14542
14560
  reason: 'rate_limit',
@@ -19546,12 +19564,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
19546
19564
  : undefined;
19547
19565
  }
19548
19566
  if (
19549
- dispatchedResponse.status === 429 &&
19550
- dispatchedResponse.headers.get(
19551
- TOOL_EXECUTE_PROVIDER_THROTTLE_HEADER,
19552
- ) === '1'
19567
+ isAttestedProvider429Response(dispatchedResponse)
19553
19568
  ) {
19554
19569
  this.resourceGovernor.recordProvider429();
19570
+ }
19571
+ if (
19572
+ dispatchedResponse.status === 429 &&
19573
+ isAttestedProvider429Response(dispatchedResponse)
19574
+ ) {
19555
19575
  provider429RetryDiagnosticStartedAt =
19556
19576
  performance.now();
19557
19577
  provider429RetryDiagnosticNextInvocationAttempt =
@@ -931,6 +931,14 @@ export interface ContextOptions {
931
931
  */
932
932
  supersededKeys?: string[];
933
933
  }>;
934
+ /** Distinct terminal rows after the map's persistence barrier, across resumes. */
935
+ onMapRowsSettled?: (input: {
936
+ playName?: string;
937
+ runId?: string;
938
+ tableNamespace: string;
939
+ succeededRows: number;
940
+ failedRows: number;
941
+ }) => void;
934
942
  /**
935
943
  * Persists coalesced, non-terminal row patches before a map suspends.
936
944
  * Terminal rows use onMapRowsCompleted; this narrow seam only preserves
@@ -328,6 +328,7 @@ export function buildPlayRunStatusSnapshot(input: {
328
328
  status: projectedSnapshot.status,
329
329
  updatedAt: projectedSnapshot.updatedAt,
330
330
  waitKind: input.run.waitKind ?? input.run.wait?.kind ?? null,
331
+ waitBoundaryId: input.run.wait?.boundaryId ?? input.run.activeBoundaryId,
331
332
  waitUntil: input.run.waitUntil,
332
333
  eventKey: input.run.wait?.eventKey ?? null,
333
334
  runtimeBackend: input.run.runtimeBackend,
@@ -496,6 +496,20 @@ export function runtimeSheetTerminalWriteCounts(input: {
496
496
  };
497
497
  }
498
498
 
499
+ /** Distinct settled counts include saved siblings, never skipped input duplicates. */
500
+ export function runtimeSheetTerminalMapCounts(
501
+ current: { persistedRows: number; succeededRows: number; failedRows: number },
502
+ map: { succeededRows: number; failedRows: number },
503
+ ): { persistedRows: number; succeededRows: number; failedRows: number } {
504
+ const succeededRows = Math.max(current.succeededRows, map.succeededRows);
505
+ const failedRows = Math.max(current.failedRows, map.failedRows);
506
+ return {
507
+ persistedRows: Math.max(current.persistedRows, succeededRows + failedRows),
508
+ succeededRows,
509
+ failedRows,
510
+ };
511
+ }
512
+
499
513
  async function vercelBypassHeaders(
500
514
  config: PlayRunnerExecutionConfig,
501
515
  ): Promise<Record<string, string>> {
@@ -2308,6 +2322,35 @@ async function run(
2308
2322
  const MAP_ROW_UPDATED_EMIT_INTERVAL_MS = 200;
2309
2323
  const MAP_ROW_UPDATED_EMIT_SAMPLE_RATE = 100;
2310
2324
  return (event: PlayExecutionEvent) => {
2325
+ if (event.type === 'map.suspended') {
2326
+ const datasetId = createRuntimeDatasetId(
2327
+ config.context.playName!,
2328
+ event.artifactTableNamespace,
2329
+ );
2330
+ const settlement = runtimeMapDatasetSettlements.get(datasetId);
2331
+ if (settlement) {
2332
+ // This event follows the map writer's durability barrier. Only
2333
+ // report accepted writes, not progress (which includes duplicates).
2334
+ emitRunnerEvent(
2335
+ {
2336
+ type: 'execution_event',
2337
+ event: {
2338
+ type: 'dataset.lifecycle',
2339
+ datasetId,
2340
+ path: settlement.path,
2341
+ tableNamespace: settlement.tableNamespace,
2342
+ phase: 'registered',
2343
+ persistedRows: settlement.persistedRows,
2344
+ succeededRows: settlement.succeededRows,
2345
+ failedRows: settlement.failedRows,
2346
+ complete: false,
2347
+ at: event.at,
2348
+ },
2349
+ },
2350
+ options,
2351
+ );
2352
+ }
2353
+ }
2311
2354
  if (event.type === 'map.row.updated') {
2312
2355
  mapRowUpdatedCount++;
2313
2356
  const now = Date.now();
@@ -2638,6 +2681,19 @@ async function run(
2638
2681
  ),
2639
2682
  });
2640
2683
  },
2684
+ onMapRowsSettled: (input) => {
2685
+ const datasetId = createRuntimeDatasetId(
2686
+ input.playName ?? config.context.playName!,
2687
+ input.tableNamespace,
2688
+ );
2689
+ const settlement = runtimeMapDatasetSettlements.get(datasetId);
2690
+ if (settlement && (!input.runId || settlement.runId === input.runId)) {
2691
+ Object.assign(
2692
+ settlement,
2693
+ runtimeSheetTerminalMapCounts(settlement, input),
2694
+ );
2695
+ }
2696
+ },
2641
2697
  onMapRowsCompleted: async (input) => {
2642
2698
  // Persist executed rows (data + cell meta) into the tenant sheet —
2643
2699
  // the sheet is the source of truth for dataset state, and the
@@ -86,6 +86,23 @@ export const TOOL_EXECUTE_PROVIDER_THROTTLE_HEADER =
86
86
  'x-deepline-provider-throttle';
87
87
  export const TOOL_EXECUTE_PROVIDER_PACING_WAIT_CODE = 'PROVIDER_PACING_WAIT';
88
88
 
89
+ /** A trusted provider 429 remains observable if the gateway cannot commit its body. */
90
+ export function isAttestedProvider429Response(response: {
91
+ status: number;
92
+ headers: Pick<Headers, 'get'>;
93
+ }): boolean {
94
+ if (response.headers.get(TOOL_EXECUTE_PROVIDER_THROTTLE_HEADER) !== '1') {
95
+ return false;
96
+ }
97
+ if (response.status === 429) return true;
98
+ const code = response.headers.get('x-deepline-error-code');
99
+ return (
100
+ response.status === 409 &&
101
+ (code === 'durable_invocation_outcome_unknown' ||
102
+ code === 'durable_invocation_commit_failed')
103
+ );
104
+ }
105
+
89
106
  /**
90
107
  * Parse the advertisement as a comma-separated token list so a later runner can
91
108
  * add a capability without a second header. An absent or unparsable value means
package/dist/cli/index.js CHANGED
@@ -3454,7 +3454,7 @@ var SDK_RELEASE = {
3454
3454
  // getters keep their established compatibility behavior.
3455
3455
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3456
3456
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3457
- version: "0.3.238",
3457
+ version: "0.3.239",
3458
3458
  updateSummary: "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.",
3459
3459
  packageCapabilities: {
3460
3460
  updatePreferences: 1
@@ -3449,7 +3449,7 @@ var SDK_RELEASE = {
3449
3449
  // getters keep their established compatibility behavior.
3450
3450
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3451
3451
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3452
- version: "0.3.238",
3452
+ version: "0.3.239",
3453
3453
  updateSummary: "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.",
3454
3454
  packageCapabilities: {
3455
3455
  updatePreferences: 1
package/dist/index.js CHANGED
@@ -1267,7 +1267,7 @@ var SDK_RELEASE = {
1267
1267
  // getters keep their established compatibility behavior.
1268
1268
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1269
1269
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
1270
- version: "0.3.238",
1270
+ version: "0.3.239",
1271
1271
  updateSummary: "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.",
1272
1272
  packageCapabilities: {
1273
1273
  updatePreferences: 1
package/dist/index.mjs CHANGED
@@ -1171,7 +1171,7 @@ var SDK_RELEASE = {
1171
1171
  // getters keep their established compatibility behavior.
1172
1172
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1173
1173
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
1174
- version: "0.3.238",
1174
+ version: "0.3.239",
1175
1175
  updateSummary: "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.",
1176
1176
  packageCapabilities: {
1177
1177
  updatePreferences: 1
@@ -149,7 +149,7 @@ type SdkRelease = {
149
149
  supportPolicy: SdkSupportPolicy;
150
150
  };
151
151
  declare const SDK_RELEASE: {
152
- readonly version: "0.3.238";
152
+ readonly version: "0.3.239";
153
153
  readonly updateSummary: "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.";
154
154
  readonly packageCapabilities: {
155
155
  readonly updatePreferences: 1;
package/dist/release.d.ts CHANGED
@@ -149,7 +149,7 @@ type SdkRelease = {
149
149
  supportPolicy: SdkSupportPolicy;
150
150
  };
151
151
  declare const SDK_RELEASE: {
152
- readonly version: "0.3.238";
152
+ readonly version: "0.3.239";
153
153
  readonly updateSummary: "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.";
154
154
  readonly packageCapabilities: {
155
155
  readonly updatePreferences: 1;
package/dist/release.js CHANGED
@@ -74,7 +74,7 @@ var SDK_RELEASE = {
74
74
  // getters keep their established compatibility behavior.
75
75
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
76
76
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
77
- version: "0.3.238",
77
+ version: "0.3.239",
78
78
  updateSummary: "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.",
79
79
  packageCapabilities: {
80
80
  updatePreferences: 1
package/dist/release.mjs CHANGED
@@ -48,7 +48,7 @@ var SDK_RELEASE = {
48
48
  // getters keep their established compatibility behavior.
49
49
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
50
50
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
51
- version: "0.3.238",
51
+ version: "0.3.239",
52
52
  updateSummary: "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.",
53
53
  packageCapabilities: {
54
54
  updatePreferences: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.3.238",
3
+ "version": "0.3.239",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",