deepline 0.3.185 → 0.3.186

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.185',
203
+ version: '0.3.186',
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: {
@@ -1331,6 +1331,8 @@ export interface PlayDefinitionDetail {
1331
1331
  cronTimezone?: string | null;
1332
1332
  nextScheduledAt?: number | null;
1333
1333
  lastScheduledAt?: number | null;
1334
+ blockedAt?: number | null;
1335
+ blockedReasonCode?: string | null;
1334
1336
  blockedReason?: string | null;
1335
1337
  [key: string]: unknown;
1336
1338
  } | null;
@@ -1403,6 +1405,8 @@ export interface PlayTriggerStatus {
1403
1405
  cron: string | null;
1404
1406
  webhook: string | null;
1405
1407
  blockedReason: string | null;
1408
+ blockedReasonCode?: string;
1409
+ blockedAt?: number;
1406
1410
  }
1407
1411
 
1408
1412
  export interface ProductNotificationEventDefinition {
@@ -0,0 +1,229 @@
1
+ import { sha256Hex, stableStringify } from '../plays/row-identity';
2
+ import { LEGACY_MAX_CONCURRENT_EXTERNAL_CALLS } from './governor/policy';
3
+ import type { PlaySchedulerSubmitInput } from './scheduler-backend';
4
+
5
+ /** Optional launch-envelope field used by the mixed-release admission fence. */
6
+ export const PLAY_LAUNCH_IDENTITY_DIGEST_FIELD = 'launchIdentityDigest';
7
+ const PLAY_LAUNCH_IDENTITY_DIGEST_NAMESPACE = 'deepline:play-launch-identity';
8
+
9
+ export type PlayLaunchFieldRole = 'identity' | 'detail';
10
+
11
+ /**
12
+ * Keep this list exhaustive over the launch envelope. A newly added field must
13
+ * be classified before it can participate in launch admission.
14
+ */
15
+ export const PLAY_LAUNCH_ENVELOPE_FIELD_ROLES: {
16
+ readonly [K in keyof PlaySchedulerSubmitInput]-?: PlayLaunchFieldRole;
17
+ } = {
18
+ orgId: 'identity',
19
+ playName: 'identity',
20
+ playReference: 'identity',
21
+ definitionScope: 'identity',
22
+ revisionId: 'identity',
23
+ artifactStorageKey: 'identity',
24
+ artifactHash: 'identity',
25
+ graphHash: 'identity',
26
+ workflowFamilyKey: 'identity',
27
+ inputSha256: 'identity',
28
+ source: 'identity',
29
+ triggerSource: 'identity',
30
+ parentRunId: 'identity',
31
+ rootRunId: 'identity',
32
+ asyncLaunchKey: 'identity',
33
+ asyncInvocationKey: 'identity',
34
+ asyncRequestFingerprint: 'identity',
35
+ force: 'identity',
36
+ forceToolRefresh: 'identity',
37
+ maxConcurrentExternalCalls: 'identity',
38
+ maxConcurrentRows: 'identity',
39
+ integrationMode: 'identity',
40
+ runtimeSandboxPlacementPolicyId: 'identity',
41
+ input: 'identity',
42
+ inputFile: 'identity',
43
+ inlineInputFile: 'identity',
44
+ inlineCsv: 'identity',
45
+ packagedFiles: 'identity',
46
+ runId: 'detail',
47
+ existingOnly: 'detail',
48
+ asyncParentRunAttempt: 'detail',
49
+ asyncParentRunLeaseToken: 'detail',
50
+ asyncParentAttemptRequired: 'detail',
51
+ asyncAncestryPlayIds: 'detail',
52
+ playId: 'detail',
53
+ schedulerSchema: 'detail',
54
+ requiresRunCreatedProjection: 'detail',
55
+ runtimeArtifact: 'detail',
56
+ runtimeProtocolVersion: 'detail',
57
+ staticPipeline: 'detail',
58
+ inputSummary: 'detail',
59
+ inputBytes: 'detail',
60
+ inputFileId: 'detail',
61
+ replayedFromRunId: 'detail',
62
+ totalRows: 'detail',
63
+ contractSnapshot: 'detail',
64
+ executionPlan: 'detail',
65
+ preloadedDbSessions: 'detail',
66
+ preloadedDbSessionUnwrapKey: 'detail',
67
+ executorToken: 'detail',
68
+ runtimeAuthority: 'detail',
69
+ runtimeApiBaseUrl: 'detail',
70
+ runtimeTokenOrigin: 'detail',
71
+ baseUrl: 'detail',
72
+ coordinatorUrl: 'detail',
73
+ coordinatorInternalToken: 'detail',
74
+ runtimeDeployVersion: 'detail',
75
+ absurdReleaseId: 'detail',
76
+ absurdReleaseEnvironment: 'detail',
77
+ vercelProtectionBypassToken: 'detail',
78
+ preferredReceiptGatewayMachineId: 'detail',
79
+ runtimeTestFaultHeader: 'detail',
80
+ testPolicyOverrides: 'detail',
81
+ submittedAtMs: 'detail',
82
+ schedulerBackend: 'detail',
83
+ executionProfile: 'detail',
84
+ runtimeBackend: 'detail',
85
+ queueMaxDelaySeconds: 'detail',
86
+ queuePriority: 'detail',
87
+ userEmail: 'detail',
88
+ userId: 'detail',
89
+ launchIdentityDigest: 'detail',
90
+ };
91
+
92
+ export const PLAY_LAUNCH_IDENTITY_FIELDS_COMPARED_OUTSIDE_DIGEST: readonly string[] =
93
+ Object.freeze(['runtimeSandboxPlacementPolicyId']);
94
+
95
+ export const PLAY_LAUNCH_IDENTITY_FIELDS: readonly string[] = Object.freeze(
96
+ Object.entries(PLAY_LAUNCH_ENVELOPE_FIELD_ROLES)
97
+ .filter(([, role]) => role === 'identity')
98
+ .map(([field]) => field)
99
+ .sort(),
100
+ );
101
+
102
+ export const PLAY_LAUNCH_DETAIL_FIELDS: readonly string[] = Object.freeze(
103
+ Object.entries(PLAY_LAUNCH_ENVELOPE_FIELD_ROLES)
104
+ .filter(([, role]) => role === 'detail')
105
+ .map(([field]) => field)
106
+ .sort(),
107
+ );
108
+
109
+ export type PlayLaunchIdentitySource = Readonly<
110
+ Partial<Record<keyof PlaySchedulerSubmitInput, unknown>>
111
+ >;
112
+
113
+ function text(value: unknown): string | null {
114
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
115
+ }
116
+
117
+ function flag(value: unknown): boolean {
118
+ return value === true;
119
+ }
120
+
121
+ function count(value: unknown): number | null {
122
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
123
+ }
124
+
125
+ function record(value: unknown): Record<string, unknown> | null {
126
+ return value && typeof value === 'object' && !Array.isArray(value)
127
+ ? (value as Record<string, unknown>)
128
+ : null;
129
+ }
130
+
131
+ function stagedFileIdentity(value: unknown): Record<string, unknown> | null {
132
+ const file = record(value);
133
+ if (!file) return null;
134
+ return {
135
+ logicalPath:
136
+ text(file.logicalPath) ?? text(file.path) ?? text(file.playPath),
137
+ fileName: text(file.fileName) ?? text(file.name),
138
+ contentType: text(file.contentType),
139
+ bytes: count(file.bytes),
140
+ contentDigest:
141
+ typeof file.contentBase64 === 'string'
142
+ ? sha256Hex(file.contentBase64)
143
+ : typeof file.inlineText === 'string'
144
+ ? sha256Hex(file.inlineText)
145
+ : null,
146
+ };
147
+ }
148
+
149
+ function stagedFileListIdentity(
150
+ value: unknown,
151
+ ): Array<Record<string, unknown>> {
152
+ if (!Array.isArray(value)) return [];
153
+ return value
154
+ .map((entry) => stagedFileIdentity(entry))
155
+ .filter((entry): entry is Record<string, unknown> => entry !== null)
156
+ .sort((left, right) =>
157
+ stableStringify(left).localeCompare(stableStringify(right)),
158
+ );
159
+ }
160
+
161
+ /** Return only customer intent; transport, authority, and release details are excluded. */
162
+ export function buildPlayLaunchIdentity(
163
+ launch: PlayLaunchIdentitySource,
164
+ ): Record<string, unknown> {
165
+ const inlineCsv = record(launch.inlineCsv);
166
+ return {
167
+ orgId: text(launch.orgId),
168
+ playName: text(launch.playName),
169
+ playReference: text(launch.playReference) ?? text(launch.playName),
170
+ definitionScope: text(launch.definitionScope),
171
+ revisionId: text(launch.revisionId),
172
+ artifactStorageKey: text(launch.artifactStorageKey),
173
+ artifactHash: text(launch.artifactHash),
174
+ graphHash: text(launch.graphHash),
175
+ workflowFamilyKey: text(launch.workflowFamilyKey),
176
+ inputSha256: text(launch.inputSha256),
177
+ source: text(launch.source),
178
+ triggerSource: text(launch.triggerSource),
179
+ parentRunId: text(launch.parentRunId),
180
+ rootRunId: text(launch.rootRunId),
181
+ asyncLaunchKey: text(launch.asyncLaunchKey),
182
+ asyncInvocationKey: text(launch.asyncInvocationKey),
183
+ asyncRequestFingerprint: text(launch.asyncRequestFingerprint),
184
+ force: flag(launch.force),
185
+ forceToolRefresh: flag(launch.forceToolRefresh),
186
+ integrationMode: text(launch.integrationMode),
187
+ maxConcurrentExternalCalls:
188
+ count(launch.maxConcurrentExternalCalls) ??
189
+ LEGACY_MAX_CONCURRENT_EXTERNAL_CALLS,
190
+ maxConcurrentRows: count(launch.maxConcurrentRows),
191
+ inputDigest: sha256Hex(stableStringify(launch.input ?? {})),
192
+ inputFile: stagedFileIdentity(launch.inputFile),
193
+ inlineInputFile: stagedFileIdentity(launch.inlineInputFile),
194
+ inlineCsv: inlineCsv
195
+ ? {
196
+ name: text(inlineCsv.name),
197
+ rowsDigest: sha256Hex(stableStringify(inlineCsv.rows ?? [])),
198
+ }
199
+ : null,
200
+ packagedFiles: stagedFileListIdentity(launch.packagedFiles),
201
+ };
202
+ }
203
+
204
+ export const PLAY_LAUNCH_DIGESTED_IDENTITY_FIELDS: readonly string[] =
205
+ Object.freeze(
206
+ PLAY_LAUNCH_IDENTITY_FIELDS.filter(
207
+ (field) =>
208
+ !PLAY_LAUNCH_IDENTITY_FIELDS_COMPARED_OUTSIDE_DIGEST.includes(field),
209
+ ),
210
+ );
211
+
212
+ export function buildPlayLaunchIdentityDigest(
213
+ launch: PlayLaunchIdentitySource,
214
+ ): string {
215
+ return sha256Hex(
216
+ `${PLAY_LAUNCH_IDENTITY_DIGEST_NAMESPACE}\0${stableStringify(
217
+ buildPlayLaunchIdentity(launch),
218
+ )}`,
219
+ );
220
+ }
221
+
222
+ export function withPlayLaunchIdentityDigest<
223
+ T extends PlaySchedulerSubmitInput,
224
+ >(launch: T): T {
225
+ return {
226
+ ...launch,
227
+ launchIdentityDigest: buildPlayLaunchIdentityDigest(launch),
228
+ };
229
+ }
@@ -42,16 +42,31 @@ export type PlayRunAdmissionJob = {
42
42
  admit(input: PlayRunAdmissionInput): Promise<PlayRunAdmissionOutcome>;
43
43
  };
44
44
 
45
+ export type PlayRunLaunchConflictReason = 'launch_identity_mismatch';
46
+
45
47
  /** A stable, machine-readable rejection for a reused run id with new intent. */
46
48
  export class PlayRunLaunchConflictError extends Error {
47
49
  readonly code = 'PLAY_RUN_LAUNCH_CONFLICT';
50
+ readonly conflictReason: PlayRunLaunchConflictReason | undefined;
51
+ readonly conflictingFields: string[] | undefined;
48
52
 
49
- constructor(runId: string, options?: { cause?: unknown }) {
53
+ constructor(
54
+ runId: string,
55
+ options?: {
56
+ cause?: unknown;
57
+ conflictReason?: PlayRunLaunchConflictReason;
58
+ conflictingFields?: readonly string[];
59
+ },
60
+ ) {
50
61
  super(
51
62
  `Play run "${runId}" already exists with a different launch.`,
52
63
  options,
53
64
  );
54
65
  this.name = 'PlayRunLaunchConflictError';
66
+ this.conflictReason = options?.conflictReason;
67
+ this.conflictingFields = options?.conflictingFields
68
+ ? [...options.conflictingFields]
69
+ : undefined;
55
70
  }
56
71
  }
57
72
 
@@ -54,6 +54,9 @@ export function createPostgresPlayRunAdmissionPorts(input: {
54
54
  artifactHash: admission.launch.artifactHash,
55
55
  graphHash: admission.launch.graphHash,
56
56
  inputSha256: admission.launch.inputSha256,
57
+ launchIdentityDigest: admission.launch.launchIdentityDigest,
58
+ conflictReason: conflict.conflictReason ?? null,
59
+ conflictingFields: conflict.conflictingFields ?? null,
57
60
  error: conflict,
58
61
  });
59
62
  throw conflict;
@@ -41,6 +41,12 @@ export type PlayRunLaunchConflictTelemetryInput = {
41
41
  artifactHash?: string | null;
42
42
  graphHash?: string | null;
43
43
  inputSha256?: string | null;
44
+ /** Named-intent digest of the incoming durable launch envelope. */
45
+ launchIdentityDigest?: string | null;
46
+ /** Admission-side conflict classification when available. */
47
+ conflictReason?: string | null;
48
+ /** Named launch-intent fields that differed, when the persisted envelope is available. */
49
+ conflictingFields?: readonly string[] | null;
44
50
  error?: unknown;
45
51
  };
46
52
 
@@ -113,6 +119,9 @@ export function buildPlayRunLaunchConflictTelemetry(
113
119
  revisionId: input.revisionId ?? null,
114
120
  artifactHash: input.artifactHash ?? null,
115
121
  graphHash: input.graphHash ?? null,
122
+ launchIdentityDigest: input.launchIdentityDigest ?? null,
123
+ conflictReason: input.conflictReason ?? null,
124
+ conflictingFields: input.conflictingFields ?? null,
116
125
  triage:
117
126
  level === 'error'
118
127
  ? {
@@ -216,6 +216,13 @@ export type PlaySchedulerSubmitInput = {
216
216
  runtimeTestFaultHeader?: string | null;
217
217
  /** Request-scoped, dev-only runtime policy overrides for black-box durability tests. */
218
218
  testPolicyOverrides?: RuntimeTestPolicyOverrides | null;
219
+ /**
220
+ * Digest of the named intent behind this launch. Runtime admission compares
221
+ * this optional field instead of treating transport and execution details as
222
+ * immutable identity. Older persisted rows omit it and keep the legacy
223
+ * comparison until they are gone.
224
+ */
225
+ launchIdentityDigest?: string | null;
219
226
  /** Millisecond epoch timestamp captured immediately before scheduler submit. */
220
227
  submittedAtMs?: number;
221
228
  /** Scheduler backend that owns orchestration for this run. */
@@ -157,6 +157,11 @@ import {
157
157
  import { LEGACY_MAX_CONCURRENT_EXTERNAL_CALLS } from '../governor/policy';
158
158
  import { requireRuntimeAuthorityDescriptor } from '../execution-capabilities';
159
159
  import { stringifyPostgresJson } from '../postgres-json';
160
+ import {
161
+ buildPlayLaunchIdentity,
162
+ withPlayLaunchIdentityDigest,
163
+ } from '../launch-identity';
164
+ import { PlayRunLaunchConflictError } from '../play-runs/admission/contracts';
160
165
  import { mintPlayRunAttemptRuntimeToken } from '../runtime-authority';
161
166
  import { withActiveSpan } from '../../observability/tracing';
162
167
  import type {
@@ -2198,6 +2203,99 @@ async function ensurePostgresSchedulerCronScheduleSchema(
2198
2203
  `);
2199
2204
  }
2200
2205
 
2206
+ /**
2207
+ * Install the launch-admission functions on every bootstrap path.
2208
+ *
2209
+ * These are function-body compatibility changes, not schema DDL. An already
2210
+ * current scheduler schema takes the fast path below, so keeping these only in
2211
+ * the full bootstrap leaves that estate with the old whole-envelope equality.
2212
+ */
2213
+ async function ensurePostgresSchedulerLaunchAdmissionFunctions(
2214
+ client: PostgresSchedulerQueryClient,
2215
+ options?: Pick<PostgresSchedulerOptions, 'schema'>,
2216
+ ): Promise<void> {
2217
+ const quotedSchema = quoteIdent(schemaName(options));
2218
+ await client.query(`
2219
+ CREATE OR REPLACE FUNCTION ${quotedSchema}.runtime_launch_admission_identity(
2220
+ launch_json jsonb
2221
+ ) RETURNS jsonb
2222
+ LANGUAGE sql
2223
+ IMMUTABLE
2224
+ STRICT
2225
+ PARALLEL SAFE
2226
+ AS $$
2227
+ SELECT jsonb_set(
2228
+ jsonb_set(
2229
+ launch_json - ARRAY[
2230
+ 'launchIdentityDigest',
2231
+ 'executorToken',
2232
+ 'inputFileId',
2233
+ 'preloadedDbSessionUnwrapKey',
2234
+ 'preloadedDbSessions',
2235
+ 'submittedAtMs',
2236
+ 'runtimeDeployVersion',
2237
+ 'absurdReleaseId',
2238
+ 'absurdReleaseEnvironment',
2239
+ 'coordinatorUrl',
2240
+ 'coordinatorInternalToken',
2241
+ 'baseUrl',
2242
+ 'runtimeApiBaseUrl',
2243
+ 'vercelProtectionBypassToken'
2244
+ ]::text[],
2245
+ '{maxConcurrentExternalCalls}',
2246
+ coalesce(
2247
+ launch_json->'maxConcurrentExternalCalls',
2248
+ to_jsonb(${LEGACY_MAX_CONCURRENT_EXTERNAL_CALLS}::integer)
2249
+ ),
2250
+ true
2251
+ ),
2252
+ '{maxConcurrentRows}',
2253
+ coalesce(launch_json->'maxConcurrentRows', 'null'::jsonb),
2254
+ true
2255
+ )
2256
+ $$
2257
+ `);
2258
+ await client.query(`
2259
+ CREATE OR REPLACE FUNCTION ${quotedSchema}.runtime_launch_admission_matches(
2260
+ existing_launch_json jsonb,
2261
+ incoming_launch_json jsonb
2262
+ ) RETURNS boolean
2263
+ LANGUAGE sql
2264
+ IMMUTABLE
2265
+ STRICT
2266
+ PARALLEL SAFE
2267
+ AS $$
2268
+ SELECT CASE
2269
+ WHEN nullif(btrim(coalesce(existing_launch_json->>'launchIdentityDigest', '')), '') IS NOT NULL
2270
+ AND nullif(btrim(coalesce(incoming_launch_json->>'launchIdentityDigest', '')), '') IS NOT NULL
2271
+ THEN btrim(existing_launch_json->>'launchIdentityDigest')
2272
+ = btrim(incoming_launch_json->>'launchIdentityDigest')
2273
+ AND (
2274
+ NOT (
2275
+ existing_launch_json ? 'runtimeSandboxPlacementPolicyId'
2276
+ AND incoming_launch_json ? 'runtimeSandboxPlacementPolicyId'
2277
+ )
2278
+ OR existing_launch_json->'runtimeSandboxPlacementPolicyId'
2279
+ IS NOT DISTINCT FROM incoming_launch_json->'runtimeSandboxPlacementPolicyId'
2280
+ )
2281
+ WHEN existing_launch_json ? 'runtimeSandboxPlacementPolicyId'
2282
+ AND incoming_launch_json ? 'runtimeSandboxPlacementPolicyId'
2283
+ THEN ${quotedSchema}.runtime_launch_admission_identity(
2284
+ existing_launch_json - 'launchIdentityDigest'
2285
+ ) = ${quotedSchema}.runtime_launch_admission_identity(
2286
+ incoming_launch_json - 'launchIdentityDigest'
2287
+ )
2288
+ ELSE ${quotedSchema}.runtime_launch_admission_identity(
2289
+ existing_launch_json - 'launchIdentityDigest'
2290
+ ) - ARRAY['runtimeSandboxPlacementPolicyId', 'launchIdentityDigest']::text[] =
2291
+ ${quotedSchema}.runtime_launch_admission_identity(
2292
+ incoming_launch_json - 'launchIdentityDigest'
2293
+ ) - ARRAY['runtimeSandboxPlacementPolicyId', 'launchIdentityDigest']::text[]
2294
+ END
2295
+ $$
2296
+ `);
2297
+ }
2298
+
2201
2299
  export async function ensurePostgresSchedulerSchema(
2202
2300
  client: PostgresSchedulerQueryClient,
2203
2301
  options?: Pick<PostgresSchedulerOptions, 'schema'> & {
@@ -2332,6 +2430,7 @@ export async function ensurePostgresSchedulerSchema(
2332
2430
  const hasLegacyAdmissionRows =
2333
2431
  legacyAdmissionRows?.rows[0]?.present === true;
2334
2432
  if (schemaCoreIsCurrent) {
2433
+ await ensurePostgresSchedulerLaunchAdmissionFunctions(client, options);
2335
2434
  if (!schemaIsCurrent) {
2336
2435
  await ensurePostgresSchedulerTerminalComputeBillingOutboxTrigger(
2337
2436
  client,
@@ -3703,6 +3802,7 @@ export async function ensurePostgresSchedulerSchema(
3703
3802
  [POSTGRES_SCHEDULER_SCHEMA_VERSION],
3704
3803
  );
3705
3804
  await ensurePostgresSchedulerCronScheduleSchema(client, options);
3805
+ await ensurePostgresSchedulerLaunchAdmissionFunctions(client, options);
3706
3806
  if (hadOutbox.rows[0]?.present !== true) {
3707
3807
  await client.query(
3708
3808
  `
@@ -4409,6 +4509,45 @@ export async function submitPostgresAbsurdPlayRun(
4409
4509
  };
4410
4510
  }
4411
4511
 
4512
+ function launchIdentityConflictFields(
4513
+ existingLaunchJson: unknown,
4514
+ incomingLaunch: PlaySchedulerSubmitInput,
4515
+ ): string[] {
4516
+ if (
4517
+ !existingLaunchJson ||
4518
+ typeof existingLaunchJson !== 'object' ||
4519
+ Array.isArray(existingLaunchJson)
4520
+ ) {
4521
+ return [];
4522
+ }
4523
+ const existingIdentity = buildPlayLaunchIdentity(
4524
+ existingLaunchJson as Record<string, unknown>,
4525
+ );
4526
+ const incomingIdentity = buildPlayLaunchIdentity(incomingLaunch);
4527
+ const fields = Object.keys(incomingIdentity).filter(
4528
+ (field) =>
4529
+ stableStringify(existingIdentity[field]) !==
4530
+ stableStringify(incomingIdentity[field]),
4531
+ );
4532
+ if (
4533
+ Object.prototype.hasOwnProperty.call(
4534
+ existingLaunchJson,
4535
+ 'runtimeSandboxPlacementPolicyId',
4536
+ ) &&
4537
+ Object.prototype.hasOwnProperty.call(
4538
+ incomingLaunch,
4539
+ 'runtimeSandboxPlacementPolicyId',
4540
+ ) &&
4541
+ stableStringify(
4542
+ (existingLaunchJson as Record<string, unknown>)
4543
+ .runtimeSandboxPlacementPolicyId,
4544
+ ) !== stableStringify(incomingLaunch.runtimeSandboxPlacementPolicyId)
4545
+ ) {
4546
+ fields.push('runtimeSandboxPlacementPolicyId');
4547
+ }
4548
+ return fields.sort();
4549
+ }
4550
+
4412
4551
  async function submitPostgresSchedulerRunTransaction(
4413
4552
  client: PostgresSchedulerQueryClient,
4414
4553
  input: PlaySchedulerSubmitInput,
@@ -4447,7 +4586,10 @@ async function submitPostgresSchedulerRunTransaction(
4447
4586
  existingOnly: requestedExistingOnly,
4448
4587
  ...durableInput
4449
4588
  } = input;
4450
- input = durableInput;
4589
+ // Every scheduler writer converges through this seam, including the cron
4590
+ // successor path and Workpool retries. Stamp after ephemeral admission proof
4591
+ // fields are removed so they can never change the durable launch identity.
4592
+ input = withPlayLaunchIdentityDigest(durableInput);
4451
4593
  const existingOnlySql = requestedExistingOnly === true ? 'true' : 'false';
4452
4594
  const releaseId = input.absurdReleaseId?.trim() || null;
4453
4595
  const legacyPreviewRelease =
@@ -4531,6 +4673,7 @@ async function submitPostgresSchedulerRunTransaction(
4531
4673
  run_created: boolean;
4532
4674
  task_created: boolean | null;
4533
4675
  task_recovery_required: boolean;
4676
+ existing_launch_json: unknown | null;
4534
4677
  }>(
4535
4678
  `
4536
4679
  WITH run_lock AS MATERIALIZED (
@@ -4578,6 +4721,7 @@ async function submitPostgresSchedulerRunTransaction(
4578
4721
  coalesce(runs.admission_json, runs.launch_json),
4579
4722
  $8::jsonb
4580
4723
  ) AS matches_launch,
4724
+ coalesce(runs.admission_json, runs.launch_json) AS launch_json,
4581
4725
  runs.absurd_task_id,
4582
4726
  runs.status
4583
4727
  FROM admission_lock
@@ -4784,6 +4928,7 @@ async function submitPostgresSchedulerRunTransaction(
4784
4928
  AND existing_run.absurd_task_id IS NULL
4785
4929
  )
4786
4930
  ) AS task_recovery_required,
4931
+ (SELECT launch_json FROM existing_run) AS existing_launch_json,
4787
4932
  CASE WHEN EXISTS (SELECT 1 FROM run_insert)
4788
4933
  OR EXISTS (SELECT 1 FROM run_recovery)
4789
4934
  THEN pg_notify($10, $11)
@@ -4840,9 +4985,13 @@ async function submitPostgresSchedulerRunTransaction(
4840
4985
  );
4841
4986
  }
4842
4987
  if (row?.accepted === false && row.existing_run && !row.matching_existing) {
4843
- throw new Error(
4844
- `Play run "${input.runId}" already exists with a different launch.`,
4845
- );
4988
+ throw new PlayRunLaunchConflictError(input.runId, {
4989
+ conflictReason: 'launch_identity_mismatch',
4990
+ conflictingFields: launchIdentityConflictFields(
4991
+ row.existing_launch_json,
4992
+ input,
4993
+ ),
4994
+ });
4846
4995
  }
4847
4996
  if (row?.accepted === false && !row?.existing_run && requestedExistingOnly) {
4848
4997
  throw new Error(
@@ -5144,6 +5293,14 @@ export class PostgresSchedulerCronScheduleConfigurationError extends Error {
5144
5293
  }
5145
5294
  }
5146
5295
 
5296
+ /** A deterministic Run identity conflict cannot be repaired by retrying the same fire. */
5297
+ export class PostgresSchedulerCronLaunchConflictError extends PostgresSchedulerCronScheduleConfigurationError {
5298
+ constructor(message: string) {
5299
+ super(message);
5300
+ this.name = 'PostgresSchedulerCronLaunchConflictError';
5301
+ }
5302
+ }
5303
+
5147
5304
  /**
5148
5305
  * The timer stores the facts needed to reconstruct a pinned admission, not a
5149
5306
  * previously prepared launch. Executor tokens, preloaded session keys,
@@ -5488,7 +5645,7 @@ function cronOccurrenceLaunch(input: {
5488
5645
  `Cron schedule "${input.schedule.scheduleId}" did not receive a fresh authority for its pinned occurrence.`,
5489
5646
  );
5490
5647
  }
5491
- return {
5648
+ return withPlayLaunchIdentityDigest({
5492
5649
  ...input.launch,
5493
5650
  schedulerSchema:
5494
5651
  input.schedulerSchema ?? input.launch.schedulerSchema ?? null,
@@ -5501,7 +5658,7 @@ function cronOccurrenceLaunch(input: {
5501
5658
  ),
5502
5659
  triggerSource: 'cron',
5503
5660
  requiresRunCreatedProjection: true,
5504
- };
5661
+ });
5505
5662
  }
5506
5663
 
5507
5664
  export type FirePostgresSchedulerCronScheduleInput = {
@@ -5524,14 +5681,26 @@ export type FirePostgresSchedulerCronScheduleInput = {
5524
5681
  * CTE inserts the immutable Run, its play-run task, run.created fact, next
5525
5682
  * cron-fire task, and next cursor. A task replay after commit sees a changed
5526
5683
  * cursor and is a harmless `superseded` no-op.
5684
+ *
5685
+ * A re-fire of the same occurrence whose deterministic Run already exists is
5686
+ * an idempotent re-fire, not a no-op. The cursor advances when the existing
5687
+ * launch identity matches, even if this statement did not create the Run.
5688
+ * A genuine identity mismatch is returned as `stalled` so it cannot silently
5689
+ * freeze the schedule behind an indistinguishable replay.
5527
5690
  */
5528
5691
  export async function firePostgresSchedulerCronSchedule(
5529
5692
  client: PostgresSchedulerQueryClient,
5530
5693
  input: FirePostgresSchedulerCronScheduleInput,
5531
5694
  options?: Pick<PostgresSchedulerOptions, 'schema'>,
5532
5695
  ): Promise<
5533
- | { status: 'admitted'; runId: string; successorDueAtMs: number }
5696
+ | {
5697
+ status: 'admitted';
5698
+ runId: string;
5699
+ successorDueAtMs: number;
5700
+ reused: boolean;
5701
+ }
5534
5702
  | { status: 'superseded' }
5703
+ | { status: 'stalled'; runId: string; reason: string }
5535
5704
  > {
5536
5705
  assertPostgresSchedulerCronScheduleInput(input);
5537
5706
  const schedule = await readPostgresSchedulerCronSchedule(
@@ -5598,7 +5767,11 @@ export async function firePostgresSchedulerCronSchedule(
5598
5767
  orgId: launch.orgId,
5599
5768
  });
5600
5769
  const result = await client.query<{
5601
- admitted: boolean;
5770
+ advanced: boolean;
5771
+ run_created: boolean;
5772
+ schedule_current: boolean;
5773
+ existing_run: boolean;
5774
+ matching_existing: boolean;
5602
5775
  release_accepting: boolean;
5603
5776
  }>(
5604
5777
  `
@@ -5623,16 +5796,28 @@ export async function firePostgresSchedulerCronSchedule(
5623
5796
  AND release_gate.accepting
5624
5797
  FOR UPDATE
5625
5798
  ), existing_run AS MATERIALIZED (
5626
- SELECT runs.run_id
5799
+ SELECT runs.run_id,
5800
+ ${quoteIdent(schemaName(options))}.runtime_launch_admission_matches(
5801
+ coalesce(runs.admission_json, runs.launch_json),
5802
+ $6::jsonb
5803
+ ) AS matches_launch
5627
5804
  FROM matching_schedule
5628
5805
  JOIN ${tableName('runs', options)} AS runs ON runs.run_id = $5::text
5806
+ ), occurrence AS MATERIALIZED (
5807
+ SELECT EXISTS (SELECT 1 FROM matching_schedule) AS schedule_current,
5808
+ EXISTS (SELECT 1 FROM existing_run) AS existing_run,
5809
+ coalesce(
5810
+ (SELECT matches_launch FROM existing_run),
5811
+ false
5812
+ ) AS matching_existing
5629
5813
  ), play_task_request AS (
5630
5814
  SELECT $7::text AS queue_name,
5631
5815
  $8::text AS task_name,
5632
5816
  $9::jsonb AS params,
5633
5817
  $10::jsonb AS options
5634
- WHERE EXISTS (SELECT 1 FROM matching_schedule)
5635
- AND NOT EXISTS (SELECT 1 FROM existing_run)
5818
+ FROM occurrence
5819
+ WHERE occurrence.schedule_current
5820
+ AND NOT occurrence.existing_run
5636
5821
  ), play_task_spawn AS MATERIALIZED (
5637
5822
  SELECT spawned.task_id::text AS task_id
5638
5823
  FROM play_task_request
@@ -5668,15 +5853,25 @@ export async function firePostgresSchedulerCronSchedule(
5668
5853
  'coordinatorInternalToken',
5669
5854
  'vercelProtectionBypassToken'
5670
5855
  ]::text[]
5671
- WHERE EXISTS (SELECT 1 FROM matching_schedule)
5672
- AND NOT EXISTS (SELECT 1 FROM existing_run)
5856
+ FROM occurrence
5857
+ WHERE occurrence.schedule_current
5858
+ AND NOT occurrence.existing_run
5673
5859
  RETURNING run_id
5860
+ ), occurrence_admitted AS MATERIALIZED (
5861
+ SELECT true AS admitted
5862
+ FROM occurrence
5863
+ WHERE EXISTS (SELECT 1 FROM run_insert)
5864
+ OR (
5865
+ occurrence.schedule_current
5866
+ AND occurrence.existing_run
5867
+ AND occurrence.matching_existing
5868
+ )
5674
5869
  ), successor_task_request AS (
5675
5870
  SELECT $11::text AS queue_name,
5676
5871
  $12::text AS task_name,
5677
5872
  $13::jsonb AS params,
5678
5873
  $14::jsonb AS options
5679
- WHERE EXISTS (SELECT 1 FROM run_insert)
5874
+ WHERE EXISTS (SELECT 1 FROM occurrence_admitted)
5680
5875
  ), successor_task_spawn AS MATERIALIZED (
5681
5876
  SELECT spawned.task_id::text AS task_id
5682
5877
  FROM successor_task_request
@@ -5750,10 +5945,14 @@ export async function firePostgresSchedulerCronSchedule(
5750
5945
  AND schedule.state = 'active'
5751
5946
  AND schedule.current_due_at = to_timestamp($3::double precision / 1000.0)
5752
5947
  AND schedule.current_fire_generation = $15::bigint
5753
- AND EXISTS (SELECT 1 FROM run_insert)
5948
+ AND EXISTS (SELECT 1 FROM occurrence_admitted)
5754
5949
  RETURNING schedule_id
5755
5950
  )
5756
- SELECT EXISTS (SELECT 1 FROM schedule_advance) AS admitted,
5951
+ SELECT EXISTS (SELECT 1 FROM schedule_advance) AS advanced,
5952
+ EXISTS (SELECT 1 FROM run_insert) AS run_created,
5953
+ (SELECT schedule_current FROM occurrence) AS schedule_current,
5954
+ (SELECT existing_run FROM occurrence) AS existing_run,
5955
+ (SELECT matching_existing FROM occurrence) AS matching_existing,
5757
5956
  coalesce(
5758
5957
  (SELECT accepting
5759
5958
  FROM ${tableName('runtime_release_admission', options)}
@@ -5780,19 +5979,34 @@ export async function firePostgresSchedulerCronSchedule(
5780
5979
  releaseAdmissionKey,
5781
5980
  ],
5782
5981
  );
5783
- if (result.rows[0]?.release_accepting !== true) {
5982
+ const fired = result.rows[0];
5983
+ if (fired?.release_accepting !== true) {
5784
5984
  throw new Error(
5785
5985
  `Cron schedule "${schedule.scheduleId}" cannot fire because Absurd release "${releaseAdmissionKey}" is not accepting admissions.`,
5786
5986
  );
5787
5987
  }
5788
- if (result.rows[0]?.admitted !== true) {
5789
- return { status: 'superseded' };
5988
+ if (fired.advanced === true) {
5989
+ return {
5990
+ status: 'admitted',
5991
+ runId: launch.runId,
5992
+ successorDueAtMs,
5993
+ reused: fired.run_created !== true,
5994
+ };
5790
5995
  }
5791
- return {
5792
- status: 'admitted',
5793
- runId: launch.runId,
5794
- successorDueAtMs,
5795
- };
5996
+ if (
5997
+ fired.schedule_current === true &&
5998
+ fired.existing_run === true &&
5999
+ fired.matching_existing !== true
6000
+ ) {
6001
+ return {
6002
+ status: 'stalled',
6003
+ runId: launch.runId,
6004
+ reason:
6005
+ `Cron schedule "${schedule.scheduleId}" generation ${schedule.generation} cannot advance past ` +
6006
+ `occurrence ${schedule.dueAtMs}: Run "${launch.runId}" already exists with a different launch identity.`,
6007
+ };
6008
+ }
6009
+ return { status: 'superseded' };
5796
6010
  }
5797
6011
 
5798
6012
  /** Fence a malformed/invalid timer so it cannot repeatedly consume workers. */
package/dist/cli/index.js CHANGED
@@ -3051,7 +3051,7 @@ var SDK_RELEASE = {
3051
3051
  // getters keep their established compatibility behavior.
3052
3052
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3053
3053
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3054
- version: "0.3.185",
3054
+ version: "0.3.186",
3055
3055
  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.",
3056
3056
  packageCapabilities: {
3057
3057
  updatePreferences: 1
@@ -27485,7 +27485,9 @@ function readLiveCronMetadata(play) {
27485
27485
  return {
27486
27486
  schedule: typeof metadata?.cronSchedule === "string" ? metadata.cronSchedule : null,
27487
27487
  timezone: typeof metadata?.cronTimezone === "string" ? metadata.cronTimezone : null,
27488
- nextScheduledAt: typeof metadata?.nextScheduledAt === "number" ? metadata.nextScheduledAt : null
27488
+ nextScheduledAt: typeof metadata?.nextScheduledAt === "number" ? metadata.nextScheduledAt : null,
27489
+ blockedAt: typeof metadata?.blockedAt === "number" ? metadata.blockedAt : null,
27490
+ blockedReasonCode: typeof metadata?.blockedReasonCode === "string" ? metadata.blockedReasonCode : null
27489
27491
  };
27490
27492
  }
27491
27493
  function liveTriggerStatusLines(status, play) {
@@ -27504,6 +27506,18 @@ function liveTriggerStatusLines(status, play) {
27504
27506
  }
27505
27507
  if (status?.blockedReason) {
27506
27508
  lines.push(` Schedule issue: ${status.blockedReason}`);
27509
+ if (status.blockedReasonCode || cron.blockedReasonCode) {
27510
+ lines.push(
27511
+ ` Schedule issue code: ${status.blockedReasonCode ?? cron.blockedReasonCode}`
27512
+ );
27513
+ }
27514
+ const blockedAt = status.blockedAt ?? cron.blockedAt;
27515
+ if (blockedAt !== null && blockedAt !== void 0) {
27516
+ lines.push(` Schedule blocked at: ${formatTimestamp(blockedAt)}`);
27517
+ }
27518
+ lines.push(
27519
+ " Recovery: republish the live revision to re-arm this schedule after the platform issue is fixed."
27520
+ );
27507
27521
  }
27508
27522
  }
27509
27523
  if (status?.webhook) {
@@ -27516,7 +27530,9 @@ function triggerStatusFromMetadata(value) {
27516
27530
  return {
27517
27531
  cron: typeof metadata?.cronStatus === "string" ? metadata.cronStatus : null,
27518
27532
  webhook: typeof metadata?.webhookStatus === "string" ? metadata.webhookStatus : null,
27519
- blockedReason: typeof metadata?.blockedReason === "string" ? metadata.blockedReason : null
27533
+ blockedReason: typeof metadata?.blockedReason === "string" ? metadata.blockedReason : null,
27534
+ blockedReasonCode: typeof metadata?.blockedReasonCode === "string" ? metadata.blockedReasonCode : null,
27535
+ blockedAt: typeof metadata?.blockedAt === "number" ? metadata.blockedAt : null
27520
27536
  };
27521
27537
  }
27522
27538
  function printPublishReceipt(input2) {
@@ -29318,6 +29334,16 @@ async function handlePlayList(args) {
29318
29334
  );
29319
29335
  if (play.triggerStatus.blockedReason) {
29320
29336
  lines.push(` schedule issue: ${play.triggerStatus.blockedReason}`);
29337
+ if (play.triggerStatus.blockedReasonCode) {
29338
+ lines.push(
29339
+ ` schedule issue code: ${play.triggerStatus.blockedReasonCode}`
29340
+ );
29341
+ }
29342
+ if (play.triggerStatus.blockedAt !== void 0) {
29343
+ lines.push(
29344
+ ` schedule blocked at: ${formatTimestamp(play.triggerStatus.blockedAt)}`
29345
+ );
29346
+ }
29321
29347
  }
29322
29348
  } else {
29323
29349
  lines.push(" scheduled execution: not configured");
@@ -3046,7 +3046,7 @@ var SDK_RELEASE = {
3046
3046
  // getters keep their established compatibility behavior.
3047
3047
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
3048
3048
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
3049
- version: "0.3.185",
3049
+ version: "0.3.186",
3050
3050
  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.",
3051
3051
  packageCapabilities: {
3052
3052
  updatePreferences: 1
@@ -27557,7 +27557,9 @@ function readLiveCronMetadata(play) {
27557
27557
  return {
27558
27558
  schedule: typeof metadata?.cronSchedule === "string" ? metadata.cronSchedule : null,
27559
27559
  timezone: typeof metadata?.cronTimezone === "string" ? metadata.cronTimezone : null,
27560
- nextScheduledAt: typeof metadata?.nextScheduledAt === "number" ? metadata.nextScheduledAt : null
27560
+ nextScheduledAt: typeof metadata?.nextScheduledAt === "number" ? metadata.nextScheduledAt : null,
27561
+ blockedAt: typeof metadata?.blockedAt === "number" ? metadata.blockedAt : null,
27562
+ blockedReasonCode: typeof metadata?.blockedReasonCode === "string" ? metadata.blockedReasonCode : null
27561
27563
  };
27562
27564
  }
27563
27565
  function liveTriggerStatusLines(status, play) {
@@ -27576,6 +27578,18 @@ function liveTriggerStatusLines(status, play) {
27576
27578
  }
27577
27579
  if (status?.blockedReason) {
27578
27580
  lines.push(` Schedule issue: ${status.blockedReason}`);
27581
+ if (status.blockedReasonCode || cron.blockedReasonCode) {
27582
+ lines.push(
27583
+ ` Schedule issue code: ${status.blockedReasonCode ?? cron.blockedReasonCode}`
27584
+ );
27585
+ }
27586
+ const blockedAt = status.blockedAt ?? cron.blockedAt;
27587
+ if (blockedAt !== null && blockedAt !== void 0) {
27588
+ lines.push(` Schedule blocked at: ${formatTimestamp(blockedAt)}`);
27589
+ }
27590
+ lines.push(
27591
+ " Recovery: republish the live revision to re-arm this schedule after the platform issue is fixed."
27592
+ );
27579
27593
  }
27580
27594
  }
27581
27595
  if (status?.webhook) {
@@ -27588,7 +27602,9 @@ function triggerStatusFromMetadata(value) {
27588
27602
  return {
27589
27603
  cron: typeof metadata?.cronStatus === "string" ? metadata.cronStatus : null,
27590
27604
  webhook: typeof metadata?.webhookStatus === "string" ? metadata.webhookStatus : null,
27591
- blockedReason: typeof metadata?.blockedReason === "string" ? metadata.blockedReason : null
27605
+ blockedReason: typeof metadata?.blockedReason === "string" ? metadata.blockedReason : null,
27606
+ blockedReasonCode: typeof metadata?.blockedReasonCode === "string" ? metadata.blockedReasonCode : null,
27607
+ blockedAt: typeof metadata?.blockedAt === "number" ? metadata.blockedAt : null
27592
27608
  };
27593
27609
  }
27594
27610
  function printPublishReceipt(input2) {
@@ -29390,6 +29406,16 @@ async function handlePlayList(args) {
29390
29406
  );
29391
29407
  if (play.triggerStatus.blockedReason) {
29392
29408
  lines.push(` schedule issue: ${play.triggerStatus.blockedReason}`);
29409
+ if (play.triggerStatus.blockedReasonCode) {
29410
+ lines.push(
29411
+ ` schedule issue code: ${play.triggerStatus.blockedReasonCode}`
29412
+ );
29413
+ }
29414
+ if (play.triggerStatus.blockedAt !== void 0) {
29415
+ lines.push(
29416
+ ` schedule blocked at: ${formatTimestamp(play.triggerStatus.blockedAt)}`
29417
+ );
29418
+ }
29393
29419
  }
29394
29420
  } else {
29395
29421
  lines.push(" scheduled execution: not configured");
package/dist/index.d.mts CHANGED
@@ -2207,6 +2207,8 @@ interface PlayDefinitionDetail {
2207
2207
  cronTimezone?: string | null;
2208
2208
  nextScheduledAt?: number | null;
2209
2209
  lastScheduledAt?: number | null;
2210
+ blockedAt?: number | null;
2211
+ blockedReasonCode?: string | null;
2210
2212
  blockedReason?: string | null;
2211
2213
  [key: string]: unknown;
2212
2214
  } | null;
@@ -2270,6 +2272,8 @@ interface PlayTriggerStatus {
2270
2272
  cron: string | null;
2271
2273
  webhook: string | null;
2272
2274
  blockedReason: string | null;
2275
+ blockedReasonCode?: string;
2276
+ blockedAt?: number;
2273
2277
  }
2274
2278
  interface ProductNotificationEventDefinition {
2275
2279
  id: string;
package/dist/index.d.ts CHANGED
@@ -2207,6 +2207,8 @@ interface PlayDefinitionDetail {
2207
2207
  cronTimezone?: string | null;
2208
2208
  nextScheduledAt?: number | null;
2209
2209
  lastScheduledAt?: number | null;
2210
+ blockedAt?: number | null;
2211
+ blockedReasonCode?: string | null;
2210
2212
  blockedReason?: string | null;
2211
2213
  [key: string]: unknown;
2212
2214
  } | null;
@@ -2270,6 +2272,8 @@ interface PlayTriggerStatus {
2270
2272
  cron: string | null;
2271
2273
  webhook: string | null;
2272
2274
  blockedReason: string | null;
2275
+ blockedReasonCode?: string;
2276
+ blockedAt?: number;
2273
2277
  }
2274
2278
  interface ProductNotificationEventDefinition {
2275
2279
  id: string;
package/dist/index.js CHANGED
@@ -864,7 +864,7 @@ var SDK_RELEASE = {
864
864
  // getters keep their established compatibility behavior.
865
865
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
866
866
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
867
- version: "0.3.185",
867
+ version: "0.3.186",
868
868
  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.",
869
869
  packageCapabilities: {
870
870
  updatePreferences: 1
package/dist/index.mjs CHANGED
@@ -768,7 +768,7 @@ var SDK_RELEASE = {
768
768
  // getters keep their established compatibility behavior.
769
769
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
770
770
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
771
- version: "0.3.185",
771
+ version: "0.3.186",
772
772
  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.",
773
773
  packageCapabilities: {
774
774
  updatePreferences: 1
@@ -209,6 +209,7 @@
209
209
  "dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts",
210
210
  "dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts",
211
211
  "dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts",
212
+ "dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts",
212
213
  "dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts",
213
214
  "dist/bundling-sources/shared_libs/play-runtime/lease-policy.ts",
214
215
  "dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts",
@@ -149,7 +149,7 @@ type SdkRelease = {
149
149
  supportPolicy: SdkSupportPolicy;
150
150
  };
151
151
  declare const SDK_RELEASE: {
152
- readonly version: "0.3.185";
152
+ readonly version: "0.3.186";
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.185";
152
+ readonly version: "0.3.186";
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.185",
77
+ version: "0.3.186",
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.185",
51
+ version: "0.3.186",
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.185",
3
+ "version": "0.3.186",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",