deepline 0.3.282 → 0.3.284

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.282',
203
+ version: '0.3.284',
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: {
@@ -73,8 +73,10 @@ import { collectStartupEgressProbeLines } from './egress-probe';
73
73
  import {
74
74
  pushRunnerTerminal,
75
75
  runnerTerminalResultForTransport,
76
+ runnerTerminalHandoffForTransport,
76
77
  type PushRunnerTerminalOptions,
77
78
  type PlayRunnerTerminalTransportResult,
79
+ type PlayRunnerTerminalTransportEvent,
78
80
  } from './runtime-terminal-push';
79
81
  import { vercelProtectionBypassHeader } from '../vercel-protection';
80
82
  import { getPlayRuntimeBatchStrategy } from '../play-runtime-batching-registry';
@@ -1407,9 +1409,7 @@ function emitProgressSidecarEvent(event: PlayRunnerEvent): void {
1407
1409
  }
1408
1410
 
1409
1411
  async function writeRunnerEventToStdout(
1410
- event:
1411
- | PlayRunnerEvent
1412
- | { type: 'result'; result: PlayRunnerTerminalTransportResult },
1412
+ event: PlayRunnerEvent | PlayRunnerTerminalTransportEvent,
1413
1413
  ): Promise<void> {
1414
1414
  await new Promise<void>((resolve, reject) => {
1415
1415
  process.stdout.write(`${JSON.stringify(event)}\n`, (error) => {
@@ -1482,9 +1482,7 @@ async function exitRunner(exitCode: number): Promise<never> {
1482
1482
  }
1483
1483
 
1484
1484
  async function emitAndExit(
1485
- event:
1486
- | PlayRunnerEvent
1487
- | { type: 'result'; result: PlayRunnerTerminalTransportResult },
1485
+ event: PlayRunnerEvent | PlayRunnerTerminalTransportEvent,
1488
1486
  exitCode: number,
1489
1487
  ): Promise<never> {
1490
1488
  await writeRunnerEventToStdout(event);
@@ -3859,37 +3857,29 @@ async function main() {
3859
3857
  // delays the terminal beyond the cap; no-op for undiagrammed plays.
3860
3858
  // Runner progress joins the same terminal handoff immediately below.
3861
3859
  await drainPendingDocflowObservationPosts();
3862
- if (terminalResult) {
3860
+ const terminalHandoff = terminalResult
3861
+ ? runnerTerminalHandoffForTransport(terminalResult, runnerProgressPusher)
3862
+ : null;
3863
+ if (terminalHandoff) runnerProgressPusher = null;
3864
+ if (terminalHandoff) {
3863
3865
  // Persist the exact terminal line locally before any network settlement.
3864
3866
  // If the gateway remains unavailable through the grace period, the shell
3865
3867
  // epilogue can still salvage this authored result instead of inventing a
3866
3868
  // runtime-limit failure after customer code already finished.
3867
- await writeRunnerEventToStdout({
3868
- type: 'result',
3869
- result: terminalResult,
3870
- });
3869
+ await writeRunnerEventToStdout(terminalHandoff.event);
3871
3870
  }
3872
3871
  // Push execution (B2-final): the pushed terminal is what WAKES the parked
3873
3872
  // worker attempt. Push before exiting; on exhaustion the captured output
3874
3873
  // file remains the ceiling-timeout salvage source.
3875
- if (runnerTerminalPushContext && terminalResult) {
3874
+ if (runnerTerminalPushContext && terminalHandoff) {
3876
3875
  // Every terminal boundary can wake a successor attempt. Transfer all
3877
3876
  // unacknowledged observations into that same ordered handoff before the
3878
3877
  // wake is visible; draining a suspended/failed attempt afterward lets
3879
3878
  // the successor claim the log channel and fence its predecessor's tail.
3880
- const finalProgress = runnerProgressPusher?.sealForTerminal() ?? {
3881
- events: [],
3882
- liveLogOffset: 0,
3883
- };
3884
- runnerProgressPusher = null;
3885
- await pushRunnerTerminalIfConfigured(terminalResult, {
3879
+ await pushRunnerTerminalIfConfigured(terminalHandoff.event.result, {
3886
3880
  warn: createPostTerminalDiagnosticWriter(),
3887
- finalProgressLogOffset: finalProgress.liveLogOffset,
3888
- ...(finalProgress.events.length
3889
- ? {
3890
- finalProgressEvents: finalProgress.events,
3891
- }
3892
- : {}),
3881
+ finalProgressLogOffset: terminalHandoff.progress?.liveLogOffset ?? 0,
3882
+ finalProgressEvents: terminalHandoff.progress?.events,
3893
3883
  });
3894
3884
  return exitRunner(0);
3895
3885
  }
@@ -4174,13 +4164,12 @@ async function terminateRunnerForRuntimeDeadline(
4174
4164
  // the Daytona shell wrapper, whose crash epilogue owns the bounded gateway
4175
4165
  // push during the outer grace period. Awaiting network/progress work here
4176
4166
  // would let customer code continue dispatching after its deadline.
4177
- return emitAndExit(
4178
- {
4179
- type: 'result',
4180
- result: failedResult,
4181
- },
4182
- 0,
4167
+ const terminalHandoff = runnerTerminalHandoffForTransport(
4168
+ failedResult,
4169
+ runnerProgressPusher,
4183
4170
  );
4171
+ runnerProgressPusher = null;
4172
+ return emitAndExit(terminalHandoff.event, 0);
4184
4173
  }
4185
4174
 
4186
4175
  // The external watchdog samples the sandbox cgroup and raises SIGUSR2 at its
@@ -18,6 +18,7 @@ import {
18
18
  type RunnerGatewayTransportEvent,
19
19
  } from './runtime-gateway-transport';
20
20
  import { RUNTIME_RELIABILITY_POLICY } from '../runtime-reliability-policy';
21
+ import type { RunnerProgressPusher } from './runtime-progress-push';
21
22
 
22
23
  /**
23
24
  * Push-execution terminal push (runs INSIDE the Daytona sandbox, B2-final).
@@ -93,6 +94,39 @@ export type PlayRunnerTerminalTransportResult =
93
94
  | (Omit<FailedRunnerResult, 'checkpoint'> &
94
95
  (OmittedTerminalCheckpoint | AbsentCheckpoint));
95
96
 
97
+ export type PlayRunnerTerminalTransportEvent = {
98
+ type: 'result';
99
+ result: PlayRunnerTerminalTransportResult;
100
+ finalProgressEvents?: PlayRunnerEvent[];
101
+ finalProgressLogOffset?: number;
102
+ };
103
+
104
+ export function runnerTerminalHandoffForTransport(
105
+ result: PlayRunnerResult | PlayRunnerTerminalTransportResult,
106
+ progressPusher?: RunnerProgressPusher | null,
107
+ ): {
108
+ event: PlayRunnerTerminalTransportEvent;
109
+ progress?: ReturnType<RunnerProgressPusher['sealForTerminal']>;
110
+ } {
111
+ const finalProgress = progressPusher?.sealForTerminal();
112
+ const terminalResult = runnerTerminalResultForTransport(result);
113
+ return {
114
+ event: {
115
+ type: 'result',
116
+ result: terminalResult,
117
+ ...(finalProgress && terminalResult.status !== 'suspended'
118
+ ? {
119
+ finalProgressLogOffset: finalProgress.liveLogOffset,
120
+ ...(finalProgress.events.length
121
+ ? { finalProgressEvents: finalProgress.events }
122
+ : {}),
123
+ }
124
+ : {}),
125
+ },
126
+ progress: finalProgress,
127
+ };
128
+ }
129
+
96
130
  type SettledRunnerTerminalTransportResult = Exclude<
97
131
  PlayRunnerTerminalTransportResult,
98
132
  SuspendedRunnerResult & IncludedResumeCheckpoint
@@ -205,7 +205,7 @@ function writeDiagnostic(line) {
205
205
  postTerminalDiagnosticBytesRemaining -= Buffer.byteLength(bounded, 'utf8') + 1;
206
206
  }
207
207
 
208
- function readResultFromOutput() {
208
+ function readResultEventFromOutput() {
209
209
  try {
210
210
  const stat = fs.statSync(outputPath);
211
211
  const start = Math.max(0, stat.size - MAX_OUTPUT_BYTES);
@@ -225,7 +225,7 @@ function readResultFromOutput() {
225
225
  event.result &&
226
226
  ['completed', 'failed', 'suspended'].includes(event.result.status)
227
227
  ) {
228
- return event.result;
228
+ return event;
229
229
  }
230
230
  } catch {}
231
231
  }
@@ -396,7 +396,8 @@ async function main() {
396
396
  writeDiagnostic('[crash-terminal] no push config; skipping');
397
397
  return;
398
398
  }
399
- const parsedResult = readResultFromOutput();
399
+ const parsedResultEvent = readResultEventFromOutput();
400
+ const parsedResult = parsedResultEvent && parsedResultEvent.result;
400
401
  // Exit codes are authored-program output too: a play may intentionally call
401
402
  // process.exit(124). Only the separate parent watchdog writes this marker.
402
403
  const runtimeLimitExceeded = Boolean(
@@ -492,6 +493,12 @@ async function main() {
492
493
  action: 'runner_terminal',
493
494
  runId: push.runId,
494
495
  result,
496
+ ...(parsedResultEvent && parsedResultEvent.finalProgressEvents !== undefined
497
+ ? { finalProgressEvents: parsedResultEvent.finalProgressEvents }
498
+ : {}),
499
+ ...(parsedResultEvent && parsedResultEvent.finalProgressLogOffset !== undefined
500
+ ? { finalProgressLogOffset: parsedResultEvent.finalProgressLogOffset }
501
+ : {}),
495
502
  });
496
503
  for (let attempt = 1; attempt <= 3; attempt += 1) {
497
504
  const controller = new AbortController();
@@ -278,6 +278,7 @@ import {
278
278
  import {
279
279
  abandonRuntimeSandboxOperationReservation,
280
280
  bindRuntimeSandboxCapacityLease,
281
+ createRuntimeSandboxOperationReservation,
281
282
  readReservedDaytonaCancellationOperation,
282
283
  recordRecoveredDaytonaCancellationSandbox,
283
284
  reserveRuntimeSandboxOperation,
@@ -3889,14 +3890,22 @@ async function executeAssignedAbsurdAttempt(input: {
3889
3890
  resumePendingDaytonaOperation: provider === 'daytona',
3890
3891
  operationKey: `sandbox-operation:${runId}:${generation}:${provider}:${providerAttempt ?? 1}`,
3891
3892
  };
3892
- const reserve = () =>
3893
- deps.runWithClient((client) =>
3894
- reserveRuntimeSandboxOperation(
3895
- client,
3896
- reservationInput,
3897
- options,
3898
- ),
3899
- );
3893
+ // A pooled callback may retry after its INSERT committed.
3894
+ // Keep unsent-create authority outside those callback retries;
3895
+ // a later admission invocation must reconcile the prior key.
3896
+ const reserveOperation =
3897
+ provider === 'daytona'
3898
+ ? createRuntimeSandboxOperationReservation(
3899
+ reservationInput,
3900
+ options,
3901
+ )
3902
+ : (client: PostgresSchedulerQueryClient) =>
3903
+ reserveRuntimeSandboxOperation(
3904
+ client,
3905
+ reservationInput,
3906
+ options,
3907
+ );
3908
+ const reserve = () => deps.runWithClient(reserveOperation);
3900
3909
  const deferForSchedulerFailure = (error: unknown): never => {
3901
3910
  console.warn(
3902
3911
  '[absurd-runtime.worker] sandbox_capacity_admission_deferred',
@@ -7,6 +7,7 @@ import { mkdir, rm, writeFile } from 'node:fs/promises';
7
7
  import { createHash, randomUUID } from 'node:crypto';
8
8
  import { dirname, isAbsolute, join, relative, sep } from 'node:path';
9
9
  import { tmpdir } from 'node:os';
10
+ import { decodeJwt } from 'jose';
10
11
  import {
11
12
  acquirePgNotifyHub,
12
13
  resolveWorkflowRuntimePostgresUrl,
@@ -18796,6 +18797,35 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
18796
18797
  maxEvents?: number;
18797
18798
  },
18798
18799
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
18800
+ ): Promise<PostgresSchedulerOutboxDrainResult> {
18801
+ return dispatchRunLedgerOutboxThroughEventWithScope(
18802
+ withClient,
18803
+ input,
18804
+ options,
18805
+ );
18806
+ }
18807
+
18808
+ type RunLedgerOutboxDrainScope = {
18809
+ runId: string;
18810
+ leaseOwner: string;
18811
+ targetEventId: string;
18812
+ nextClaimed: PostgresSchedulerOutboxEvent[];
18813
+ authority: {
18814
+ runtimeApi: WorkerRuntimeApiContext;
18815
+ expiresAtMs: number;
18816
+ } | null;
18817
+ };
18818
+
18819
+ async function dispatchRunLedgerOutboxThroughEventWithScope(
18820
+ withClient: <T>(
18821
+ operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
18822
+ ) => Promise<T>,
18823
+ input: RunLedgerOutboxDispatchInput & {
18824
+ targetEventId: string;
18825
+ maxEvents?: number;
18826
+ },
18827
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
18828
+ scope?: RunLedgerOutboxDrainScope,
18799
18829
  ): Promise<PostgresSchedulerOutboxDrainResult> {
18800
18830
  const runId = input.runId?.trim();
18801
18831
  if (!runId) {
@@ -18822,18 +18852,17 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
18822
18852
 
18823
18853
  let remaining = maxEvents;
18824
18854
  while (remaining === null || remaining > 0) {
18825
- const result =
18826
- await dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
18827
- withClient,
18828
- {
18829
- ...input,
18830
- waitForRunLock: true,
18831
- continueThroughTerminal: false,
18832
- limit:
18833
- remaining === null ? batchSize : Math.min(batchSize, remaining),
18834
- },
18835
- options,
18836
- );
18855
+ const result = await dispatchRunLedgerOutboxBatchWithScope(
18856
+ withClient,
18857
+ {
18858
+ ...input,
18859
+ waitForRunLock: true,
18860
+ continueThroughTerminal: false,
18861
+ limit: remaining === null ? batchSize : Math.min(batchSize, remaining),
18862
+ },
18863
+ options,
18864
+ scope,
18865
+ );
18837
18866
  attempted += result.attempted;
18838
18867
  projected += result.projected;
18839
18868
  failed += result.failed;
@@ -18859,7 +18888,12 @@ export async function dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope
18859
18888
  // A projection failure is durably scheduled for retry or moved to terminal
18860
18889
  // DLQ. Its policy owns the next attempt; spinning here would only reclaim
18861
18890
  // the same failed predecessor and delay the park indefinitely.
18862
- if (result.projected === 0 || result.attempted === 0) break;
18891
+ if (
18892
+ result.projected === 0 ||
18893
+ result.attempted === 0 ||
18894
+ (scope && scope.nextClaimed.length === 0)
18895
+ )
18896
+ break;
18863
18897
  }
18864
18898
 
18865
18899
  // A concurrent dispatcher can claim the target between batches. An empty
@@ -18905,11 +18939,37 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
18905
18939
  ) => Promise<T>,
18906
18940
  input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean },
18907
18941
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
18942
+ ): Promise<PostgresSchedulerOutboxProjectionResult> {
18943
+ return dispatchRunLedgerOutboxBatchWithScope(withClient, input, options);
18944
+ }
18945
+
18946
+ async function dispatchRunLedgerOutboxBatchWithScope(
18947
+ withClient: <T>(
18948
+ operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
18949
+ ) => Promise<T>,
18950
+ input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean },
18951
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
18952
+ scope?: RunLedgerOutboxDrainScope,
18908
18953
  ): Promise<PostgresSchedulerOutboxProjectionResult> {
18909
18954
  const dispatchStartedAtMs = Date.now();
18910
18955
  const leaseOwner =
18911
18956
  input.leaseOwner?.trim() || `run-ledger-outbox-dispatch:${randomUUID()}`;
18912
- const events = await withClient(async (client) => {
18957
+ const continuation: { current: RunLedgerOutboxDrainScope | null } = {
18958
+ current: scope ?? null,
18959
+ };
18960
+ const releasePendingContinuation = async () => {
18961
+ const nextClaimed = continuation.current?.nextClaimed ?? [];
18962
+ if (nextClaimed.length === 0) return;
18963
+ await withClient((client) =>
18964
+ releasePostgresSchedulerOutboxLease(
18965
+ client,
18966
+ nextClaimed.map((event) => event.eventId),
18967
+ { ...options, leaseOwner },
18968
+ ),
18969
+ );
18970
+ nextClaimed.length = 0;
18971
+ };
18972
+ const claimEvents = async (client: PostgresSchedulerQueryClient) => {
18913
18973
  // The checkout is exclusive. Hold the existing scheduler run's row lock
18914
18974
  // only while choosing/claiming its outbox prefix, never over HTTP. A
18915
18975
  // second statement takes a fresh READ COMMITTED snapshot after the lock:
@@ -19037,7 +19097,10 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19037
19097
  await client.query('ROLLBACK');
19038
19098
  throw error;
19039
19099
  }
19040
- });
19100
+ };
19101
+ const events = scope?.nextClaimed.length
19102
+ ? scope.nextClaimed.splice(0)
19103
+ : await withClient(claimEvents);
19041
19104
  if (events.length === 0) {
19042
19105
  return {
19043
19106
  attempted: 0,
@@ -19057,9 +19120,19 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19057
19120
  try {
19058
19121
  const first = events[0]!;
19059
19122
  const tokenAuthority = input.tokenAuthority ?? 'local_mint';
19123
+ if (
19124
+ scope &&
19125
+ (scope.runId !== first.runId || scope.leaseOwner !== leaseOwner)
19126
+ ) {
19127
+ throw new Error('Run-ledger continuation is outside its dispatch scope.');
19128
+ }
19129
+ if (scope?.authority && scope.authority.expiresAtMs <= Date.now()) {
19130
+ throw new Error('Run-ledger continuation authority expired.');
19131
+ }
19060
19132
  const gatewayRuntimeApi =
19061
19133
  tokenAuthority === 'gateway'
19062
- ? await loadPostgresSchedulerRuntimeApiForRun(
19134
+ ? (scope?.authority?.runtimeApi ??
19135
+ (await loadPostgresSchedulerRuntimeApiForRun(
19063
19136
  withClient,
19064
19137
  first.runId,
19065
19138
  first.leaseOwner
@@ -19068,7 +19141,7 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19068
19141
  tokenAuthority,
19069
19142
  options,
19070
19143
  input.runtimeApi?.signal,
19071
- )
19144
+ )))
19072
19145
  : null;
19073
19146
  const resolvedRuntimeApi =
19074
19147
  gatewayRuntimeApi ??
@@ -19110,6 +19183,12 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19110
19183
  // The caller-owned admission deadline (see `signal` above) wins.
19111
19184
  ...(input.signal ? { signal: input.signal } : {}),
19112
19185
  };
19186
+ const tokenExpiresAtMs = runtimeApiTokenExpiresAtMs(
19187
+ runtimeApi.executorToken,
19188
+ );
19189
+ if (scope && gatewayRuntimeApi && tokenExpiresAtMs !== null) {
19190
+ scope.authority = { runtimeApi, expiresAtMs: tokenExpiresAtMs };
19191
+ }
19113
19192
  authorityReadyAtMs = Date.now();
19114
19193
  const renewDelivery = () =>
19115
19194
  withClient((client) =>
@@ -19123,13 +19202,14 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19123
19202
  options,
19124
19203
  ),
19125
19204
  );
19126
- await renewDelivery();
19127
19205
  // A pending start is always claimed alone because later events require its
19128
19206
  // snapshot. It cannot share the append endpoint, so retain that mutation
19129
19207
  // as its own exact delivery.
19130
19208
  if (first.eventType === RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runCreated) {
19209
+ await renewDelivery();
19131
19210
  await projectPostgresSchedulerCreatedEvent(runtimeApi, first);
19132
19211
  } else if (first.eventType === RUN_SCHEDULER_OUTBOX_EVENT_TYPE.runStarted) {
19212
+ await renewDelivery();
19133
19213
  const leaseOwner = first.leaseOwner;
19134
19214
  const launch = leaseOwner
19135
19215
  ? await withClient((client) =>
@@ -19173,16 +19253,18 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19173
19253
  deliveryDigest: batchDeliveryKey,
19174
19254
  }),
19175
19255
  });
19256
+ } else {
19257
+ await renewDelivery();
19176
19258
  }
19177
19259
  }
19178
19260
  appendFinishedAtMs = Date.now();
19179
- const deliveredRows = await withClient((client) =>
19180
- markPostgresSchedulerOutboxDeliveredRows(
19181
- client,
19182
- events.map((event) => event.eventId),
19183
- { ...options, leaseOwner },
19184
- ),
19261
+ const acknowledgement = await acknowledgeRunLedgerPrefixWithContinuation(
19262
+ withClient,
19263
+ { input, events, leaseOwner, runtimeApi, scope, continuation },
19264
+ options,
19185
19265
  );
19266
+ const { deliveredRows } = acknowledgement;
19267
+ if (!acknowledgement.continuation) await releasePendingContinuation();
19186
19268
  acknowledgedAtMs = Date.now();
19187
19269
  const markedDelivered = deliveredRows.length;
19188
19270
  if (input.onDelivered && deliveredRows.length > 0) {
@@ -19210,19 +19292,16 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19210
19292
  retrying: 0,
19211
19293
  retryingEventIds: [],
19212
19294
  };
19213
- if (
19214
- input.continueThroughTerminal !== true ||
19215
- input.headEventId ||
19216
- markedDelivered !== events.length
19217
- ) {
19295
+ if (!acknowledgement.continuation || scope) {
19218
19296
  return delivered;
19219
19297
  }
19220
19298
  return await continueRunLedgerOutboxThroughTerminal(
19221
19299
  withClient,
19222
- { input, leaseOwner, runId: first.runId, delivered, events },
19300
+ { input, delivered, scope: acknowledgement.continuation },
19223
19301
  options,
19224
19302
  );
19225
19303
  } catch (error) {
19304
+ await releasePendingContinuation();
19226
19305
  if (input.signal?.aborted) {
19227
19306
  // The caller's latency budget expired; this is not a delivery failure.
19228
19307
  // Release the lease (fenced to this owner) without charging a
@@ -19310,19 +19389,10 @@ export async function dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
19310
19389
  }
19311
19390
 
19312
19391
  /**
19313
- * Whoever holds a run's outbox lease delivers its terminal fact.
19314
- *
19315
- * A worker commits a run's terminal and immediately asks to deliver it
19316
- * (`run-terminal:<runId>`). When another dispatcher (normally the projector)
19317
- * is mid-append on an earlier event of the same run, that run-scoped claim
19318
- * finds the run leased and correctly claims nothing, and the terminal then
19319
- * waited for the projector's next wave: seconds of a finished run still
19320
- * reading "running" (live-run-observation-e2e, 4.2 s). So the lease holder,
19321
- * having acknowledged its batch, carries the run's terminal fact through in
19322
- * the same turn when it is now pending. The terminal is the run's last fact,
19323
- * so this is one finite causal drain (the same leased, idempotent
19324
- * `dispatchRunLedgerOutboxToLedgerThroughEvent` the worker uses), not a loop;
19325
- * a run without a pending terminal costs one indexed read.
19392
+ * A terminal producer cannot claim an earlier prefix held by a projector.
19393
+ * The existing owner atomically retains each successor through the persisted
19394
+ * terminal, preserving ordered receipts without waiting for another wave.
19395
+ * With no pending terminal, the ACK transaction retains no successor.
19326
19396
  */
19327
19397
  async function continueRunLedgerOutboxThroughTerminal(
19328
19398
  withClient: <T>(
@@ -19330,36 +19400,22 @@ async function continueRunLedgerOutboxThroughTerminal(
19330
19400
  ) => Promise<T>,
19331
19401
  input: {
19332
19402
  input: RunLedgerOutboxDispatchInput & { waitForRunLock?: boolean };
19333
- leaseOwner: string;
19334
- runId: string;
19335
19403
  delivered: PostgresSchedulerOutboxProjectionResult;
19336
- events: readonly PostgresSchedulerOutboxEvent[];
19404
+ scope: RunLedgerOutboxDrainScope;
19337
19405
  },
19338
19406
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
19339
19407
  ): Promise<PostgresSchedulerOutboxProjectionResult> {
19340
- const terminalEventId = `run-terminal:${input.runId}`;
19341
- if (input.events.some((event) => event.eventId === terminalEventId)) {
19342
- return input.delivered;
19343
- }
19344
- const terminal = await withClient((client) =>
19345
- readPostgresSchedulerOutboxDeliveryStatus(
19346
- client,
19347
- { runId: input.runId, eventId: terminalEventId },
19348
- { ...options, includeBlocked: false },
19349
- ),
19408
+ const drained = await dispatchRunLedgerOutboxThroughEventWithScope(
19409
+ withClient,
19410
+ {
19411
+ ...input.input,
19412
+ runId: input.scope.runId,
19413
+ leaseOwner: input.scope.leaseOwner,
19414
+ targetEventId: input.scope.targetEventId,
19415
+ },
19416
+ options,
19417
+ input.scope,
19350
19418
  );
19351
- if (!terminal.exists || terminal.delivered) return input.delivered;
19352
- const drained =
19353
- await dispatchRunLedgerOutboxToLedgerThroughEventWithClientScope(
19354
- withClient,
19355
- {
19356
- ...input.input,
19357
- runId: input.runId,
19358
- leaseOwner: input.leaseOwner,
19359
- targetEventId: terminalEventId,
19360
- },
19361
- options,
19362
- );
19363
19419
  return {
19364
19420
  attempted: input.delivered.attempted + drained.attempted,
19365
19421
  projected: input.delivered.projected + drained.projected,
@@ -19371,6 +19427,120 @@ async function continueRunLedgerOutboxThroughTerminal(
19371
19427
  };
19372
19428
  }
19373
19429
 
19430
+ function runtimeApiTokenExpiresAtMs(token: string): number | null {
19431
+ try {
19432
+ const expiresAt = decodeJwt(token).exp;
19433
+ return typeof expiresAt === 'number' && Number.isFinite(expiresAt)
19434
+ ? expiresAt * 1_000
19435
+ : null;
19436
+ } catch {
19437
+ // Opaque legacy/test tokens retain the existing fresh-authority path.
19438
+ return null;
19439
+ }
19440
+ }
19441
+
19442
+ async function acknowledgeRunLedgerPrefixWithContinuation(
19443
+ withClient: <T>(
19444
+ operation: (client: PostgresSchedulerQueryClient) => Promise<T>,
19445
+ ) => Promise<T>,
19446
+ input: {
19447
+ input: RunLedgerOutboxDispatchInput;
19448
+ events: PostgresSchedulerOutboxEvent[];
19449
+ leaseOwner: string;
19450
+ runtimeApi: WorkerRuntimeApiContext;
19451
+ scope?: RunLedgerOutboxDrainScope;
19452
+ continuation: { current: RunLedgerOutboxDrainScope | null };
19453
+ },
19454
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
19455
+ ): Promise<{
19456
+ deliveredRows: RunLedgerOutboxDeliveredRow[];
19457
+ continuation: RunLedgerOutboxDrainScope | null;
19458
+ }> {
19459
+ const runId = input.events[0]!.runId;
19460
+ const targetEventId = input.scope?.targetEventId ?? `run-terminal:${runId}`;
19461
+ const retainContinuation =
19462
+ !!input.scope ||
19463
+ (input.input.continueThroughTerminal === true && !input.input.headEventId);
19464
+ return withClient(async (client) => {
19465
+ if (retainContinuation) {
19466
+ await client.query('BEGIN');
19467
+ }
19468
+ try {
19469
+ if (retainContinuation) {
19470
+ await client.query(
19471
+ `SELECT set_config('lock_timeout', $1, true), set_config('statement_timeout', $2, true)`,
19472
+ [
19473
+ `${RUNTIME_SCHEDULER_CANCEL_LOCK_TIMEOUT_MS}ms`,
19474
+ `${RUNTIME_SCHEDULER_CANCEL_STATEMENT_TIMEOUT_MS}ms`,
19475
+ ],
19476
+ );
19477
+ // Keep the acknowledged prefix and its successor under one run lock.
19478
+ // No competing projector can take the next head while this ACK's
19479
+ // response is travelling back to the lease holder.
19480
+ await client.query(
19481
+ `SELECT run_id FROM ${tableName('runs', options)} WHERE run_id = $1 FOR UPDATE`,
19482
+ [runId],
19483
+ );
19484
+ }
19485
+ const deliveredRows = await markPostgresSchedulerOutboxDeliveredRows(
19486
+ client,
19487
+ input.events.map((event) => event.eventId),
19488
+ { ...options, leaseOwner: input.leaseOwner },
19489
+ );
19490
+ let continuation: RunLedgerOutboxDrainScope | null = null;
19491
+ if (
19492
+ retainContinuation &&
19493
+ deliveredRows.length === input.events.length &&
19494
+ !input.events.some((event) => event.eventId === targetEventId)
19495
+ ) {
19496
+ const target = await readPostgresSchedulerOutboxDeliveryStatus(
19497
+ client,
19498
+ { runId, eventId: targetEventId },
19499
+ options,
19500
+ );
19501
+ if (target.exists && !target.delivered && !target.blocked) {
19502
+ const nextClaimed = await readPendingPostgresSchedulerOutboxEvents(
19503
+ client,
19504
+ {
19505
+ runId,
19506
+ leaseOwner: input.leaseOwner,
19507
+ leaseSeconds: input.input.leaseSeconds ?? 120,
19508
+ limit: Math.min(
19509
+ RUN_LEDGER_PROJECTION_PREFIX_MAX_FACTS,
19510
+ input.input.limit ?? 1,
19511
+ ),
19512
+ inTransaction: true,
19513
+ },
19514
+ options,
19515
+ );
19516
+ if (nextClaimed.length > 0) {
19517
+ const expiresAtMs = runtimeApiTokenExpiresAtMs(
19518
+ input.runtimeApi.executorToken,
19519
+ );
19520
+ continuation = input.scope ?? {
19521
+ runId,
19522
+ leaseOwner: input.leaseOwner,
19523
+ targetEventId,
19524
+ nextClaimed: [],
19525
+ authority:
19526
+ input.input.tokenAuthority === 'gateway' && expiresAtMs !== null
19527
+ ? { runtimeApi: input.runtimeApi, expiresAtMs }
19528
+ : null,
19529
+ };
19530
+ continuation.nextClaimed = nextClaimed;
19531
+ input.continuation.current = continuation;
19532
+ }
19533
+ }
19534
+ }
19535
+ if (retainContinuation) await client.query('COMMIT');
19536
+ return { deliveredRows, continuation };
19537
+ } catch (error) {
19538
+ if (retainContinuation) await client.query('ROLLBACK');
19539
+ throw error;
19540
+ }
19541
+ });
19542
+ }
19543
+
19374
19544
  async function projectPostgresSchedulerCreatedEvent(
19375
19545
  runtimeApi: WorkerRuntimeApiContext,
19376
19546
  event: PostgresSchedulerOutboxEvent,
@@ -26,6 +26,10 @@ export const RUNTIME_SANDBOX_CAPACITY_LEASE_MS = 50 * 60 * 1_000;
26
26
  */
27
27
  export type RuntimeSandboxCapacityAdmissionMode = 'observe' | 'enforce';
28
28
 
29
+ // Only a still-unresolved admission callback can prove its own create is unsent.
30
+ // This authority is process-local and cannot be reconstructed from stored keys.
31
+ const unsentReservationInputs = new WeakMap<object, string>();
32
+
29
33
  export function runtimeSandboxCapacityAdmissionMode(
30
34
  env: Record<string, string | undefined> = process.env,
31
35
  ): RuntimeSandboxCapacityAdmissionMode {
@@ -306,10 +310,12 @@ export async function reserveRuntimeSandboxCapacityLease(
306
310
  const activeLeases = Number(observed.rows[0]?.active_leases ?? 0);
307
311
  const wouldDefer = activeLeases >= limit;
308
312
  const leaseId = input.leaseId?.trim() || randomUUID();
313
+ const unsentProviderOperationToken = unsentReservationInputs.get(input);
309
314
  const providerOperationToken =
310
315
  input.attempt === undefined
311
316
  ? null
312
- : runtimeSandboxProviderOperationToken(leaseId);
317
+ : (unsentProviderOperationToken ??
318
+ runtimeSandboxProviderOperationToken(leaseId));
313
319
  if (
314
320
  input.resumePendingDaytonaOperation === true &&
315
321
  input.provider === RuntimeSandboxProvider.Daytona &&
@@ -342,7 +348,15 @@ export async function reserveRuntimeSandboxCapacityLease(
342
348
  );
343
349
  }
344
350
  const pendingOperation = pending.rows[0];
345
- if (pendingOperation) {
351
+ if (
352
+ pendingOperation &&
353
+ !(
354
+ pendingOperation.lease_id === leaseId &&
355
+ pendingOperation.provider_operation_token ===
356
+ unsentProviderOperationToken &&
357
+ unsentProviderOperationToken !== undefined
358
+ )
359
+ ) {
346
360
  const pendingToken =
347
361
  pendingOperation.provider_operation_token ??
348
362
  (await backfillReservedProviderOperationToken(
@@ -424,7 +438,8 @@ export async function reserveRuntimeSandboxCapacityLease(
424
438
  if (
425
439
  input.attempt !== undefined &&
426
440
  row.provider_operation_token !== null &&
427
- row.provider_operation_token !== providerOperationToken
441
+ row.provider_operation_token !== providerOperationToken &&
442
+ unsentProviderOperationToken === undefined
428
443
  ) {
429
444
  throw new Error(
430
445
  'operationKey reservation has an incompatible provider operation token.',
@@ -450,7 +465,10 @@ export async function reserveRuntimeSandboxCapacityLease(
450
465
  provider: input.provider,
451
466
  attempt: input.attempt,
452
467
  environment: input.environment ?? 'production',
453
- providerOperationToken,
468
+ providerOperationToken:
469
+ unsentProviderOperationToken === undefined
470
+ ? providerOperationToken
471
+ : runtimeSandboxProviderOperationToken(leaseId),
454
472
  },
455
473
  options,
456
474
  );
@@ -462,7 +480,9 @@ export async function reserveRuntimeSandboxCapacityLease(
462
480
  limit,
463
481
  activeLeases,
464
482
  wouldDefer,
465
- resumeExistingProviderOperation: false,
483
+ resumeExistingProviderOperation:
484
+ unsentProviderOperationToken !== undefined &&
485
+ resolvedProviderOperationToken !== unsentProviderOperationToken,
466
486
  };
467
487
  }
468
488
  if (wouldDefer && (input.mode ?? 'enforce') === 'enforce') {
@@ -545,14 +565,33 @@ export async function reserveRuntimeSandboxCapacityLease(
545
565
  'operationKey reservation did not persist the requested sandbox operation.',
546
566
  );
547
567
  }
568
+ const resolvedProviderOperationToken =
569
+ row.provider_operation_token === null && input.attempt !== undefined
570
+ ? await backfillReservedProviderOperationToken(
571
+ client,
572
+ {
573
+ leaseId,
574
+ orgId,
575
+ runId,
576
+ provider: input.provider,
577
+ attempt: input.attempt,
578
+ environment: input.environment ?? 'production',
579
+ providerOperationToken:
580
+ runtimeSandboxProviderOperationToken(leaseId),
581
+ },
582
+ options,
583
+ )
584
+ : row.provider_operation_token;
548
585
  return {
549
586
  granted: true,
550
587
  leaseId,
551
- providerOperationToken: row.provider_operation_token,
588
+ providerOperationToken: resolvedProviderOperationToken,
552
589
  limit,
553
590
  activeLeases,
554
591
  wouldDefer,
555
- resumeExistingProviderOperation: false,
592
+ resumeExistingProviderOperation:
593
+ unsentProviderOperationToken !== undefined &&
594
+ resolvedProviderOperationToken !== unsentProviderOperationToken,
556
595
  };
557
596
  }
558
597
 
@@ -649,6 +688,54 @@ export async function reserveRuntimeSandboxOperation(
649
688
  );
650
689
  }
651
690
 
691
+ /**
692
+ * Create once per provider admission, outside pooled callback retries. A fresh
693
+ * token identifies this invocation's unsent reservation, while the deterministic
694
+ * operation key still coalesces concurrent admission. A losing invocation must
695
+ * reconcile the winning token, even on 404.
696
+ * The callback becomes unusable on resolution, before provider I/O can start.
697
+ */
698
+ export function createRuntimeSandboxOperationReservation(
699
+ input: Parameters<typeof reserveRuntimeSandboxOperation>[1],
700
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
701
+ ) {
702
+ if (!Number.isSafeInteger(input.attempt) || input.attempt < 1) {
703
+ throw new Error('attempt must be a positive safe integer.');
704
+ }
705
+ const reservationInput: Parameters<
706
+ typeof reserveRuntimeSandboxCapacityLease
707
+ >[1] = {
708
+ ...input,
709
+ leaseId: assertId(input.operationKey, 'operationKey'),
710
+ };
711
+ unsentReservationInputs.set(
712
+ reservationInput,
713
+ runtimeSandboxProviderOperationToken(randomUUID()),
714
+ );
715
+ let inFlight = false;
716
+ let settled = false;
717
+ return async (client: PostgresSchedulerQueryClient) => {
718
+ if (inFlight || settled) {
719
+ throw new Error(
720
+ 'Sandbox reservation callback is already in use or settled.',
721
+ );
722
+ }
723
+ inFlight = true;
724
+ try {
725
+ const reserved = await reserveRuntimeSandboxCapacityLease(
726
+ client,
727
+ reservationInput,
728
+ options,
729
+ );
730
+ settled = true;
731
+ unsentReservationInputs.delete(reservationInput);
732
+ return reserved;
733
+ } finally {
734
+ inFlight = false;
735
+ }
736
+ };
737
+ }
738
+
652
739
  export async function bindRuntimeSandboxCapacityLease(
653
740
  client: PostgresSchedulerQueryClient,
654
741
  input: {
@@ -6,6 +6,7 @@ type SecretRedactionOptions = {
6
6
  keyPolicy?: 'strict' | 'public-result';
7
7
  /** Replace sample cursor values with a non-capability-bearing placeholder. */
8
8
  placeholderContinuationValues?: boolean;
9
+ redactJsonStrings?: boolean;
9
10
  };
10
11
 
11
12
  const SENSITIVE_KEY_PATTERNS = [
@@ -399,12 +400,83 @@ function maybeRedactString(value: string, options: SecretRedactionOptions) {
399
400
  : redacted.replace(HIGH_ENTROPY_VALUE_PATTERN, REDACTED_VALUE);
400
401
  }
401
402
 
403
+ function parseJsonContainerText(value: unknown): unknown {
404
+ if (typeof value !== 'string') return value;
405
+ const trimmed = value.trim();
406
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return value;
407
+ try {
408
+ return JSON.parse(value);
409
+ } catch {
410
+ return value;
411
+ }
412
+ }
413
+
414
+ function jsonStringContainsSecrets(
415
+ value: string,
416
+ options: SecretRedactionOptions,
417
+ parentKey?: string,
418
+ path: readonly string[] = [],
419
+ ): boolean {
420
+ // Inspect parsed values, but never reserialize SQL text and round its numeric literals.
421
+ const parsed = parseJsonContainerText(value);
422
+ if (!parsed || typeof parsed !== 'object') return false;
423
+ const pending = [
424
+ {
425
+ value: parsed,
426
+ parentKey,
427
+ path: [...path],
428
+ },
429
+ ];
430
+ const textOptions = { ...options, redactJsonStrings: false };
431
+ while (pending.length > 0) {
432
+ const current = pending.pop()!;
433
+ const sourceIsArray = Array.isArray(current.value);
434
+ for (const [key, rawItem] of Object.entries(current.value)) {
435
+ const item = parseJsonContainerText(rawItem);
436
+ const parentKey = sourceIsArray ? current.parentKey : key;
437
+ if (
438
+ !sourceIsArray &&
439
+ options.keyPolicy === 'public-result' &&
440
+ isPublicResultSafeTokenKey(key, current.path, item) &&
441
+ (!item || typeof item !== 'object')
442
+ ) {
443
+ if (
444
+ typeof item === 'string' &&
445
+ maybeRedactString(item, textOptions) !== item
446
+ )
447
+ return true;
448
+ continue;
449
+ }
450
+ if (!sourceIsArray && shouldRedactKey(key)) return true;
451
+ if (item && typeof item === 'object') {
452
+ pending.push({
453
+ value: item,
454
+ parentKey,
455
+ path: sourceIsArray ? current.path : [...current.path, key],
456
+ });
457
+ } else if (
458
+ typeof item === 'string' &&
459
+ redactSecretsInValue(item, parentKey, textOptions) !== item
460
+ ) {
461
+ return true;
462
+ }
463
+ }
464
+ }
465
+ return false;
466
+ }
467
+
402
468
  export function redactSecretsInValue<T>(
403
469
  value: T,
404
470
  parentKey?: string,
405
471
  options: SecretRedactionOptions = {},
406
472
  ): T {
407
473
  if (typeof value === 'string') {
474
+ if (
475
+ options.redactJsonStrings &&
476
+ jsonStringContainsSecrets(value, options, parentKey)
477
+ ) {
478
+ return REDACTED_VALUE as T;
479
+ }
408
480
  if (hasExplicitSecretValue(value, options)) {
409
481
  return REDACTED_VALUE as T;
410
482
  }
@@ -447,8 +519,15 @@ export function redactSecretsInValue<T>(
447
519
  const redactNestedValue = (
448
520
  item: unknown,
449
521
  itemParentKey: string | undefined,
522
+ path: readonly string[],
450
523
  ): unknown => {
451
524
  if (typeof item !== 'string') return item;
525
+ if (
526
+ options.redactJsonStrings &&
527
+ jsonStringContainsSecrets(item, options, itemParentKey, path)
528
+ ) {
529
+ return REDACTED_VALUE;
530
+ }
452
531
  if (hasExplicitSecretValue(item, options)) {
453
532
  return REDACTED_VALUE;
454
533
  }
@@ -473,7 +552,15 @@ export function redactSecretsInValue<T>(
473
552
  (!item || typeof item !== 'object')
474
553
  ) {
475
554
  const redacted =
476
- typeof item === 'string' ? maybeRedactString(item, options) : item;
555
+ typeof item === 'string'
556
+ ? options.redactJsonStrings &&
557
+ jsonStringContainsSecrets(item, options, itemParentKey, [
558
+ ...current.path,
559
+ key,
560
+ ])
561
+ ? REDACTED_VALUE
562
+ : maybeRedactString(item, options)
563
+ : item;
477
564
  (current.target as Record<string, unknown>)[key] =
478
565
  options.placeholderContinuationValues &&
479
566
  isPublicResultCursorKey(key, current.path) &&
@@ -502,6 +589,7 @@ export function redactSecretsInValue<T>(
502
589
  (current.target as Record<string, unknown>)[key] = redactNestedValue(
503
590
  item,
504
591
  itemParentKey,
592
+ sourceIsArray ? current.path : [...current.path, key],
505
593
  );
506
594
  }
507
595
  }
package/dist/cli/index.js CHANGED
@@ -3515,7 +3515,7 @@ var SDK_RELEASE = {
3515
3515
  // getters keep their established compatibility behavior.
3516
3516
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3517
3517
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3518
- version: "0.3.282",
3518
+ version: "0.3.284",
3519
3519
  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.",
3520
3520
  packageCapabilities: {
3521
3521
  updatePreferences: 1
@@ -3510,7 +3510,7 @@ var SDK_RELEASE = {
3510
3510
  // getters keep their established compatibility behavior.
3511
3511
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3512
3512
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3513
- version: "0.3.282",
3513
+ version: "0.3.284",
3514
3514
  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.",
3515
3515
  packageCapabilities: {
3516
3516
  updatePreferences: 1
package/dist/index.js CHANGED
@@ -1328,7 +1328,7 @@ var SDK_RELEASE = {
1328
1328
  // getters keep their established compatibility behavior.
1329
1329
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1330
1330
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
1331
- version: "0.3.282",
1331
+ version: "0.3.284",
1332
1332
  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.",
1333
1333
  packageCapabilities: {
1334
1334
  updatePreferences: 1
package/dist/index.mjs CHANGED
@@ -1232,7 +1232,7 @@ var SDK_RELEASE = {
1232
1232
  // getters keep their established compatibility behavior.
1233
1233
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1234
1234
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
1235
- version: "0.3.282",
1235
+ version: "0.3.284",
1236
1236
  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.",
1237
1237
  packageCapabilities: {
1238
1238
  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.282";
152
+ readonly version: "0.3.284";
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.282";
152
+ readonly version: "0.3.284";
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.282",
77
+ version: "0.3.284",
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.282",
51
+ version: "0.3.284",
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.282",
3
+ "version": "0.3.284",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",