deepline 0.1.248 → 0.1.250

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.
@@ -80,6 +80,13 @@ import type {
80
80
  import type { PlayStagedFileRef } from './plays/local-file-discovery.js';
81
81
  import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-manifest.js';
82
82
  import type { EnrichCompiledConfig } from './cli/enrich-play-compiler.js';
83
+ import { RUNTIME_ENVIRONMENT_TOKEN_HEADER } from '../../shared_libs/play-runtime/coordinator-headers.js';
84
+ import {
85
+ normalizePlayRuntimeEnvironment,
86
+ normalizePlayRuntimeNamespace,
87
+ normalizePlayRuntimeSelection,
88
+ type PlayRuntimeSelection,
89
+ } from '../../shared_libs/play-runtime/runtime-environment.js';
83
90
 
84
91
  const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
85
92
  const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
@@ -109,6 +116,75 @@ function resolvePlayRunIntegrationMode(
109
116
  );
110
117
  }
111
118
 
119
+ function resolvePlayRunRuntimeSelection(
120
+ request: StartPlayRunRequest,
121
+ ): PlayRuntimeSelection | undefined {
122
+ if (request.runtime !== undefined) {
123
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
124
+ if (!runtime) {
125
+ throw new DeeplineError(
126
+ 'runtime must be exactly { environment: "preview", namespace } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
127
+ undefined,
128
+ 'INVALID_RUNTIME_SELECTION',
129
+ );
130
+ }
131
+ return runtime;
132
+ }
133
+ // These variables select where the app executes a run, not which app the
134
+ // SDK calls. DEEPLINE_API_BASE_URL still chooses the app host. Environment
135
+ // opts into remote preview routing, namespace names the isolated lane, and
136
+ // the token only authorizes that selection. Omitting all three leaves the
137
+ // app free to use its native runtime (local for a local app, prod for prod).
138
+ const configured = process.env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
139
+ const configuredNamespace = process.env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
140
+ const configuredToken =
141
+ process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
142
+ if (!configured) {
143
+ if (configuredNamespace || configuredToken) {
144
+ throw new DeeplineError(
145
+ 'DEEPLINE_RUNTIME_ENVIRONMENT=preview and DEEPLINE_RUNTIME_NAMESPACE are required when runtime selection configuration is present.',
146
+ undefined,
147
+ 'INVALID_RUNTIME_ENVIRONMENT',
148
+ );
149
+ }
150
+ return undefined;
151
+ }
152
+ const environment = normalizePlayRuntimeEnvironment(configured);
153
+ if (!environment) {
154
+ throw new DeeplineError(
155
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
156
+ undefined,
157
+ 'INVALID_RUNTIME_ENVIRONMENT',
158
+ );
159
+ }
160
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
161
+ if (!namespace) {
162
+ throw new DeeplineError(
163
+ 'DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.',
164
+ undefined,
165
+ 'INVALID_RUNTIME_NAMESPACE',
166
+ );
167
+ }
168
+ return { environment, namespace };
169
+ }
170
+
171
+ function runtimeSelectionHeaders(
172
+ runtime: PlayRuntimeSelection | undefined,
173
+ ): Record<string, string> | undefined {
174
+ if (!runtime) return undefined;
175
+ // Keep the credential out of the typed runtime payload. The app consumes it
176
+ // at the HTTP boundary and never forwards it to Fly, Absurd, or Daytona.
177
+ const token = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
178
+ if (!token) {
179
+ throw new DeeplineError(
180
+ 'DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.',
181
+ undefined,
182
+ 'RUNTIME_ENVIRONMENT_TOKEN_REQUIRED',
183
+ );
184
+ }
185
+ return { [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token };
186
+ }
187
+
112
188
  function normalizeTestPolicyOverrides(
113
189
  value: unknown,
114
190
  source: string,
@@ -1577,6 +1653,7 @@ export class DeeplineClient {
1577
1653
  const testPolicyOverrides =
1578
1654
  request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
1579
1655
  const forceToolRefresh = request.forceToolRefresh === true;
1656
+ const runtime = resolvePlayRunRuntimeSelection(request);
1580
1657
  const response = await this.http.post<Record<string, unknown>>(
1581
1658
  '/api/v2/plays/run',
1582
1659
  {
@@ -1619,9 +1696,10 @@ export class DeeplineClient {
1619
1696
  // defaults to absurd; callers normally omit this field.
1620
1697
  ...(request.profile ? { profile: request.profile } : {}),
1621
1698
  ...(integrationMode ? { integrationMode } : {}),
1699
+ ...(runtime ? { runtime } : {}),
1622
1700
  ...(testPolicyOverrides ? { testPolicyOverrides } : {}),
1623
1701
  },
1624
- undefined,
1702
+ runtimeSelectionHeaders(runtime),
1625
1703
  // A start can reach the server even when its response is lost or times
1626
1704
  // out. Retrying this mutation without an idempotency key can create a
1627
1705
  // second root run, so callers must resolve ambiguous failures explicitly.
@@ -1649,6 +1727,7 @@ export class DeeplineClient {
1649
1727
  const testPolicyOverrides =
1650
1728
  request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
1651
1729
  const forceToolRefresh = request.forceToolRefresh === true;
1730
+ const runtime = resolvePlayRunRuntimeSelection(request);
1652
1731
  const body = {
1653
1732
  ...(request.name ? { name: request.name } : {}),
1654
1733
  ...(request.revisionId ? { revisionId: request.revisionId } : {}),
@@ -1687,6 +1766,7 @@ export class DeeplineClient {
1687
1766
  : {}),
1688
1767
  ...(request.profile ? { profile: request.profile } : {}),
1689
1768
  ...(integrationMode ? { integrationMode } : {}),
1769
+ ...(runtime ? { runtime } : {}),
1690
1770
  ...(testPolicyOverrides ? { testPolicyOverrides } : {}),
1691
1771
  };
1692
1772
  for await (const event of this.http.streamSse<PlayLiveEvent>(
@@ -1694,6 +1774,7 @@ export class DeeplineClient {
1694
1774
  {
1695
1775
  method: 'POST',
1696
1776
  body,
1777
+ headers: runtimeSelectionHeaders(runtime),
1697
1778
  signal: options?.signal,
1698
1779
  },
1699
1780
  )) {
@@ -113,7 +113,7 @@ export const SDK_RELEASE = {
113
113
  // runtime intentionally no longer compiles at publish or launch time.
114
114
  // 0.1.242 removes the retired Workers/ESM compiler, generated bundles, and
115
115
  // deploy-time artifact migration. Play authoring now has one CJS contract.
116
- version: '0.1.248',
116
+ version: '0.1.250',
117
117
  apiContract: '2026-07-cjs-absurd-only-play-runtime-hard-cutover',
118
118
  supportPolicy: {
119
119
  minimumSupported: '0.1.53',
@@ -1,4 +1,5 @@
1
1
  import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-manifest';
2
+ import type { PlayRuntimeSelection } from '../../shared_libs/play-runtime/runtime-environment';
2
3
  import type {
3
4
  DeeplineToolCategory,
4
5
  PlayBootstrapFinderKind,
@@ -1226,6 +1227,8 @@ export interface StartPlayRunRequest {
1226
1227
  profile?: string;
1227
1228
  /** Optional per-run provider execution mode for eval/smoke runs. */
1228
1229
  integrationMode?: 'live' | 'eval_stub' | 'fixture';
1230
+ /** Internal runtime estate selection. The app host remains unchanged. */
1231
+ runtime?: PlayRuntimeSelection;
1229
1232
  /** Internal/dev-only runtime policy overrides for black-box durability tests. */
1230
1233
  testPolicyOverrides?: Record<string, unknown>;
1231
1234
  }
@@ -6997,7 +6997,9 @@ export class PlayContextImpl {
6997
6997
  Object.fromEntries(response.headers.entries()),
6998
6998
  ) as Record<string, string>,
6999
6999
  bodyText: redactedBodyText,
7000
- json: this.secretRedactor.redact(parseJsonOrNull(bodyText)),
7000
+ json: this.secretRedactor.redactKnownSecrets(
7001
+ parseJsonOrNull(bodyText),
7002
+ ),
7001
7003
  };
7002
7004
 
7003
7005
  this.checkpoint.resolvedBoundaries = {
@@ -28,6 +28,13 @@ export const WORKER_CALLBACK_URL_OVERRIDE_HEADER =
28
28
  'x-deepline-worker-callback-url';
29
29
  export const RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER =
30
30
  'x-deepline-runtime-scheduler-schema';
31
+ /**
32
+ * CLI-to-app credential used only to authorize a typed runtime-environment
33
+ * selection. It is deliberately separate from the coordinator credential:
34
+ * selecting an approved environment must not grant access to runtime callbacks.
35
+ */
36
+ export const RUNTIME_ENVIRONMENT_TOKEN_HEADER =
37
+ 'x-deepline-runtime-environment-token';
31
38
  /**
32
39
  * Internal top-level `profile=absurd` launches, including deploy canaries, use
33
40
  * this header to pin an exact candidate release before activation. Public API
@@ -27,10 +27,17 @@ export function daytonaClientOptions(apiKey: string): DaytonaClientOptions {
27
27
  };
28
28
  }
29
29
 
30
- export function loadDaytonaRequiredConfig(): DaytonaRequiredConfig {
31
- const apiKey = process.env.DAYTONA_API_KEY?.trim();
30
+ export function loadDaytonaRequiredConfig(
31
+ options: {
32
+ environment?: 'preview' | 'production' | null;
33
+ } = {},
34
+ ): DaytonaRequiredConfig {
35
+ const environment = options.environment ?? null;
36
+ const keyName =
37
+ environment === 'preview' ? 'DAYTONA_API_KEY_PREVIEW' : 'DAYTONA_API_KEY';
38
+ const apiKey = process.env[keyName]?.trim();
32
39
  if (!apiKey) {
33
- throw new Error('Missing required Daytona configuration: DAYTONA_API_KEY.');
40
+ throw new Error(`Missing required Daytona configuration: ${keyName}.`);
34
41
  }
35
42
  return {
36
43
  apiKey,
@@ -624,6 +624,10 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
624
624
  await callbacks?.onRuntimeResourceAcquired?.({
625
625
  kind: 'daytona_sandbox',
626
626
  sandboxId: sandbox.id,
627
+ daytonaEnvironment:
628
+ process.env.DEEPLINE_RUNTIME_ENVIRONMENT === 'preview'
629
+ ? 'preview'
630
+ : 'production',
627
631
  billingStartedAt: acquired.billingStartedAt,
628
632
  billingEndedAt,
629
633
  cpu: typeof sandbox.cpu === 'number' ? sandbox.cpu : null,
@@ -12,6 +12,7 @@ import type {
12
12
  export type PlayRunnerRuntimeResource = {
13
13
  kind: 'daytona_sandbox';
14
14
  sandboxId: string;
15
+ daytonaEnvironment?: 'preview' | 'production';
15
16
  billingStartedAt: number;
16
17
  billingEndedAt?: number | null;
17
18
  terminalReason?: RuntimeResourceTerminalReason | null;
@@ -0,0 +1,44 @@
1
+ export const PLAY_RUNTIME_ENVIRONMENTS = ['preview'] as const;
2
+
3
+ export type PlayRuntimeEnvironment = (typeof PLAY_RUNTIME_ENVIRONMENTS)[number];
4
+
5
+ export type PlayRuntimeSelection = {
6
+ environment: 'preview';
7
+ /** Caller-named isolation scope inside a remote runtime environment. */
8
+ namespace: string;
9
+ };
10
+
11
+ const PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
12
+
13
+ export function normalizePlayRuntimeNamespace(value: unknown): string | null {
14
+ if (typeof value !== 'string') return null;
15
+ const normalized = value.trim();
16
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
17
+ }
18
+
19
+ /** Validate the selector as one value so the SDK and server cannot drift. */
20
+ export function normalizePlayRuntimeSelection(
21
+ value: unknown,
22
+ ): PlayRuntimeSelection | null {
23
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
24
+ const record = value as Record<string, unknown>;
25
+ if (
26
+ Object.keys(record).some(
27
+ (key) => key !== 'environment' && key !== 'namespace',
28
+ ) ||
29
+ record.environment !== 'preview'
30
+ ) {
31
+ return null;
32
+ }
33
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
34
+ return namespace ? { environment: 'preview', namespace } : null;
35
+ }
36
+
37
+ export function normalizePlayRuntimeEnvironment(
38
+ value: unknown,
39
+ ): PlayRuntimeEnvironment | null {
40
+ return typeof value === 'string' &&
41
+ PLAY_RUNTIME_ENVIRONMENTS.includes(value as PlayRuntimeEnvironment)
42
+ ? (value as PlayRuntimeEnvironment)
43
+ : null;
44
+ }
@@ -76,6 +76,7 @@ export function redactSecretLikeString(value: string): string {
76
76
  export type SecretRedactionContext = {
77
77
  register(value: string): void;
78
78
  redactString(value: string): string;
79
+ redactKnownSecrets<T>(value: T): T;
79
80
  redact<T>(value: T): T;
80
81
  };
81
82
 
@@ -129,9 +130,26 @@ export function createSecretRedactionContext(
129
130
  return value;
130
131
  }
131
132
 
133
+ function redactKnownSecrets<T>(value: T): T {
134
+ if (typeof value === 'string') return redactString(value) as T;
135
+ if (Array.isArray(value)) {
136
+ return value.map((entry) => redactKnownSecrets(entry)) as T;
137
+ }
138
+ if (value && typeof value === 'object') {
139
+ return Object.fromEntries(
140
+ Object.entries(value).map(([key, entry]) => [
141
+ key,
142
+ redactKnownSecrets(entry),
143
+ ]),
144
+ ) as T;
145
+ }
146
+ return value;
147
+ }
148
+
132
149
  return {
133
150
  register,
134
151
  redactString,
152
+ redactKnownSecrets,
135
153
  redact,
136
154
  };
137
155
  }
package/dist/cli/index.js CHANGED
@@ -632,7 +632,7 @@ var SDK_RELEASE = {
632
632
  // runtime intentionally no longer compiles at publish or launch time.
633
633
  // 0.1.242 removes the retired Workers/ESM compiler, generated bundles, and
634
634
  // deploy-time artifact migration. Play authoring now has one CJS contract.
635
- version: "0.1.248",
635
+ version: "0.1.250",
636
636
  apiContract: "2026-07-cjs-absurd-only-play-runtime-hard-cutover",
637
637
  supportPolicy: {
638
638
  minimumSupported: "0.1.53",
@@ -790,6 +790,7 @@ var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
790
790
  var COORDINATOR_URL_OVERRIDE_HEADER = "x-deepline-coordinator-url";
791
791
  var WORKER_CALLBACK_URL_OVERRIDE_HEADER = "x-deepline-worker-callback-url";
792
792
  var RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER = "x-deepline-runtime-scheduler-schema";
793
+ var RUNTIME_ENVIRONMENT_TOKEN_HEADER = "x-deepline-runtime-environment-token";
793
794
  var ABSURD_RELEASE_OVERRIDE_HEADER = "x-deepline-absurd-release";
794
795
  var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
795
796
 
@@ -1454,9 +1455,25 @@ function createSecretRedactionContext(initialValues = []) {
1454
1455
  }
1455
1456
  return value;
1456
1457
  }
1458
+ function redactKnownSecrets(value) {
1459
+ if (typeof value === "string") return redactString(value);
1460
+ if (Array.isArray(value)) {
1461
+ return value.map((entry) => redactKnownSecrets(entry));
1462
+ }
1463
+ if (value && typeof value === "object") {
1464
+ return Object.fromEntries(
1465
+ Object.entries(value).map(([key, entry]) => [
1466
+ key,
1467
+ redactKnownSecrets(entry)
1468
+ ])
1469
+ );
1470
+ }
1471
+ return value;
1472
+ }
1457
1473
  return {
1458
1474
  register,
1459
1475
  redactString,
1476
+ redactKnownSecrets,
1460
1477
  redact
1461
1478
  };
1462
1479
  }
@@ -2363,6 +2380,29 @@ async function* observeRunEvents(options) {
2363
2380
  }
2364
2381
  }
2365
2382
 
2383
+ // ../shared_libs/play-runtime/runtime-environment.ts
2384
+ var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
2385
+ var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
2386
+ function normalizePlayRuntimeNamespace(value) {
2387
+ if (typeof value !== "string") return null;
2388
+ const normalized = value.trim();
2389
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
2390
+ }
2391
+ function normalizePlayRuntimeSelection(value) {
2392
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2393
+ const record = value;
2394
+ if (Object.keys(record).some(
2395
+ (key) => key !== "environment" && key !== "namespace"
2396
+ ) || record.environment !== "preview") {
2397
+ return null;
2398
+ }
2399
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
2400
+ return namespace ? { environment: "preview", namespace } : null;
2401
+ }
2402
+ function normalizePlayRuntimeEnvironment(value) {
2403
+ return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
2404
+ }
2405
+
2366
2406
  // src/client.ts
2367
2407
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
2368
2408
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -2385,6 +2425,61 @@ function resolvePlayRunIntegrationMode(request) {
2385
2425
  request.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE
2386
2426
  );
2387
2427
  }
2428
+ function resolvePlayRunRuntimeSelection(request) {
2429
+ if (request.runtime !== void 0) {
2430
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
2431
+ if (!runtime) {
2432
+ throw new DeeplineError(
2433
+ 'runtime must be exactly { environment: "preview", namespace } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
2434
+ void 0,
2435
+ "INVALID_RUNTIME_SELECTION"
2436
+ );
2437
+ }
2438
+ return runtime;
2439
+ }
2440
+ const configured = process.env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
2441
+ const configuredNamespace = process.env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
2442
+ const configuredToken = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2443
+ if (!configured) {
2444
+ if (configuredNamespace || configuredToken) {
2445
+ throw new DeeplineError(
2446
+ "DEEPLINE_RUNTIME_ENVIRONMENT=preview and DEEPLINE_RUNTIME_NAMESPACE are required when runtime selection configuration is present.",
2447
+ void 0,
2448
+ "INVALID_RUNTIME_ENVIRONMENT"
2449
+ );
2450
+ }
2451
+ return void 0;
2452
+ }
2453
+ const environment = normalizePlayRuntimeEnvironment(configured);
2454
+ if (!environment) {
2455
+ throw new DeeplineError(
2456
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
2457
+ void 0,
2458
+ "INVALID_RUNTIME_ENVIRONMENT"
2459
+ );
2460
+ }
2461
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
2462
+ if (!namespace) {
2463
+ throw new DeeplineError(
2464
+ "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
2465
+ void 0,
2466
+ "INVALID_RUNTIME_NAMESPACE"
2467
+ );
2468
+ }
2469
+ return { environment, namespace };
2470
+ }
2471
+ function runtimeSelectionHeaders(runtime) {
2472
+ if (!runtime) return void 0;
2473
+ const token = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2474
+ if (!token) {
2475
+ throw new DeeplineError(
2476
+ "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
2477
+ void 0,
2478
+ "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
2479
+ );
2480
+ }
2481
+ return { [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token };
2482
+ }
2388
2483
  function normalizeTestPolicyOverrides(value, source) {
2389
2484
  if (value && typeof value === "object" && !Array.isArray(value)) {
2390
2485
  return value;
@@ -3083,6 +3178,7 @@ var DeeplineClient = class {
3083
3178
  const integrationMode = resolvePlayRunIntegrationMode(request);
3084
3179
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
3085
3180
  const forceToolRefresh = request.forceToolRefresh === true;
3181
+ const runtime = resolvePlayRunRuntimeSelection(request);
3086
3182
  const response = await this.http.post(
3087
3183
  "/api/v2/plays/run",
3088
3184
  {
@@ -3109,9 +3205,10 @@ var DeeplineClient = class {
3109
3205
  // defaults to absurd; callers normally omit this field.
3110
3206
  ...request.profile ? { profile: request.profile } : {},
3111
3207
  ...integrationMode ? { integrationMode } : {},
3208
+ ...runtime ? { runtime } : {},
3112
3209
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
3113
3210
  },
3114
- void 0,
3211
+ runtimeSelectionHeaders(runtime),
3115
3212
  // A start can reach the server even when its response is lost or times
3116
3213
  // out. Retrying this mutation without an idempotency key can create a
3117
3214
  // second root run, so callers must resolve ambiguous failures explicitly.
@@ -3134,6 +3231,7 @@ var DeeplineClient = class {
3134
3231
  const integrationMode = resolvePlayRunIntegrationMode(request);
3135
3232
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
3136
3233
  const forceToolRefresh = request.forceToolRefresh === true;
3234
+ const runtime = resolvePlayRunRuntimeSelection(request);
3137
3235
  const body = {
3138
3236
  ...request.name ? { name: request.name } : {},
3139
3237
  ...request.revisionId ? { revisionId: request.revisionId } : {},
@@ -3156,6 +3254,7 @@ var DeeplineClient = class {
3156
3254
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
3157
3255
  ...request.profile ? { profile: request.profile } : {},
3158
3256
  ...integrationMode ? { integrationMode } : {},
3257
+ ...runtime ? { runtime } : {},
3159
3258
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
3160
3259
  };
3161
3260
  for await (const event of this.http.streamSse(
@@ -3163,6 +3262,7 @@ var DeeplineClient = class {
3163
3262
  {
3164
3263
  method: "POST",
3165
3264
  body,
3265
+ headers: runtimeSelectionHeaders(runtime),
3166
3266
  signal: options?.signal
3167
3267
  }
3168
3268
  )) {
@@ -24350,24 +24450,42 @@ function renderAvailableToolsText(payload) {
24350
24450
  const returned = asFiniteNumber(payload.returned) ?? tools.length;
24351
24451
  const total = asFiniteNumber(payload.total);
24352
24452
  const lines = [
24353
- total !== void 0 ? `Monitor tools you can deploy (${returned} of ${total}):` : "Monitor tools you can deploy:"
24453
+ total !== void 0 ? `Signal Radar types you can deploy (${returned} of ${total}):` : "Signal Radar types you can deploy:"
24354
24454
  ];
24455
+ let currentProvider;
24355
24456
  for (const raw of tools) {
24356
24457
  const entry = asRecord(raw);
24357
24458
  const id = entry ? asString(entry.tool) : void 0;
24358
24459
  if (!entry || !id) continue;
24359
24460
  const name = asString(entry.name) ?? asString(entry.display_name);
24360
24461
  const deployedCount = asFiniteNumber(entry.deployed_count);
24462
+ const description = asString(entry.description);
24463
+ const streams = Array.isArray(entry.streams) ? entry.streams.map((stream) => asString(stream)).filter((stream) => Boolean(stream)) : [];
24464
+ const provider = id.split(".")[0];
24465
+ if (provider && provider !== currentProvider) {
24466
+ currentProvider = provider;
24467
+ lines.push("", ` ${provider}:`);
24468
+ }
24361
24469
  lines.push(
24362
- ` ${id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (deployed: ${deployedCount})` : ""}`
24470
+ ` ${id.split(".").slice(1).join(".") || id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (${deployedCount} deployed)` : ""}`
24363
24471
  );
24472
+ if (description) lines.push(` ${description}`);
24473
+ if (streams.length > 0) {
24474
+ lines.push(` emits: ${streams.join(", ")}`);
24475
+ }
24364
24476
  }
24365
24477
  if (payload.is_truncated === true) {
24366
24478
  lines.push("List truncated; raise --limit to see more.");
24367
24479
  }
24368
24480
  lines.push(
24369
24481
  "",
24370
- "Describe one (payload schema + output streams): deepline monitors available <tool-id> --json"
24482
+ "Next:",
24483
+ " Inspect payload fields, output columns, and a play binding:",
24484
+ " deepline monitors available <provider.tool> --json",
24485
+ " See monitors already deployed in this workspace:",
24486
+ " deepline monitors list",
24487
+ " Validate a monitor definition before deploying:",
24488
+ ` deepline monitors check '{"key":"my-radar","tool":"<provider.tool>","payload":{...}}'`
24371
24489
  );
24372
24490
  return `${lines.join("\n")}
24373
24491
  `;
@@ -617,7 +617,7 @@ var SDK_RELEASE = {
617
617
  // runtime intentionally no longer compiles at publish or launch time.
618
618
  // 0.1.242 removes the retired Workers/ESM compiler, generated bundles, and
619
619
  // deploy-time artifact migration. Play authoring now has one CJS contract.
620
- version: "0.1.248",
620
+ version: "0.1.250",
621
621
  apiContract: "2026-07-cjs-absurd-only-play-runtime-hard-cutover",
622
622
  supportPolicy: {
623
623
  minimumSupported: "0.1.53",
@@ -775,6 +775,7 @@ var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
775
775
  var COORDINATOR_URL_OVERRIDE_HEADER = "x-deepline-coordinator-url";
776
776
  var WORKER_CALLBACK_URL_OVERRIDE_HEADER = "x-deepline-worker-callback-url";
777
777
  var RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER = "x-deepline-runtime-scheduler-schema";
778
+ var RUNTIME_ENVIRONMENT_TOKEN_HEADER = "x-deepline-runtime-environment-token";
778
779
  var ABSURD_RELEASE_OVERRIDE_HEADER = "x-deepline-absurd-release";
779
780
  var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
780
781
 
@@ -1439,9 +1440,25 @@ function createSecretRedactionContext(initialValues = []) {
1439
1440
  }
1440
1441
  return value;
1441
1442
  }
1443
+ function redactKnownSecrets(value) {
1444
+ if (typeof value === "string") return redactString(value);
1445
+ if (Array.isArray(value)) {
1446
+ return value.map((entry) => redactKnownSecrets(entry));
1447
+ }
1448
+ if (value && typeof value === "object") {
1449
+ return Object.fromEntries(
1450
+ Object.entries(value).map(([key, entry]) => [
1451
+ key,
1452
+ redactKnownSecrets(entry)
1453
+ ])
1454
+ );
1455
+ }
1456
+ return value;
1457
+ }
1442
1458
  return {
1443
1459
  register,
1444
1460
  redactString,
1461
+ redactKnownSecrets,
1445
1462
  redact
1446
1463
  };
1447
1464
  }
@@ -2348,6 +2365,29 @@ async function* observeRunEvents(options) {
2348
2365
  }
2349
2366
  }
2350
2367
 
2368
+ // ../shared_libs/play-runtime/runtime-environment.ts
2369
+ var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
2370
+ var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
2371
+ function normalizePlayRuntimeNamespace(value) {
2372
+ if (typeof value !== "string") return null;
2373
+ const normalized = value.trim();
2374
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
2375
+ }
2376
+ function normalizePlayRuntimeSelection(value) {
2377
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2378
+ const record = value;
2379
+ if (Object.keys(record).some(
2380
+ (key) => key !== "environment" && key !== "namespace"
2381
+ ) || record.environment !== "preview") {
2382
+ return null;
2383
+ }
2384
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
2385
+ return namespace ? { environment: "preview", namespace } : null;
2386
+ }
2387
+ function normalizePlayRuntimeEnvironment(value) {
2388
+ return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
2389
+ }
2390
+
2351
2391
  // src/client.ts
2352
2392
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
2353
2393
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -2370,6 +2410,61 @@ function resolvePlayRunIntegrationMode(request) {
2370
2410
  request.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE
2371
2411
  );
2372
2412
  }
2413
+ function resolvePlayRunRuntimeSelection(request) {
2414
+ if (request.runtime !== void 0) {
2415
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
2416
+ if (!runtime) {
2417
+ throw new DeeplineError(
2418
+ 'runtime must be exactly { environment: "preview", namespace } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
2419
+ void 0,
2420
+ "INVALID_RUNTIME_SELECTION"
2421
+ );
2422
+ }
2423
+ return runtime;
2424
+ }
2425
+ const configured = process.env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
2426
+ const configuredNamespace = process.env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
2427
+ const configuredToken = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2428
+ if (!configured) {
2429
+ if (configuredNamespace || configuredToken) {
2430
+ throw new DeeplineError(
2431
+ "DEEPLINE_RUNTIME_ENVIRONMENT=preview and DEEPLINE_RUNTIME_NAMESPACE are required when runtime selection configuration is present.",
2432
+ void 0,
2433
+ "INVALID_RUNTIME_ENVIRONMENT"
2434
+ );
2435
+ }
2436
+ return void 0;
2437
+ }
2438
+ const environment = normalizePlayRuntimeEnvironment(configured);
2439
+ if (!environment) {
2440
+ throw new DeeplineError(
2441
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
2442
+ void 0,
2443
+ "INVALID_RUNTIME_ENVIRONMENT"
2444
+ );
2445
+ }
2446
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
2447
+ if (!namespace) {
2448
+ throw new DeeplineError(
2449
+ "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
2450
+ void 0,
2451
+ "INVALID_RUNTIME_NAMESPACE"
2452
+ );
2453
+ }
2454
+ return { environment, namespace };
2455
+ }
2456
+ function runtimeSelectionHeaders(runtime) {
2457
+ if (!runtime) return void 0;
2458
+ const token = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2459
+ if (!token) {
2460
+ throw new DeeplineError(
2461
+ "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
2462
+ void 0,
2463
+ "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
2464
+ );
2465
+ }
2466
+ return { [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token };
2467
+ }
2373
2468
  function normalizeTestPolicyOverrides(value, source) {
2374
2469
  if (value && typeof value === "object" && !Array.isArray(value)) {
2375
2470
  return value;
@@ -3068,6 +3163,7 @@ var DeeplineClient = class {
3068
3163
  const integrationMode = resolvePlayRunIntegrationMode(request);
3069
3164
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
3070
3165
  const forceToolRefresh = request.forceToolRefresh === true;
3166
+ const runtime = resolvePlayRunRuntimeSelection(request);
3071
3167
  const response = await this.http.post(
3072
3168
  "/api/v2/plays/run",
3073
3169
  {
@@ -3094,9 +3190,10 @@ var DeeplineClient = class {
3094
3190
  // defaults to absurd; callers normally omit this field.
3095
3191
  ...request.profile ? { profile: request.profile } : {},
3096
3192
  ...integrationMode ? { integrationMode } : {},
3193
+ ...runtime ? { runtime } : {},
3097
3194
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
3098
3195
  },
3099
- void 0,
3196
+ runtimeSelectionHeaders(runtime),
3100
3197
  // A start can reach the server even when its response is lost or times
3101
3198
  // out. Retrying this mutation without an idempotency key can create a
3102
3199
  // second root run, so callers must resolve ambiguous failures explicitly.
@@ -3119,6 +3216,7 @@ var DeeplineClient = class {
3119
3216
  const integrationMode = resolvePlayRunIntegrationMode(request);
3120
3217
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
3121
3218
  const forceToolRefresh = request.forceToolRefresh === true;
3219
+ const runtime = resolvePlayRunRuntimeSelection(request);
3122
3220
  const body = {
3123
3221
  ...request.name ? { name: request.name } : {},
3124
3222
  ...request.revisionId ? { revisionId: request.revisionId } : {},
@@ -3141,6 +3239,7 @@ var DeeplineClient = class {
3141
3239
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
3142
3240
  ...request.profile ? { profile: request.profile } : {},
3143
3241
  ...integrationMode ? { integrationMode } : {},
3242
+ ...runtime ? { runtime } : {},
3144
3243
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
3145
3244
  };
3146
3245
  for await (const event of this.http.streamSse(
@@ -3148,6 +3247,7 @@ var DeeplineClient = class {
3148
3247
  {
3149
3248
  method: "POST",
3150
3249
  body,
3250
+ headers: runtimeSelectionHeaders(runtime),
3151
3251
  signal: options?.signal
3152
3252
  }
3153
3253
  )) {
@@ -24386,24 +24486,42 @@ function renderAvailableToolsText(payload) {
24386
24486
  const returned = asFiniteNumber(payload.returned) ?? tools.length;
24387
24487
  const total = asFiniteNumber(payload.total);
24388
24488
  const lines = [
24389
- total !== void 0 ? `Monitor tools you can deploy (${returned} of ${total}):` : "Monitor tools you can deploy:"
24489
+ total !== void 0 ? `Signal Radar types you can deploy (${returned} of ${total}):` : "Signal Radar types you can deploy:"
24390
24490
  ];
24491
+ let currentProvider;
24391
24492
  for (const raw of tools) {
24392
24493
  const entry = asRecord(raw);
24393
24494
  const id = entry ? asString(entry.tool) : void 0;
24394
24495
  if (!entry || !id) continue;
24395
24496
  const name = asString(entry.name) ?? asString(entry.display_name);
24396
24497
  const deployedCount = asFiniteNumber(entry.deployed_count);
24498
+ const description = asString(entry.description);
24499
+ const streams = Array.isArray(entry.streams) ? entry.streams.map((stream) => asString(stream)).filter((stream) => Boolean(stream)) : [];
24500
+ const provider = id.split(".")[0];
24501
+ if (provider && provider !== currentProvider) {
24502
+ currentProvider = provider;
24503
+ lines.push("", ` ${provider}:`);
24504
+ }
24397
24505
  lines.push(
24398
- ` ${id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (deployed: ${deployedCount})` : ""}`
24506
+ ` ${id.split(".").slice(1).join(".") || id}${name ? ` \u2014 ${name}` : ""}${deployedCount !== void 0 ? ` (${deployedCount} deployed)` : ""}`
24399
24507
  );
24508
+ if (description) lines.push(` ${description}`);
24509
+ if (streams.length > 0) {
24510
+ lines.push(` emits: ${streams.join(", ")}`);
24511
+ }
24400
24512
  }
24401
24513
  if (payload.is_truncated === true) {
24402
24514
  lines.push("List truncated; raise --limit to see more.");
24403
24515
  }
24404
24516
  lines.push(
24405
24517
  "",
24406
- "Describe one (payload schema + output streams): deepline monitors available <tool-id> --json"
24518
+ "Next:",
24519
+ " Inspect payload fields, output columns, and a play binding:",
24520
+ " deepline monitors available <provider.tool> --json",
24521
+ " See monitors already deployed in this workspace:",
24522
+ " deepline monitors list",
24523
+ " Validate a monitor definition before deploying:",
24524
+ ` deepline monitors check '{"key":"my-radar","tool":"<provider.tool>","payload":{...}}'`
24407
24525
  );
24408
24526
  return `${lines.join("\n")}
24409
24527
  `;
package/dist/index.d.mts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { a as PlayCompilerManifest } from './compiler-manifest-DlE7dnRm.mjs';
2
2
 
3
+ type PlayRuntimeSelection = {
4
+ environment: 'preview';
5
+ /** Caller-named isolation scope inside a remote runtime environment. */
6
+ namespace: string;
7
+ };
8
+
3
9
  declare const DEEPLINE_TOOL_CATEGORIES: readonly ["company_search", "people_search", "people_enrich", "email_finder", "email_verify", "phone_finder", "phone_verify", "identity_resolution", "reverse_lookup", "enrichment", "batch", "premium", "free"];
4
10
  type DeeplineToolCategory = (typeof DEEPLINE_TOOL_CATEGORIES)[number] | (string & {});
5
11
  declare const PLAY_BOOTSTRAP_TEMPLATES: readonly ["people-list", "company-list", "people-email", "people-phone", "company-people", "company-people-email", "company-people-phone"];
@@ -1194,6 +1200,8 @@ interface StartPlayRunRequest {
1194
1200
  profile?: string;
1195
1201
  /** Optional per-run provider execution mode for eval/smoke runs. */
1196
1202
  integrationMode?: 'live' | 'eval_stub' | 'fixture';
1203
+ /** Internal runtime estate selection. The app host remains unchanged. */
1204
+ runtime?: PlayRuntimeSelection;
1197
1205
  /** Internal/dev-only runtime policy overrides for black-box durability tests. */
1198
1206
  testPolicyOverrides?: Record<string, unknown>;
1199
1207
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { a as PlayCompilerManifest } from './compiler-manifest-DlE7dnRm.js';
2
2
 
3
+ type PlayRuntimeSelection = {
4
+ environment: 'preview';
5
+ /** Caller-named isolation scope inside a remote runtime environment. */
6
+ namespace: string;
7
+ };
8
+
3
9
  declare const DEEPLINE_TOOL_CATEGORIES: readonly ["company_search", "people_search", "people_enrich", "email_finder", "email_verify", "phone_finder", "phone_verify", "identity_resolution", "reverse_lookup", "enrichment", "batch", "premium", "free"];
4
10
  type DeeplineToolCategory = (typeof DEEPLINE_TOOL_CATEGORIES)[number] | (string & {});
5
11
  declare const PLAY_BOOTSTRAP_TEMPLATES: readonly ["people-list", "company-list", "people-email", "people-phone", "company-people", "company-people-email", "company-people-phone"];
@@ -1194,6 +1200,8 @@ interface StartPlayRunRequest {
1194
1200
  profile?: string;
1195
1201
  /** Optional per-run provider execution mode for eval/smoke runs. */
1196
1202
  integrationMode?: 'live' | 'eval_stub' | 'fixture';
1203
+ /** Internal runtime estate selection. The app host remains unchanged. */
1204
+ runtime?: PlayRuntimeSelection;
1197
1205
  /** Internal/dev-only runtime policy overrides for black-box durability tests. */
1198
1206
  testPolicyOverrides?: Record<string, unknown>;
1199
1207
  }
package/dist/index.js CHANGED
@@ -431,7 +431,7 @@ var SDK_RELEASE = {
431
431
  // runtime intentionally no longer compiles at publish or launch time.
432
432
  // 0.1.242 removes the retired Workers/ESM compiler, generated bundles, and
433
433
  // deploy-time artifact migration. Play authoring now has one CJS contract.
434
- version: "0.1.248",
434
+ version: "0.1.250",
435
435
  apiContract: "2026-07-cjs-absurd-only-play-runtime-hard-cutover",
436
436
  supportPolicy: {
437
437
  minimumSupported: "0.1.53",
@@ -589,6 +589,7 @@ var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
589
589
  var COORDINATOR_URL_OVERRIDE_HEADER = "x-deepline-coordinator-url";
590
590
  var WORKER_CALLBACK_URL_OVERRIDE_HEADER = "x-deepline-worker-callback-url";
591
591
  var RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER = "x-deepline-runtime-scheduler-schema";
592
+ var RUNTIME_ENVIRONMENT_TOKEN_HEADER = "x-deepline-runtime-environment-token";
592
593
  var ABSURD_RELEASE_OVERRIDE_HEADER = "x-deepline-absurd-release";
593
594
  var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
594
595
 
@@ -1253,9 +1254,25 @@ function createSecretRedactionContext(initialValues = []) {
1253
1254
  }
1254
1255
  return value;
1255
1256
  }
1257
+ function redactKnownSecrets(value) {
1258
+ if (typeof value === "string") return redactString(value);
1259
+ if (Array.isArray(value)) {
1260
+ return value.map((entry) => redactKnownSecrets(entry));
1261
+ }
1262
+ if (value && typeof value === "object") {
1263
+ return Object.fromEntries(
1264
+ Object.entries(value).map(([key, entry]) => [
1265
+ key,
1266
+ redactKnownSecrets(entry)
1267
+ ])
1268
+ );
1269
+ }
1270
+ return value;
1271
+ }
1256
1272
  return {
1257
1273
  register,
1258
1274
  redactString,
1275
+ redactKnownSecrets,
1259
1276
  redact
1260
1277
  };
1261
1278
  }
@@ -2162,6 +2179,29 @@ async function* observeRunEvents(options) {
2162
2179
  }
2163
2180
  }
2164
2181
 
2182
+ // ../shared_libs/play-runtime/runtime-environment.ts
2183
+ var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
2184
+ var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
2185
+ function normalizePlayRuntimeNamespace(value) {
2186
+ if (typeof value !== "string") return null;
2187
+ const normalized = value.trim();
2188
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
2189
+ }
2190
+ function normalizePlayRuntimeSelection(value) {
2191
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2192
+ const record = value;
2193
+ if (Object.keys(record).some(
2194
+ (key) => key !== "environment" && key !== "namespace"
2195
+ ) || record.environment !== "preview") {
2196
+ return null;
2197
+ }
2198
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
2199
+ return namespace ? { environment: "preview", namespace } : null;
2200
+ }
2201
+ function normalizePlayRuntimeEnvironment(value) {
2202
+ return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
2203
+ }
2204
+
2165
2205
  // src/client.ts
2166
2206
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
2167
2207
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -2184,6 +2224,61 @@ function resolvePlayRunIntegrationMode(request) {
2184
2224
  request.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE
2185
2225
  );
2186
2226
  }
2227
+ function resolvePlayRunRuntimeSelection(request) {
2228
+ if (request.runtime !== void 0) {
2229
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
2230
+ if (!runtime) {
2231
+ throw new DeeplineError(
2232
+ 'runtime must be exactly { environment: "preview", namespace } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
2233
+ void 0,
2234
+ "INVALID_RUNTIME_SELECTION"
2235
+ );
2236
+ }
2237
+ return runtime;
2238
+ }
2239
+ const configured = process.env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
2240
+ const configuredNamespace = process.env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
2241
+ const configuredToken = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2242
+ if (!configured) {
2243
+ if (configuredNamespace || configuredToken) {
2244
+ throw new DeeplineError(
2245
+ "DEEPLINE_RUNTIME_ENVIRONMENT=preview and DEEPLINE_RUNTIME_NAMESPACE are required when runtime selection configuration is present.",
2246
+ void 0,
2247
+ "INVALID_RUNTIME_ENVIRONMENT"
2248
+ );
2249
+ }
2250
+ return void 0;
2251
+ }
2252
+ const environment = normalizePlayRuntimeEnvironment(configured);
2253
+ if (!environment) {
2254
+ throw new DeeplineError(
2255
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
2256
+ void 0,
2257
+ "INVALID_RUNTIME_ENVIRONMENT"
2258
+ );
2259
+ }
2260
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
2261
+ if (!namespace) {
2262
+ throw new DeeplineError(
2263
+ "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
2264
+ void 0,
2265
+ "INVALID_RUNTIME_NAMESPACE"
2266
+ );
2267
+ }
2268
+ return { environment, namespace };
2269
+ }
2270
+ function runtimeSelectionHeaders(runtime) {
2271
+ if (!runtime) return void 0;
2272
+ const token = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2273
+ if (!token) {
2274
+ throw new DeeplineError(
2275
+ "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
2276
+ void 0,
2277
+ "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
2278
+ );
2279
+ }
2280
+ return { [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token };
2281
+ }
2187
2282
  function normalizeTestPolicyOverrides(value, source) {
2188
2283
  if (value && typeof value === "object" && !Array.isArray(value)) {
2189
2284
  return value;
@@ -2882,6 +2977,7 @@ var DeeplineClient = class {
2882
2977
  const integrationMode = resolvePlayRunIntegrationMode(request);
2883
2978
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
2884
2979
  const forceToolRefresh = request.forceToolRefresh === true;
2980
+ const runtime = resolvePlayRunRuntimeSelection(request);
2885
2981
  const response = await this.http.post(
2886
2982
  "/api/v2/plays/run",
2887
2983
  {
@@ -2908,9 +3004,10 @@ var DeeplineClient = class {
2908
3004
  // defaults to absurd; callers normally omit this field.
2909
3005
  ...request.profile ? { profile: request.profile } : {},
2910
3006
  ...integrationMode ? { integrationMode } : {},
3007
+ ...runtime ? { runtime } : {},
2911
3008
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
2912
3009
  },
2913
- void 0,
3010
+ runtimeSelectionHeaders(runtime),
2914
3011
  // A start can reach the server even when its response is lost or times
2915
3012
  // out. Retrying this mutation without an idempotency key can create a
2916
3013
  // second root run, so callers must resolve ambiguous failures explicitly.
@@ -2933,6 +3030,7 @@ var DeeplineClient = class {
2933
3030
  const integrationMode = resolvePlayRunIntegrationMode(request);
2934
3031
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
2935
3032
  const forceToolRefresh = request.forceToolRefresh === true;
3033
+ const runtime = resolvePlayRunRuntimeSelection(request);
2936
3034
  const body = {
2937
3035
  ...request.name ? { name: request.name } : {},
2938
3036
  ...request.revisionId ? { revisionId: request.revisionId } : {},
@@ -2955,6 +3053,7 @@ var DeeplineClient = class {
2955
3053
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
2956
3054
  ...request.profile ? { profile: request.profile } : {},
2957
3055
  ...integrationMode ? { integrationMode } : {},
3056
+ ...runtime ? { runtime } : {},
2958
3057
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
2959
3058
  };
2960
3059
  for await (const event of this.http.streamSse(
@@ -2962,6 +3061,7 @@ var DeeplineClient = class {
2962
3061
  {
2963
3062
  method: "POST",
2964
3063
  body,
3064
+ headers: runtimeSelectionHeaders(runtime),
2965
3065
  signal: options?.signal
2966
3066
  }
2967
3067
  )) {
package/dist/index.mjs CHANGED
@@ -361,7 +361,7 @@ var SDK_RELEASE = {
361
361
  // runtime intentionally no longer compiles at publish or launch time.
362
362
  // 0.1.242 removes the retired Workers/ESM compiler, generated bundles, and
363
363
  // deploy-time artifact migration. Play authoring now has one CJS contract.
364
- version: "0.1.248",
364
+ version: "0.1.250",
365
365
  apiContract: "2026-07-cjs-absurd-only-play-runtime-hard-cutover",
366
366
  supportPolicy: {
367
367
  minimumSupported: "0.1.53",
@@ -519,6 +519,7 @@ var COORDINATOR_INTERNAL_TOKEN_HEADER = "x-deepline-internal-token";
519
519
  var COORDINATOR_URL_OVERRIDE_HEADER = "x-deepline-coordinator-url";
520
520
  var WORKER_CALLBACK_URL_OVERRIDE_HEADER = "x-deepline-worker-callback-url";
521
521
  var RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER = "x-deepline-runtime-scheduler-schema";
522
+ var RUNTIME_ENVIRONMENT_TOKEN_HEADER = "x-deepline-runtime-environment-token";
522
523
  var ABSURD_RELEASE_OVERRIDE_HEADER = "x-deepline-absurd-release";
523
524
  var SYNTHETIC_RUN_HEADER = "x-deepline-synthetic-run";
524
525
 
@@ -1183,9 +1184,25 @@ function createSecretRedactionContext(initialValues = []) {
1183
1184
  }
1184
1185
  return value;
1185
1186
  }
1187
+ function redactKnownSecrets(value) {
1188
+ if (typeof value === "string") return redactString(value);
1189
+ if (Array.isArray(value)) {
1190
+ return value.map((entry) => redactKnownSecrets(entry));
1191
+ }
1192
+ if (value && typeof value === "object") {
1193
+ return Object.fromEntries(
1194
+ Object.entries(value).map(([key, entry]) => [
1195
+ key,
1196
+ redactKnownSecrets(entry)
1197
+ ])
1198
+ );
1199
+ }
1200
+ return value;
1201
+ }
1186
1202
  return {
1187
1203
  register,
1188
1204
  redactString,
1205
+ redactKnownSecrets,
1189
1206
  redact
1190
1207
  };
1191
1208
  }
@@ -2092,6 +2109,29 @@ async function* observeRunEvents(options) {
2092
2109
  }
2093
2110
  }
2094
2111
 
2112
+ // ../shared_libs/play-runtime/runtime-environment.ts
2113
+ var PLAY_RUNTIME_ENVIRONMENTS = ["preview"];
2114
+ var PLAY_RUNTIME_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]{0,30}$/;
2115
+ function normalizePlayRuntimeNamespace(value) {
2116
+ if (typeof value !== "string") return null;
2117
+ const normalized = value.trim();
2118
+ return PLAY_RUNTIME_NAMESPACE_PATTERN.test(normalized) ? normalized : null;
2119
+ }
2120
+ function normalizePlayRuntimeSelection(value) {
2121
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2122
+ const record = value;
2123
+ if (Object.keys(record).some(
2124
+ (key) => key !== "environment" && key !== "namespace"
2125
+ ) || record.environment !== "preview") {
2126
+ return null;
2127
+ }
2128
+ const namespace = normalizePlayRuntimeNamespace(record.namespace);
2129
+ return namespace ? { environment: "preview", namespace } : null;
2130
+ }
2131
+ function normalizePlayRuntimeEnvironment(value) {
2132
+ return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
2133
+ }
2134
+
2095
2135
  // src/client.ts
2096
2136
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
2097
2137
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
@@ -2114,6 +2154,61 @@ function resolvePlayRunIntegrationMode(request) {
2114
2154
  request.integrationMode ?? process.env.DEEPLINE_EVAL_INTEGRATION_MODE
2115
2155
  );
2116
2156
  }
2157
+ function resolvePlayRunRuntimeSelection(request) {
2158
+ if (request.runtime !== void 0) {
2159
+ const runtime = normalizePlayRuntimeSelection(request.runtime);
2160
+ if (!runtime) {
2161
+ throw new DeeplineError(
2162
+ 'runtime must be exactly { environment: "preview", namespace } with namespace matching ^[a-z][a-z0-9-]{0,30}$.',
2163
+ void 0,
2164
+ "INVALID_RUNTIME_SELECTION"
2165
+ );
2166
+ }
2167
+ return runtime;
2168
+ }
2169
+ const configured = process.env.DEEPLINE_RUNTIME_ENVIRONMENT?.trim();
2170
+ const configuredNamespace = process.env.DEEPLINE_RUNTIME_NAMESPACE?.trim();
2171
+ const configuredToken = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2172
+ if (!configured) {
2173
+ if (configuredNamespace || configuredToken) {
2174
+ throw new DeeplineError(
2175
+ "DEEPLINE_RUNTIME_ENVIRONMENT=preview and DEEPLINE_RUNTIME_NAMESPACE are required when runtime selection configuration is present.",
2176
+ void 0,
2177
+ "INVALID_RUNTIME_ENVIRONMENT"
2178
+ );
2179
+ }
2180
+ return void 0;
2181
+ }
2182
+ const environment = normalizePlayRuntimeEnvironment(configured);
2183
+ if (!environment) {
2184
+ throw new DeeplineError(
2185
+ `DEEPLINE_RUNTIME_ENVIRONMENT supports only explicit preview selection. Received "${configured}". Omit it to use the app-native runtime.`,
2186
+ void 0,
2187
+ "INVALID_RUNTIME_ENVIRONMENT"
2188
+ );
2189
+ }
2190
+ const namespace = normalizePlayRuntimeNamespace(configuredNamespace);
2191
+ if (!namespace) {
2192
+ throw new DeeplineError(
2193
+ "DEEPLINE_RUNTIME_NAMESPACE is required for preview and must match ^[a-z][a-z0-9-]{0,30}$.",
2194
+ void 0,
2195
+ "INVALID_RUNTIME_NAMESPACE"
2196
+ );
2197
+ }
2198
+ return { environment, namespace };
2199
+ }
2200
+ function runtimeSelectionHeaders(runtime) {
2201
+ if (!runtime) return void 0;
2202
+ const token = process.env.DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN?.trim();
2203
+ if (!token) {
2204
+ throw new DeeplineError(
2205
+ "DEEPLINE_RUNTIME_ENVIRONMENT_TOKEN is required for explicit preview runtime selection.",
2206
+ void 0,
2207
+ "RUNTIME_ENVIRONMENT_TOKEN_REQUIRED"
2208
+ );
2209
+ }
2210
+ return { [RUNTIME_ENVIRONMENT_TOKEN_HEADER]: token };
2211
+ }
2117
2212
  function normalizeTestPolicyOverrides(value, source) {
2118
2213
  if (value && typeof value === "object" && !Array.isArray(value)) {
2119
2214
  return value;
@@ -2812,6 +2907,7 @@ var DeeplineClient = class {
2812
2907
  const integrationMode = resolvePlayRunIntegrationMode(request);
2813
2908
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
2814
2909
  const forceToolRefresh = request.forceToolRefresh === true;
2910
+ const runtime = resolvePlayRunRuntimeSelection(request);
2815
2911
  const response = await this.http.post(
2816
2912
  "/api/v2/plays/run",
2817
2913
  {
@@ -2838,9 +2934,10 @@ var DeeplineClient = class {
2838
2934
  // defaults to absurd; callers normally omit this field.
2839
2935
  ...request.profile ? { profile: request.profile } : {},
2840
2936
  ...integrationMode ? { integrationMode } : {},
2937
+ ...runtime ? { runtime } : {},
2841
2938
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
2842
2939
  },
2843
- void 0,
2940
+ runtimeSelectionHeaders(runtime),
2844
2941
  // A start can reach the server even when its response is lost or times
2845
2942
  // out. Retrying this mutation without an idempotency key can create a
2846
2943
  // second root run, so callers must resolve ambiguous failures explicitly.
@@ -2863,6 +2960,7 @@ var DeeplineClient = class {
2863
2960
  const integrationMode = resolvePlayRunIntegrationMode(request);
2864
2961
  const testPolicyOverrides = request.testPolicyOverrides ?? parseEnvTestPolicyOverrides();
2865
2962
  const forceToolRefresh = request.forceToolRefresh === true;
2963
+ const runtime = resolvePlayRunRuntimeSelection(request);
2866
2964
  const body = {
2867
2965
  ...request.name ? { name: request.name } : {},
2868
2966
  ...request.revisionId ? { revisionId: request.revisionId } : {},
@@ -2885,6 +2983,7 @@ var DeeplineClient = class {
2885
2983
  ...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
2886
2984
  ...request.profile ? { profile: request.profile } : {},
2887
2985
  ...integrationMode ? { integrationMode } : {},
2986
+ ...runtime ? { runtime } : {},
2888
2987
  ...testPolicyOverrides ? { testPolicyOverrides } : {}
2889
2988
  };
2890
2989
  for await (const event of this.http.streamSse(
@@ -2892,6 +2991,7 @@ var DeeplineClient = class {
2892
2991
  {
2893
2992
  method: "POST",
2894
2993
  body,
2994
+ headers: runtimeSelectionHeaders(runtime),
2895
2995
  signal: options?.signal
2896
2996
  }
2897
2997
  )) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.248",
3
+ "version": "0.1.250",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {