deepline 0.3.82 → 0.3.83

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.
@@ -199,7 +199,7 @@ export const SDK_RELEASE = {
199
199
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
- version: '0.3.82',
202
+ version: '0.3.83',
203
203
  updateSummary:
204
204
  'Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.',
205
205
  packageCapabilities: {
@@ -18,6 +18,7 @@ import {
18
18
  reducePlayRunLedgerEvents,
19
19
  slicePositionalLogLines,
20
20
  type PlayRunLedgerEvent,
21
+ type PlayRunLedgerIngressEvent,
21
22
  type PlayRunLedgerSnapshot,
22
23
  } from './/run-ledger';
23
24
  import type {
@@ -149,7 +150,7 @@ type RuntimeApiRequest =
149
150
  | {
150
151
  action: 'append_run_events';
151
152
  playId: string;
152
- events: PlayRunLedgerEvent[];
153
+ events: PlayRunLedgerIngressEvent[];
153
154
  idempotencyKey?: string;
154
155
  }
155
156
  | {
@@ -380,18 +381,42 @@ async function suppressBulkClaimResponseUntilTimeout(
380
381
  });
381
382
  }
382
383
 
384
+ function isRecord(value: unknown): value is Record<string, unknown> {
385
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
386
+ }
387
+
383
388
  function splitRunEventForAppRuntime(
384
- event: PlayRunLedgerEvent,
385
- ): PlayRunLedgerEvent[] {
386
- if (event.type !== 'log.appended' || event.lines.length === 0) return [event];
387
- const split: PlayRunLedgerEvent[] = [];
389
+ event: PlayRunLedgerIngressEvent,
390
+ ): PlayRunLedgerIngressEvent[] {
391
+ const eventType =
392
+ typeof event.key === 'string'
393
+ ? event.key
394
+ : typeof event.type === 'string'
395
+ ? event.type
396
+ : null;
397
+ const payload = isRecord(event.payload) ? event.payload : null;
398
+ const payloadLines = payload?.lines;
399
+ const payloadOwnsLines = Array.isArray(payloadLines);
400
+ const rawLines = payloadOwnsLines ? payloadLines : event.lines;
401
+ const lines = Array.isArray(rawLines)
402
+ ? rawLines.filter((line): line is string => typeof line === 'string')
403
+ : [];
404
+ if (eventType !== 'log.appended' || lines.length === 0) return [event];
405
+ const payloadChannelOffset =
406
+ typeof payload?.channelOffset === 'number'
407
+ ? payload.channelOffset
408
+ : undefined;
409
+ const eventChannelOffset =
410
+ typeof event.channelOffset === 'number' ? event.channelOffset : undefined;
411
+ const split: PlayRunLedgerIngressEvent[] = [];
412
+ const { lines: _outerLines, ...eventWithoutOuterLines } = event;
388
413
  let index = 0;
389
- while (index < event.lines.length) {
414
+ while (index < lines.length) {
390
415
  const start = index;
391
416
  let bytes = 2;
392
- while (index < event.lines.length) {
417
+ while (index < lines.length) {
393
418
  const lineBytes = appRuntimeTextEncoder.encode(
394
- JSON.stringify(event.lines[index]!),
419
+ JSON.stringify(lines[index]!),
395
420
  ).length;
396
421
  if (
397
422
  index > start &&
@@ -402,22 +427,40 @@ function splitRunEventForAppRuntime(
402
427
  bytes += lineBytes + 1;
403
428
  index += 1;
404
429
  }
405
- split.push({
406
- ...event,
407
- lines: event.lines.slice(start, index),
408
- ...(typeof event.channelOffset === 'number'
409
- ? { channelOffset: event.channelOffset + start }
410
- : {}),
411
- });
430
+ const batchLines = lines.slice(start, index);
431
+ split.push(
432
+ payload
433
+ ? {
434
+ ...eventWithoutOuterLines,
435
+ ...(payloadOwnsLines ? {} : { lines: batchLines }),
436
+ payload: {
437
+ ...payload,
438
+ ...(payloadOwnsLines ? { lines: batchLines } : {}),
439
+ ...(payloadChannelOffset === undefined
440
+ ? {}
441
+ : { channelOffset: payloadChannelOffset + start }),
442
+ },
443
+ ...(eventChannelOffset === undefined
444
+ ? {}
445
+ : { channelOffset: eventChannelOffset + start }),
446
+ }
447
+ : {
448
+ ...event,
449
+ lines: batchLines,
450
+ ...(eventChannelOffset === undefined
451
+ ? {}
452
+ : { channelOffset: eventChannelOffset + start }),
453
+ },
454
+ );
412
455
  }
413
456
  return split;
414
457
  }
415
458
 
416
459
  export function partitionRunEventsForAppRuntime(
417
- events: readonly PlayRunLedgerEvent[],
418
- ): PlayRunLedgerEvent[][] {
419
- const batches: PlayRunLedgerEvent[][] = [];
420
- let current: PlayRunLedgerEvent[] = [];
460
+ events: readonly PlayRunLedgerIngressEvent[],
461
+ ): PlayRunLedgerIngressEvent[][] {
462
+ const batches: PlayRunLedgerIngressEvent[][] = [];
463
+ let current: PlayRunLedgerIngressEvent[] = [];
421
464
  let currentBytes = 2;
422
465
  for (const event of events.flatMap(splitRunEventForAppRuntime)) {
423
466
  const eventBytes = appRuntimeTextEncoder.encode(
@@ -742,9 +785,15 @@ function appRuntimeRetryAfterMs(
742
785
  }
743
786
  } catch {}
744
787
 
745
- const retryAfterSeconds = Number(response.headers.get('retry-after'));
746
- return Number.isFinite(retryAfterSeconds)
747
- ? Math.max(0, Math.min(retryAfterSeconds * 1_000, 30_000))
788
+ const retryAfter = response.headers.get('retry-after')?.trim();
789
+ if (!retryAfter) return null;
790
+ const retryAfterSeconds = Number(retryAfter);
791
+ if (Number.isFinite(retryAfterSeconds)) {
792
+ return Math.max(0, Math.min(retryAfterSeconds * 1_000, 30_000));
793
+ }
794
+ const retryAt = Date.parse(retryAfter);
795
+ return Number.isFinite(retryAt)
796
+ ? Math.max(0, Math.min(retryAt - Date.now(), 30_000))
748
797
  : null;
749
798
  }
750
799
 
@@ -997,6 +1046,8 @@ export class AppRuntimeApiResponseError extends Error {
997
1046
  readonly code: string | null;
998
1047
  readonly requestId: string | null;
999
1048
  readonly retryable: boolean;
1049
+ /** Server-requested delay, retained for durable callers after local retries end. */
1050
+ readonly retryAfterMs: number | null;
1000
1051
  readonly detail: string;
1001
1052
 
1002
1053
  constructor(input: {
@@ -1005,6 +1056,7 @@ export class AppRuntimeApiResponseError extends Error {
1005
1056
  code?: string | null;
1006
1057
  requestId?: string | null;
1007
1058
  retryable: boolean;
1059
+ retryAfterMs?: number | null;
1008
1060
  detail: string;
1009
1061
  boundaryLabel?: string;
1010
1062
  }) {
@@ -1020,6 +1072,10 @@ export class AppRuntimeApiResponseError extends Error {
1020
1072
  this.code = input.code?.trim() || null;
1021
1073
  this.requestId = input.requestId?.trim() || null;
1022
1074
  this.retryable = input.retryable;
1075
+ this.retryAfterMs =
1076
+ typeof input.retryAfterMs === 'number' && Number.isFinite(input.retryAfterMs)
1077
+ ? Math.max(0, Math.floor(input.retryAfterMs))
1078
+ : null;
1023
1079
  this.detail = input.detail;
1024
1080
  }
1025
1081
  }
@@ -1038,7 +1094,7 @@ const APP_RUNTIME_CAPACITY_ERROR_CODES = new Set([
1038
1094
  * than spending the ordinary request retry ladder.
1039
1095
  */
1040
1096
  export class AppRuntimeApiCapacityError extends AppRuntimeApiResponseError {
1041
- readonly retryAfterMs: number;
1097
+ declare readonly retryAfterMs: number;
1042
1098
 
1043
1099
  constructor(input: {
1044
1100
  action: RuntimeApiRequest['action'];
@@ -1374,6 +1430,7 @@ async function postAppRuntimeApi<TResponse>(
1374
1430
  body: responseText,
1375
1431
  contentType: response.headers.get('content-type'),
1376
1432
  }),
1433
+ retryAfterMs: appRuntimeRetryAfterMs(response, responseText),
1377
1434
  detail: summarizeAppRuntimeErrorBody(responseText),
1378
1435
  boundaryLabel,
1379
1436
  });
@@ -1870,7 +1927,7 @@ async function appendRunEventsViaAppRuntimeRaw(
1870
1927
  context: WorkerRuntimeApiContext,
1871
1928
  input: {
1872
1929
  playId: string;
1873
- events: PlayRunLedgerEvent[];
1930
+ events: PlayRunLedgerIngressEvent[];
1874
1931
  idempotencyKey?: string;
1875
1932
  },
1876
1933
  ): Promise<void> {
@@ -1890,7 +1947,7 @@ export async function appendRunEventsViaAppRuntime(
1890
1947
  context: WorkerRuntimeApiContext,
1891
1948
  input: {
1892
1949
  playId: string;
1893
- events: PlayRunLedgerEvent[];
1950
+ events: PlayRunLedgerIngressEvent[];
1894
1951
  idempotencyKey?: string;
1895
1952
  },
1896
1953
  ): Promise<void> {
@@ -1907,10 +1964,14 @@ export async function appendRunEventsViaAppRuntime(
1907
1964
  for (let index = batches.length - 1; index >= 0; index -= 1) {
1908
1965
  if (
1909
1966
  batches[index]!.some(
1910
- (event) =>
1911
- event.type === 'run.completed' ||
1912
- event.type === 'run.failed' ||
1913
- event.type === 'run.cancelled',
1967
+ (event) => {
1968
+ const key = event.key ?? event.type;
1969
+ return (
1970
+ key === 'run.completed' ||
1971
+ key === 'run.failed' ||
1972
+ key === 'run.cancelled'
1973
+ );
1974
+ },
1914
1975
  )
1915
1976
  ) {
1916
1977
  terminalBatchIndex = index;
@@ -1,6 +1,7 @@
1
1
  import { createSecretRedactionContext } from './secret-redaction';
2
2
  import {
3
3
  assertRuntimeReceiptOutputWithinLimit,
4
+ inspectConvexLedgerPayload,
4
5
  jsonByteLengthUpTo,
5
6
  LEDGER_TERMINAL_RESULT_MAX_BYTES,
6
7
  } from './output-size-limits';
@@ -31,6 +32,31 @@ export function sanitizeLedgerPayload(value: unknown): unknown {
31
32
  return ledgerIngressRedactor.redact(value);
32
33
  }
33
34
 
35
+ function opaqueLedgerPayloadSummary(
36
+ issue: NonNullable<ReturnType<typeof inspectConvexLedgerPayload>>,
37
+ ): Record<string, unknown> {
38
+ return {
39
+ __kind: 'deepline.opaque_ledger_payload_summary.v1',
40
+ reason: issue.reason,
41
+ ...(typeof issue.bytes === 'number' ? { bytes: issue.bytes } : {}),
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Preserve unfamiliar event payloads when they fit the Convex event contract.
47
+ * If they do not, keep a safe diagnostic summary instead of allowing one newer
48
+ * producer to permanently wedge its ordered ledger outbox behind a document
49
+ * limit, depth, or field-name rejection.
50
+ */
51
+ export function sanitizeOpaqueLedgerPayload(value: unknown): unknown {
52
+ const sourceIssue = inspectConvexLedgerPayload(value);
53
+ if (sourceIssue) return opaqueLedgerPayloadSummary(sourceIssue);
54
+
55
+ const redacted = ledgerIngressRedactor.redact(value);
56
+ const redactedIssue = inspectConvexLedgerPayload(redacted);
57
+ return redactedIssue ? opaqueLedgerPayloadSummary(redactedIssue) : redacted;
58
+ }
59
+
34
60
  /** Secret-redact a receipt payload without coupling its 10 MiB cell limit to the ledger. */
35
61
  export function sanitizeRuntimeReceiptPayload(value: unknown): unknown {
36
62
  // Reject an obviously oversized source before doing redaction work, then
@@ -173,6 +173,12 @@ type PlayRunLedgerBaseEvent = {
173
173
  seq?: number;
174
174
  occurredAt: number;
175
175
  source: PlayRunLedgerEventSource;
176
+ /**
177
+ * Producer-owned detail. The ledger preserves this without assigning it
178
+ * snapshot meaning; a known event's projection reads only the fields its
179
+ * reducer understands.
180
+ */
181
+ payload?: unknown;
176
182
  };
177
183
 
178
184
  export type PlayRunLedgerEvent =
@@ -289,6 +295,55 @@ export type PlayRunLedgerEvent =
289
295
  observation: PlayActivityObservation;
290
296
  });
291
297
 
298
+ /**
299
+ * Wire shape accepted by the Run Ledger ingress.
300
+ *
301
+ * Producers can evolve their event vocabulary independently from the current
302
+ * snapshot reducer. The only producer-owned identity is `key` (or the legacy
303
+ * `type` spelling); `payload` and all additional fields are open. The receiver
304
+ * authenticates the delivery and assigns trusted run/time/source fields before
305
+ * an unfamiliar key becomes {@link PlayRunLedgerOpaqueEvent} in storage.
306
+ * Keep the known {@link PlayRunLedgerEvent} union closed so reducer authors
307
+ * retain exhaustive type checking for the semantics they actually own.
308
+ */
309
+ export type PlayRunLedgerIngressEvent = {
310
+ /** Preferred generic event identity. */
311
+ key?: string;
312
+ /** Backward-compatible event identity used by existing runners. */
313
+ type?: string;
314
+ /** Optional redundant target; a supplied value must match the append target. */
315
+ runId?: string;
316
+ /** Optional producer timestamp; the receiver supplies one when omitted. */
317
+ occurredAt?: number;
318
+ /** Advisory producer label; unknown labels normalize to `system`. */
319
+ source?: PlayRunLedgerEventSource | string;
320
+ /** Arbitrary producer-owned detail. */
321
+ payload?: unknown;
322
+ [field: string]: unknown;
323
+ };
324
+
325
+ /**
326
+ * A well-formed event whose type is not understood by this ledger version.
327
+ * The original type and its redacted, bounded payload are durable history;
328
+ * projection deliberately treats this as a strict no-op.
329
+ */
330
+ export type PlayRunLedgerOpaqueEvent = PlayRunLedgerBaseEvent & {
331
+ type: string;
332
+ opaque: true;
333
+ payload: unknown;
334
+ };
335
+
336
+ /** A stored event accepted by this ledger version. */
337
+ export type AcceptedPlayRunLedgerEvent =
338
+ | PlayRunLedgerEvent
339
+ | PlayRunLedgerOpaqueEvent;
340
+
341
+ export function isPlayRunLedgerOpaqueEvent(
342
+ event: AcceptedPlayRunLedgerEvent,
343
+ ): event is PlayRunLedgerOpaqueEvent {
344
+ return 'opaque' in event && event.opaque === true;
345
+ }
346
+
292
347
  export type PlayRunLedgerStatusPatch = {
293
348
  runId: string;
294
349
  playName?: string | null;
@@ -1102,8 +1157,14 @@ function refineTerminalSyntheticStepStart(
1102
1157
 
1103
1158
  export function reducePlayRunLedgerEvent(
1104
1159
  snapshot: PlayRunLedgerSnapshot,
1105
- event: PlayRunLedgerEvent,
1160
+ event: AcceptedPlayRunLedgerEvent,
1106
1161
  ): PlayRunLedgerSnapshot {
1162
+ // Storage compatibility is intentionally wider than reducer semantics. An
1163
+ // unknown event is durable evidence for a newer producer, not permission to
1164
+ // guess at run state or to advance any snapshot timestamp.
1165
+ if (isPlayRunLedgerOpaqueEvent(event)) {
1166
+ return snapshot;
1167
+ }
1107
1168
  if (event.runId !== snapshot.runId) {
1108
1169
  return snapshot;
1109
1170
  }
@@ -1525,12 +1586,18 @@ export function reducePlayRunLedgerEvent(
1525
1586
  : event.observation.activityId,
1526
1587
  });
1527
1588
  }
1589
+ default:
1590
+ // `appendPlayRunEventsInternal` has historical callers that predate the
1591
+ // opaque ingress envelope. Keep the reducer fail-closed even if one
1592
+ // reaches storage with a newer raw type: it is durable history, never a
1593
+ // reason to manufacture snapshot state or return `undefined`.
1594
+ return snapshot;
1528
1595
  }
1529
1596
  }
1530
1597
 
1531
1598
  export function reducePlayRunLedgerEvents(
1532
1599
  snapshot: PlayRunLedgerSnapshot,
1533
- events: readonly PlayRunLedgerEvent[],
1600
+ events: readonly AcceptedPlayRunLedgerEvent[],
1534
1601
  ): PlayRunLedgerSnapshot {
1535
1602
  return events.reduce(reducePlayRunLedgerEvent, snapshot);
1536
1603
  }
package/dist/cli/index.js CHANGED
@@ -1225,7 +1225,7 @@ var SDK_RELEASE = {
1225
1225
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1226
1226
  // getters keep their established compatibility behavior.
1227
1227
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1228
- version: "0.3.82",
1228
+ version: "0.3.83",
1229
1229
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
1230
1230
  packageCapabilities: {
1231
1231
  updatePreferences: 1
@@ -1210,7 +1210,7 @@ var SDK_RELEASE = {
1210
1210
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1211
1211
  // getters keep their established compatibility behavior.
1212
1212
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1213
- version: "0.3.82",
1213
+ version: "0.3.83",
1214
1214
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
1215
1215
  packageCapabilities: {
1216
1216
  updatePreferences: 1
package/dist/index.js CHANGED
@@ -832,7 +832,7 @@ var SDK_RELEASE = {
832
832
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
833
833
  // getters keep their established compatibility behavior.
834
834
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
835
- version: "0.3.82",
835
+ version: "0.3.83",
836
836
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
837
837
  packageCapabilities: {
838
838
  updatePreferences: 1
package/dist/index.mjs CHANGED
@@ -744,7 +744,7 @@ var SDK_RELEASE = {
744
744
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
745
745
  // getters keep their established compatibility behavior.
746
746
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
747
- version: "0.3.82",
747
+ version: "0.3.83",
748
748
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
749
749
  packageCapabilities: {
750
750
  updatePreferences: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.3.82",
3
+ "version": "0.3.83",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",