deepline 0.2.67 → 0.2.69

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.
@@ -1767,6 +1767,10 @@ export class DeeplineClient {
1767
1767
  const cloneEditStarter = this.playCloneEditStarter(play);
1768
1768
  return {
1769
1769
  name: play.name,
1770
+ // playKey and triggerStatus were projected away here, so `plays describe`
1771
+ // was strictly less informative than `plays list` for the same play — no
1772
+ // stable key, and no way to see that a cron was armed.
1773
+ ...(play.playKey ? { playKey: play.playKey } : {}),
1770
1774
  ...(play.reference ? { reference: play.reference } : {}),
1771
1775
  ...(play.displayName ? { displayName: play.displayName } : {}),
1772
1776
  ...(description ? { description } : {}),
@@ -1794,6 +1798,11 @@ export class DeeplineClient {
1794
1798
  examples: [runCommand],
1795
1799
  ...(cloneEditStarter ? { cloneEditStarter } : {}),
1796
1800
  currentPublishedVersion: play.currentPublishedVersion ?? null,
1801
+ // Read the live version off the live revision. It was previously only
1802
+ // ever written by the publish/live routes, so every list and describe
1803
+ // payload reported liveVersion: null even for an actively serving play.
1804
+ liveVersion: play.liveRevision?.version ?? null,
1805
+ ...(play.triggerStatus ? { triggerStatus: play.triggerStatus } : {}),
1797
1806
  isDraftDirty: play.isDraftDirty,
1798
1807
  };
1799
1808
  }
@@ -23,6 +23,15 @@ export type SdkCompatibilityResponse = {
23
23
  message: string;
24
24
  update_command: string;
25
25
  command?: string | null;
26
+ /**
27
+ * Present only when the queried command postdates this CLI. Advisory: the
28
+ * client is not unsupported, it simply predates the command.
29
+ */
30
+ command_introduced_in?: {
31
+ command: string;
32
+ introduced_in: string;
33
+ reason: string;
34
+ };
26
35
  auto_update?: {
27
36
  should_auto_update: boolean;
28
37
  required: boolean;
@@ -91,6 +91,29 @@ export type SdkSupportPolicy = {
91
91
  minimumSupported: string;
92
92
  reason: string;
93
93
  }>;
94
+ /**
95
+ * The version that first shipped a command.
96
+ *
97
+ * Deliberately not a support floor. `commandMinimumSupported` says an older
98
+ * client is BROKEN for a command and forces an update; this says only that
99
+ * an older client never had the command at all. That difference matters in
100
+ * both directions: introducing a command must not auto-update anyone, and a
101
+ * support floor may not exceed the version being released, while an
102
+ * introduction version is a fact about an already-published release.
103
+ *
104
+ * It exists because agent skills are served by the backend and re-synced on
105
+ * nearly every CLI invocation, while the CLI stays pinned. A release that
106
+ * adds a command and documents it in the same commit therefore hands every
107
+ * installed client instructions it cannot follow, and `unknown command` is
108
+ * all the author sees. Recording the version lets the CLI say which release
109
+ * added it. `check:skill-cli-commands` requires an entry here before a skill
110
+ * may document a command.
111
+ */
112
+ commandIntroducedIn?: ReadonlyArray<{
113
+ command: string;
114
+ introducedIn: string;
115
+ reason: string;
116
+ }>;
94
117
  /** Previously published wire contracts that remain supported. */
95
118
  compatibleApiContracts?: readonly string[];
96
119
  /**
@@ -160,7 +183,7 @@ export const SDK_RELEASE = {
160
183
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
161
184
  // exposed storage-dependent synchronous access. This deliberate minor
162
185
  // release keeps lazy paging semantics independent of row residency.
163
- version: '0.2.67',
186
+ version: '0.2.69',
164
187
  contracts: {
165
188
  api: {
166
189
  name: 'sdk-http-api',
@@ -188,6 +211,14 @@ export const SDK_RELEASE = {
188
211
  supportPolicy: {
189
212
  minimumSupported: '0.1.53',
190
213
  deprecatedBelow: '0.1.219',
214
+ commandIntroducedIn: [
215
+ {
216
+ command: 'notifications',
217
+ introducedIn: '0.2.40',
218
+ reason:
219
+ 'deepline notifications was added in SDK CLI 0.2.40. Older versions have no way to configure a Play failure alert, so a cron Play that dies reports to nobody.',
220
+ },
221
+ ],
191
222
  commandMinimumSupported: [
192
223
  {
193
224
  command: 'enrich',
@@ -1130,6 +1130,8 @@ export interface ProductNotification {
1130
1130
 
1131
1131
  export interface PlayDescription {
1132
1132
  name: string;
1133
+ /** Stable registry key. Same value `plays list` reports for this play. */
1134
+ playKey?: string;
1133
1135
  reference?: string;
1134
1136
  displayName?: string;
1135
1137
  description?: string | null;
@@ -1151,6 +1153,17 @@ export interface PlayDescription {
1151
1153
  checkCommand: string;
1152
1154
  };
1153
1155
  currentPublishedVersion?: number | null;
1156
+ /**
1157
+ * Version currently serving runs by name, from the live revision. Null when
1158
+ * the play has never been published.
1159
+ */
1160
+ liveVersion?: number | null;
1161
+ /** Whether this play's cron and webhook triggers are armed. */
1162
+ triggerStatus?: {
1163
+ cron: string | null;
1164
+ webhook: string | null;
1165
+ blockedReason: string | null;
1166
+ };
1154
1167
  isDraftDirty?: boolean;
1155
1168
  latestRunId?: string | null;
1156
1169
  }
@@ -383,19 +383,44 @@ export type PlaySqlQuery = {
383
383
  };
384
384
 
385
385
  declare const PLAY_SECRET_HANDLE_BRAND: unique symbol;
386
+ /**
387
+ * An opaque reference to a workspace secret, returned by `ctx.secrets.get`. The handle never carries the value into play code: it stringifies to `[secret:NAME]` and throws on `JSON.stringify`, so a secret cannot reach a play variable, a log line, a dataset cell, or a replay artifact even by accident. Only the runtime resolves it, at the moment it attaches the request header.
388
+ *
389
+ * @sdkReference runtime 176 SecretHandle
390
+ */
386
391
  export type PlaySecretHandle = {
387
392
  readonly [PLAY_SECRET_HANDLE_BRAND]: never;
393
+ /** Name of the workspace secret, uppercased. Never its value. */
388
394
  readonly name: string;
395
+ /** Renders `[secret:NAME]`, so an interpolated handle leaks nothing. */
389
396
  toString(): string;
397
+ /** Always throws. A secret handle is deliberately not serializable. */
390
398
  toJSON(): never;
391
399
  };
400
+ /**
401
+ * One resolved authentication scheme, built by `ctx.secrets.bearer` or `ctx.secrets.header` and attached to a request through `init.auth`.
402
+ *
403
+ * @sdkReference runtime 177 SecretAuth
404
+ */
392
405
  export type PlaySecretAuth = {
406
+ /** `bearer` sends `Authorization: Bearer <value>`; `header` sends a named header. */
393
407
  readonly kind: 'bearer' | 'header';
408
+ /** The handle whose value the runtime attaches. */
394
409
  readonly secret: PlaySecretHandle;
410
+ /** Header name, set only when `kind` is `header`. */
395
411
  readonly header?: string;
396
412
  };
413
+ /**
414
+ * The `init` accepted by `ctx.fetch`. Same shape as `RequestInit` plus `auth`.
415
+ *
416
+ * @sdkReference runtime 174 SecretAwareRequestInit
417
+ */
397
418
  export type PlaySecretAwareRequestInit = Omit<RequestInit, 'headers'> & {
419
+ /** Ordinary request headers, recorded in the durable receipt. Never interpolate a secret value here — use `auth`. */
398
420
  headers?: HeadersInit;
421
+ /**
422
+ * The single authenticated header for this request. One value, not a list: exactly one `ctx.secrets` auth attaches per `ctx.fetch`. An API wanting two credentialed headers at once — Supabase with both `apikey` and `Authorization` — cannot express both. Put the must-stay-secret credential in `auth`; pass a genuinely non-secret second value in `headers`. If both are secret, the request needs a server-side proxy holding one of them.
423
+ */
399
424
  auth?: PlaySecretAuth;
400
425
  };
401
426
  export type PlayLooseObject = { [key: string]: PlayLooseObject };
@@ -789,13 +814,27 @@ export type PlayAuthoringFetchOptions = {
789
814
  staleAfterSeconds?: DurableCallStaleAfterSeconds;
790
815
  };
791
816
 
817
+ /**
818
+ * The value `ctx.fetch(...)` resolves to: a plain durable record, not a WHATWG `Response`. The body is read once at request time so the call can be checkpointed and replayed, so `bodyText` and `json` are already-materialized properties. There is no `.json()`, `.text()`, or `.body` to await — `await res.json()` is a type error, not a typing problem.
819
+ *
820
+ * @sdkReference runtime 175 PlayFetchResponse
821
+ */
792
822
  export type PlayAuthoringFetchResponse = {
823
+ /** True when the response status is in the 2xx range. */
793
824
  ok: boolean;
825
+ /** HTTP status code as returned by the upstream server. */
794
826
  status: number;
827
+ /** HTTP status text as returned by the upstream server. */
795
828
  statusText: string;
829
+ /** Final response URL after any redirects. */
796
830
  url: string;
831
+ /** Response headers, lowercased, with any known secret values redacted. */
797
832
  headers: Record<string, string>;
833
+ /** Full response body as text, with any known secret values redacted. */
798
834
  bodyText: string;
835
+ /**
836
+ * The parsed body, eagerly decoded at request time. Read it as a property — `const body = res.json`, never `await res.json()`. Null when the body is empty AND when it is not valid JSON: a malformed payload is reported as null rather than thrown, so check `res.ok` and fall back to `res.bodyText` before treating null as an empty result.
837
+ */
799
838
  json: unknown | null;
800
839
  };
801
840
 
@@ -831,8 +870,24 @@ export const PLAY_AUTHORING_DOCUMENTATION = {
831
870
  'ctx.run.id is stable while Deepline retries or resumes one durable run. A separately submitted run receives a new id.',
832
871
  use: 'Use it when deriving an external idempotency key for a sequence of batches.',
833
872
  },
873
+ staticCallKeys: {
874
+ constraint:
875
+ 'Durable call keys — the ctx.fetch key, the ctx.dataset key, the ctx.step id — must be static string literals. The key names a durable receipt, so check, publish, and replay have to agree on it before the body runs. A key computed at runtime cannot be resolved at check time and is rejected.',
876
+ consequence:
877
+ 'This is an architectural constraint, not a style rule. A play cannot loop over a computed key, so it cannot page a large table with a helper like page(pageNumber). Unrolling one literal key per page is not a design at any real page count.',
878
+ workaround:
879
+ 'Push the aggregation server-side and call it once: a SQL function, a view, or a provider endpoint that returns the whole result. Keep unrolled literal keys only for a handful of genuinely distinct calls. To fan out over rows, use ctx.dataset with a static key — the per-row receipt identity comes from the row, not from the key.',
880
+ },
834
881
  } as const;
835
882
 
883
+ /**
884
+ * Shown when a ctx.fetch key is present but not statically resolvable. Named
885
+ * separately so the field registry, the check hint, and the generated
886
+ * reference cannot drift from one another.
887
+ */
888
+ export const PLAY_AUTHORING_STATIC_FETCH_KEY_HINT =
889
+ `${PLAY_AUTHORING_DOCUMENTATION.staticCallKeys.workaround} Do not compute the key.` as const;
890
+
836
891
  /** The complete customer-authored `ctx` Interface shared by every Adapter. */
837
892
  export interface PlayAuthoringRuntimeContext {
838
893
  /**
@@ -932,8 +987,23 @@ export interface PlayAuthoringRuntimeContext {
932
987
  ): Promise<PlayAuthoringFetchResponse>;
933
988
 
934
989
  secrets: {
990
+ /**
991
+ * Reference a workspace secret by name without reading its value. This is the only supported way to authenticate an outbound request from a Play: `process.env.X` is rejected at check time because an env read puts the raw value in a play variable, where it can reach a log line or a replay artifact. A handle cannot — pass it to `bearer` or `header` and the runtime resolves it only while attaching the header. Names are uppercased; manage stored values with `deepline secrets set` / `deepline secrets list`.
992
+ *
993
+ * @sdkReference runtime 171 ctx.secrets.get(name)
994
+ */
935
995
  get(name: string): PlaySecretHandle;
996
+ /**
997
+ * Send the secret as `Authorization: Bearer <value>`.
998
+ *
999
+ * @sdkReference runtime 172 ctx.secrets.bearer(secret)
1000
+ */
936
1001
  bearer(secret: PlaySecretHandle): PlaySecretAuth;
1002
+ /**
1003
+ * Send the secret as a named header, for APIs that do not use bearer tokens — `x-api-key`, `apikey`, `private-token`, and similar.
1004
+ *
1005
+ * @sdkReference runtime 173 ctx.secrets.header(header, secret)
1006
+ */
937
1007
  header(header: string, secret: PlaySecretHandle): PlaySecretAuth;
938
1008
  };
939
1009
 
@@ -1830,6 +1900,7 @@ export const PLAY_AUTHORING_FIELD_REGISTRY = {
1830
1900
  issueCode: 'play_authoring_durable_policy_invalid',
1831
1901
  description: 'Stable durable identity for one external HTTP request.',
1832
1902
  errorMessage: 'ctx.fetch key must be a non-empty static string.',
1903
+ unresolvedHint: PLAY_AUTHORING_STATIC_FETCH_KEY_HINT,
1833
1904
  },
1834
1905
  'ctx.fetch.staleAfterSeconds': {
1835
1906
  schema: Type.Union([Type.Null(), Type.Integer({ minimum: 0 })]),
@@ -2220,6 +2291,13 @@ export const PLAY_AUTHORING_FIELD_REGISTRY = {
2220
2291
  issueCode: PlayAuthoringContractIssueCode;
2221
2292
  description: string;
2222
2293
  errorMessage: string;
2294
+ /**
2295
+ * Hint shown when the value is present but could not be resolved
2296
+ * statically. Defaults to generic "use a literal" advice, which is useless
2297
+ * when the author has a real reason to compute the value. Set this on any
2298
+ * field where the reason is common enough to name the way out.
2299
+ */
2300
+ unresolvedHint?: string;
2223
2301
  }
2224
2302
  >;
2225
2303
 
@@ -2331,6 +2409,25 @@ function cloudReferenceType(path: PlayAuthoringFieldPath): string {
2331
2409
  return PLAY_AUTHORING_FIELD_REGISTRY[path].referenceType;
2332
2410
  }
2333
2411
 
2412
+ const DEFAULT_UNRESOLVED_HINT =
2413
+ 'Use a literal value so check, publish, and runtime agree.';
2414
+
2415
+ /**
2416
+ * Hint for a field that is present but not statically resolvable.
2417
+ *
2418
+ * The registry is `as const satisfies`, so an entry without `unresolvedHint`
2419
+ * has no such property in its literal type. Read it through here rather than
2420
+ * widening the registry and losing the per-field schema narrowing.
2421
+ */
2422
+ export function playAuthoringUnresolvedHint(
2423
+ path: PlayAuthoringFieldPath,
2424
+ ): string {
2425
+ const definition = PLAY_AUTHORING_FIELD_REGISTRY[path] as {
2426
+ unresolvedHint?: string;
2427
+ };
2428
+ return definition.unresolvedHint ?? DEFAULT_UNRESOLVED_HINT;
2429
+ }
2430
+
2334
2431
  /** Ambient declarations generated into the cloud Play compiler from this model. */
2335
2432
  export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
2336
2433
  `export type DurableCallStaleAfterSeconds = ${cloudReferenceType('ctx.tools.execute.staleAfterSeconds')};`,