deepline 0.1.55 → 0.1.56

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.
package/dist/cli/index.js CHANGED
@@ -220,10 +220,10 @@ function resolveConfig(options) {
220
220
 
221
221
  // src/release.ts
222
222
  var SDK_RELEASE = {
223
- version: "0.1.55",
224
- apiContract: "2026-05-run-response-package",
223
+ version: "0.1.56",
224
+ apiContract: "2026-05-play-tool-describe-starters",
225
225
  supportPolicy: {
226
- latest: "0.1.55",
226
+ latest: "0.1.56",
227
227
  minimumSupported: "0.1.53",
228
228
  deprecatedBelow: "0.1.53"
229
229
  }
@@ -727,6 +727,23 @@ var DeeplineClient = class {
727
727
  }
728
728
  return `deepline plays run ${target} --input '{...}' --watch`;
729
729
  }
730
+ starterPlayPath(play) {
731
+ const target = play.reference || play.name;
732
+ const unqualifiedName = target.split("/").pop() || play.name;
733
+ const safeName = unqualifiedName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
734
+ return `./${safeName || "play"}.play.ts`;
735
+ }
736
+ playCloneEditStarter(play) {
737
+ const readonlyPrebuilt = (play.origin === "prebuilt" || play.ownerType === "deepline") && !play.canEdit;
738
+ if (!play.canClone && !readonlyPrebuilt) return void 0;
739
+ const target = play.reference || play.name;
740
+ const path = this.starterPlayPath(play);
741
+ return {
742
+ path,
743
+ command: `deepline plays get ${target} --source --out ${path}`,
744
+ checkCommand: `deepline plays check ${path}`
745
+ };
746
+ }
730
747
  summarizePlayListItem(play, options) {
731
748
  const aliases = play.aliases?.length ? play.aliases : [play.name];
732
749
  const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
@@ -735,6 +752,7 @@ var DeeplineClient = class {
735
752
  "rowOutputSchema"
736
753
  );
737
754
  const runCommand2 = this.playRunCommand(play, { csvInput });
755
+ const cloneEditStarter = this.playCloneEditStarter(play);
738
756
  return {
739
757
  name: play.name,
740
758
  ...play.reference ? { reference: play.reference } : {},
@@ -750,6 +768,7 @@ var DeeplineClient = class {
750
768
  ...rowOutputSchema ? { rowOutputSchema } : {},
751
769
  runCommand: runCommand2,
752
770
  examples: [runCommand2],
771
+ ...cloneEditStarter ? { cloneEditStarter } : {},
753
772
  currentPublishedVersion: play.currentPublishedVersion ?? null,
754
773
  isDraftDirty: play.isDraftDirty
755
774
  };
@@ -1452,7 +1471,6 @@ var DeeplineClient = class {
1452
1471
  async searchPlays(options) {
1453
1472
  const params = new URLSearchParams();
1454
1473
  params.set("search", options.query.trim());
1455
- if (options.origin) params.set("origin", options.origin);
1456
1474
  const response = await this.http.get(
1457
1475
  `/api/v2/plays?${params.toString()}`
1458
1476
  );
@@ -5717,6 +5735,204 @@ function createCliProgress(enabled) {
5717
5735
  return progress;
5718
5736
  }
5719
5737
 
5738
+ // ../shared_libs/plays/row-identity.ts
5739
+ var POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
5740
+ var PLAY_NAME_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
5741
+ var MAP_KEY_NAMESPACE_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
5742
+ var SHA256_INITIAL_HASH = [
5743
+ 1779033703,
5744
+ 3144134277,
5745
+ 1013904242,
5746
+ 2773480762,
5747
+ 1359893119,
5748
+ 2600822924,
5749
+ 528734635,
5750
+ 1541459225
5751
+ ];
5752
+ var SHA256_ROUND_CONSTANTS = [
5753
+ 1116352408,
5754
+ 1899447441,
5755
+ 3049323471,
5756
+ 3921009573,
5757
+ 961987163,
5758
+ 1508970993,
5759
+ 2453635748,
5760
+ 2870763221,
5761
+ 3624381080,
5762
+ 310598401,
5763
+ 607225278,
5764
+ 1426881987,
5765
+ 1925078388,
5766
+ 2162078206,
5767
+ 2614888103,
5768
+ 3248222580,
5769
+ 3835390401,
5770
+ 4022224774,
5771
+ 264347078,
5772
+ 604807628,
5773
+ 770255983,
5774
+ 1249150122,
5775
+ 1555081692,
5776
+ 1996064986,
5777
+ 2554220882,
5778
+ 2821834349,
5779
+ 2952996808,
5780
+ 3210313671,
5781
+ 3336571891,
5782
+ 3584528711,
5783
+ 113926993,
5784
+ 338241895,
5785
+ 666307205,
5786
+ 773529912,
5787
+ 1294757372,
5788
+ 1396182291,
5789
+ 1695183700,
5790
+ 1986661051,
5791
+ 2177026350,
5792
+ 2456956037,
5793
+ 2730485921,
5794
+ 2820302411,
5795
+ 3259730800,
5796
+ 3345764771,
5797
+ 3516065817,
5798
+ 3600352804,
5799
+ 4094571909,
5800
+ 275423344,
5801
+ 430227734,
5802
+ 506948616,
5803
+ 659060556,
5804
+ 883997877,
5805
+ 958139571,
5806
+ 1322822218,
5807
+ 1537002063,
5808
+ 1747873779,
5809
+ 1955562222,
5810
+ 2024104815,
5811
+ 2227730452,
5812
+ 2361852424,
5813
+ 2428436474,
5814
+ 2756734187,
5815
+ 3204031479,
5816
+ 3329325298
5817
+ ];
5818
+ function rightRotate32(value, bits) {
5819
+ return value >>> bits | value << 32 - bits;
5820
+ }
5821
+ function sha256Hex(input) {
5822
+ const bytes = Array.from(new TextEncoder().encode(input));
5823
+ const bitLength = bytes.length * 8;
5824
+ bytes.push(128);
5825
+ while (bytes.length % 64 !== 56) {
5826
+ bytes.push(0);
5827
+ }
5828
+ const highBits = Math.floor(bitLength / 4294967296);
5829
+ const lowBits = bitLength >>> 0;
5830
+ bytes.push(
5831
+ highBits >>> 24 & 255,
5832
+ highBits >>> 16 & 255,
5833
+ highBits >>> 8 & 255,
5834
+ highBits & 255,
5835
+ lowBits >>> 24 & 255,
5836
+ lowBits >>> 16 & 255,
5837
+ lowBits >>> 8 & 255,
5838
+ lowBits & 255
5839
+ );
5840
+ const hash = [...SHA256_INITIAL_HASH];
5841
+ const words = new Array(64).fill(0);
5842
+ for (let offset = 0; offset < bytes.length; offset += 64) {
5843
+ for (let index = 0; index < 16; index += 1) {
5844
+ const wordOffset = offset + index * 4;
5845
+ words[index] = (bytes[wordOffset] ?? 0) << 24 | (bytes[wordOffset + 1] ?? 0) << 16 | (bytes[wordOffset + 2] ?? 0) << 8 | (bytes[wordOffset + 3] ?? 0);
5846
+ }
5847
+ for (let index = 16; index < 64; index += 1) {
5848
+ const s0 = rightRotate32(words[index - 15], 7) ^ rightRotate32(words[index - 15], 18) ^ words[index - 15] >>> 3;
5849
+ const s1 = rightRotate32(words[index - 2], 17) ^ rightRotate32(words[index - 2], 19) ^ words[index - 2] >>> 10;
5850
+ words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
5851
+ }
5852
+ let [a, b, c, d, e, f, g, h] = hash;
5853
+ for (let index = 0; index < 64; index += 1) {
5854
+ const s1 = rightRotate32(e, 6) ^ rightRotate32(e, 11) ^ rightRotate32(e, 25);
5855
+ const ch = e & f ^ ~e & g;
5856
+ const temp1 = h + s1 + ch + SHA256_ROUND_CONSTANTS[index] + words[index] >>> 0;
5857
+ const s0 = rightRotate32(a, 2) ^ rightRotate32(a, 13) ^ rightRotate32(a, 22);
5858
+ const maj = a & b ^ a & c ^ b & c;
5859
+ const temp2 = s0 + maj >>> 0;
5860
+ h = g;
5861
+ g = f;
5862
+ f = e;
5863
+ e = d + temp1 >>> 0;
5864
+ d = c;
5865
+ c = b;
5866
+ b = a;
5867
+ a = temp1 + temp2 >>> 0;
5868
+ }
5869
+ hash[0] = hash[0] + a >>> 0;
5870
+ hash[1] = hash[1] + b >>> 0;
5871
+ hash[2] = hash[2] + c >>> 0;
5872
+ hash[3] = hash[3] + d >>> 0;
5873
+ hash[4] = hash[4] + e >>> 0;
5874
+ hash[5] = hash[5] + f >>> 0;
5875
+ hash[6] = hash[6] + g >>> 0;
5876
+ hash[7] = hash[7] + h >>> 0;
5877
+ }
5878
+ return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
5879
+ }
5880
+ function sanitizeIdentifierPart(value) {
5881
+ return value.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
5882
+ }
5883
+ function validateIdentifierPart(rawValue, label, maxLength) {
5884
+ const sanitized = sanitizeIdentifierPart(rawValue);
5885
+ if (!sanitized) {
5886
+ throw new Error(
5887
+ `${label} must contain at least one letter or number after normalization. Use only letters, numbers, underscores, or hyphens.`
5888
+ );
5889
+ }
5890
+ if (sanitized.length > maxLength) {
5891
+ throw new Error(
5892
+ `${label} is too long after normalization (${sanitized.length}/${maxLength}). Shorten it to ${maxLength} characters or fewer. Normalized value: "${sanitized}".`
5893
+ );
5894
+ }
5895
+ return sanitized;
5896
+ }
5897
+ function normalizePlayName(value) {
5898
+ if (value.includes("/")) {
5899
+ throw new Error(
5900
+ 'Play name cannot contain "/". Slash is reserved for qualified play references like "prebuilt/example" or "self/example".'
5901
+ );
5902
+ }
5903
+ return validateIdentifierPart(value, "Play name", PLAY_NAME_MAX_LENGTH);
5904
+ }
5905
+ function normalizePlayNameForSheet(value) {
5906
+ if (!value.includes("/")) {
5907
+ return normalizePlayName(value);
5908
+ }
5909
+ const digest = sha256Hex(value).slice(0, 12);
5910
+ const normalizedReference = sanitizeIdentifierPart(
5911
+ value.replace(/\//g, "__")
5912
+ );
5913
+ const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
5914
+ const prefix = normalizedReference.slice(0, prefixLength).replace(/_+$/g, "") || "qualified_play";
5915
+ return `${prefix}_${digest}`;
5916
+ }
5917
+ function normalizeTableNamespace(value) {
5918
+ return validateIdentifierPart(
5919
+ value,
5920
+ "ctx.map() key",
5921
+ MAP_KEY_NAMESPACE_MAX_LENGTH
5922
+ );
5923
+ }
5924
+ function validatePlaySheetTableName(playName, tableNamespace) {
5925
+ const playSegment = normalizePlayNameForSheet(playName);
5926
+ const keySegment = normalizeTableNamespace(tableNamespace);
5927
+ const resolved = `${playSegment}_${keySegment}`;
5928
+ if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
5929
+ throw new Error(
5930
+ `Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.map() key. Resolved table name: "${resolved}".`
5931
+ );
5932
+ }
5933
+ return resolved;
5934
+ }
5935
+
5720
5936
  // src/cli/trace.ts
5721
5937
  var cliTraceStartedAt = Date.now();
5722
5938
  function isTruthyEnv(value) {
@@ -5765,6 +5981,31 @@ async function traceCliSpan(phase, fields, run) {
5765
5981
  }
5766
5982
  }
5767
5983
 
5984
+ // src/cli/play-check-hints.ts
5985
+ var EXTRACTED_GETTER_ERROR_HINT = "Deepline hint: extractedValues/extractedLists .get() only works for declared Deepline getters listed by `deepline tools describe <tool> --json`. Use `toolExecutionResult.toolResponse.raw` for provider/tool-specific fields.";
5986
+ function sourceLineForError(sourceCode, error) {
5987
+ const match = error.match(/:(\d+):(\d+)\s/);
5988
+ const lineNumber = match?.[1] ? Number(match[1]) : NaN;
5989
+ if (!Number.isInteger(lineNumber) || lineNumber < 1) return "";
5990
+ return sourceCode.split(/\r?\n/)[lineNumber - 1] ?? "";
5991
+ }
5992
+ function looksLikeInvalidExtractedGetter(error, sourceLine) {
5993
+ if (!/Property '[^']+' does not exist on type/.test(error)) return false;
5994
+ return /\bextracted(?:Values|Lists)\s*\./.test(sourceLine);
5995
+ }
5996
+ function addPlayCheckRepairHints(input) {
5997
+ let addedHint = false;
5998
+ return input.errors.map((error) => {
5999
+ const line = sourceLineForError(input.sourceCode, error);
6000
+ if (addedHint || !looksLikeInvalidExtractedGetter(error, line) || error.includes(EXTRACTED_GETTER_ERROR_HINT)) {
6001
+ return error;
6002
+ }
6003
+ addedHint = true;
6004
+ return `${error}
6005
+ ${EXTRACTED_GETTER_ERROR_HINT}`;
6006
+ });
6007
+ }
6008
+
5768
6009
  // src/cli/commands/play.ts
5769
6010
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
5770
6011
  "--json",
@@ -5844,9 +6085,23 @@ function formatPlayListReference(play) {
5844
6085
  return play.reference || play.name;
5845
6086
  }
5846
6087
  function defaultMaterializedPlayPath(reference) {
6088
+ return (0, import_node_path10.resolve)(defaultStarterPlayPath(reference));
6089
+ }
6090
+ function defaultStarterPlayPath(reference) {
5847
6091
  const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
5848
6092
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5849
- return (0, import_node_path10.resolve)(`${safeName || "play"}.play.ts`);
6093
+ return `./${safeName || "play"}.play.ts`;
6094
+ }
6095
+ function buildCloneEditStarter(play) {
6096
+ const readonlyPrebuilt = (play.origin === "prebuilt" || play.ownerType === "deepline") && !play.canEdit;
6097
+ if (!play.canClone && !readonlyPrebuilt) return void 0;
6098
+ const reference = play.reference || play.name;
6099
+ const path = defaultStarterPlayPath(reference);
6100
+ return {
6101
+ path,
6102
+ command: `deepline plays get ${reference} --source --out ${path}`,
6103
+ checkCommand: `deepline plays check ${path}`
6104
+ };
5850
6105
  }
5851
6106
  function materializeRemotePlaySource(input) {
5852
6107
  if (isFileTarget(input.target)) {
@@ -6292,6 +6547,8 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
6292
6547
  "cancelled"
6293
6548
  ]);
6294
6549
  var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
6550
+ var PLAY_CUSTOMER_STORAGE_SCHEMA_NAME = "storage";
6551
+ var PLAY_INTERNAL_STEP_RECEIPT_TABLE = "_deepline_step_receipts";
6295
6552
  function getEventPayload(event) {
6296
6553
  return event.payload && typeof event.payload === "object" ? event.payload : {};
6297
6554
  }
@@ -6345,6 +6602,86 @@ function getLogLinesFromLiveEvent(event) {
6345
6602
  const lines = getEventPayload(event).lines;
6346
6603
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
6347
6604
  }
6605
+ function quoteSqlIdentifier(identifier) {
6606
+ return `"${identifier.replace(/"/g, '""')}"`;
6607
+ }
6608
+ function quoteSqlLiteral(value) {
6609
+ return `'${value.replace(/'/g, "''")}'`;
6610
+ }
6611
+ function buildDebugDbQueryCommand(sql) {
6612
+ return `deepline db query --sql ${shellSingleQuote(sql)} --max-rows 20 --json`;
6613
+ }
6614
+ function buildStepReceiptsDebugCommand(runId) {
6615
+ const table = `${quoteSqlIdentifier(
6616
+ PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
6617
+ )}.${quoteSqlIdentifier(PLAY_INTERNAL_STEP_RECEIPT_TABLE)}`;
6618
+ const sql = `select convert_from(k, 'UTF8') as receipt_key, case status when 0 then 'pending' when 1 then 'running' when 2 then 'completed' when 3 then 'failed' when 4 then 'skipped' else status::text end as status, output, error, updated_at from ${table} where run_id = ${quoteSqlLiteral(runId)} order by updated_at asc, receipt_key asc limit 20`;
6619
+ return buildDebugDbQueryCommand(sql);
6620
+ }
6621
+ function buildMapTableDebugCommand(input) {
6622
+ try {
6623
+ const tableName = validatePlaySheetTableName(
6624
+ input.playName,
6625
+ input.tableNamespace
6626
+ );
6627
+ const table = `${quoteSqlIdentifier(
6628
+ PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
6629
+ )}.${quoteSqlIdentifier(tableName)}`;
6630
+ const sql = `select * from ${table} where _run_id = ${quoteSqlLiteral(input.runId)} limit 20`;
6631
+ return buildDebugDbQueryCommand(sql);
6632
+ } catch {
6633
+ return null;
6634
+ }
6635
+ }
6636
+ function extractTableNamespaceFromLiveEvent(event) {
6637
+ const payload = getEventPayload(event);
6638
+ const candidates = [
6639
+ payload.artifactTableNamespace,
6640
+ payload.tableNamespace,
6641
+ payload.mapNodeId
6642
+ ];
6643
+ for (const candidate of candidates) {
6644
+ if (typeof candidate === "string" && candidate.trim()) {
6645
+ return candidate.trim();
6646
+ }
6647
+ }
6648
+ return null;
6649
+ }
6650
+ function emitLiveDebugTableHints(input) {
6651
+ if (input.jsonOutput || !input.runId || input.runId === "pending") {
6652
+ return;
6653
+ }
6654
+ input.state.emittedDebugKeys ??= /* @__PURE__ */ new Set();
6655
+ const receiptsKey = `receipts:${input.runId}`;
6656
+ if (!input.state.emittedDebugKeys.has(receiptsKey)) {
6657
+ input.state.emittedDebugKeys.add(receiptsKey);
6658
+ input.progress.writeLine(
6659
+ `Debug top-level outputs: ${buildStepReceiptsDebugCommand(input.runId)}`,
6660
+ process.stdout
6661
+ );
6662
+ }
6663
+ const tableNamespace = extractTableNamespaceFromLiveEvent(input.event);
6664
+ if (!tableNamespace) {
6665
+ return;
6666
+ }
6667
+ const tableKey = `table:${input.runId}:${tableNamespace}`;
6668
+ if (input.state.emittedDebugKeys.has(tableKey)) {
6669
+ return;
6670
+ }
6671
+ const command = buildMapTableDebugCommand({
6672
+ playName: input.playName,
6673
+ runId: input.runId,
6674
+ tableNamespace
6675
+ });
6676
+ if (!command) {
6677
+ return;
6678
+ }
6679
+ input.state.emittedDebugKeys.add(tableKey);
6680
+ input.progress.writeLine(
6681
+ `Debug rows for ${tableNamespace}: ${command}`,
6682
+ process.stdout
6683
+ );
6684
+ }
6348
6685
  function describeLiveEventPhase(event) {
6349
6686
  const payload = getEventPayload(event);
6350
6687
  if (event.type === "play.run.status") {
@@ -6497,6 +6834,14 @@ async function waitForPlayCompletionByStream(input) {
6497
6834
  lastPhase = phase;
6498
6835
  input.progress.phase(phase);
6499
6836
  }
6837
+ emitLiveDebugTableHints({
6838
+ event,
6839
+ playName: input.playName,
6840
+ runId: input.workflowId,
6841
+ jsonOutput: input.jsonOutput,
6842
+ state: input.state,
6843
+ progress: input.progress
6844
+ });
6500
6845
  printPlayLogLines({
6501
6846
  lines: getLogLinesFromLiveEvent(event),
6502
6847
  status: null,
@@ -6645,6 +6990,14 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6645
6990
  state,
6646
6991
  progress: input.progress
6647
6992
  });
6993
+ emitLiveDebugTableHints({
6994
+ event,
6995
+ playName: input.playName,
6996
+ runId: workflowId,
6997
+ jsonOutput: input.jsonOutput,
6998
+ state,
6999
+ progress: input.progress
7000
+ });
6648
7001
  if (!input.jsonOutput) {
6649
7002
  printPlayProgressLines({
6650
7003
  lines: getProgressLinesFromLiveEvent(event),
@@ -6700,6 +7053,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6700
7053
  });
6701
7054
  return waitForPlayCompletionByStream({
6702
7055
  client: input.client,
7056
+ playName: input.playName,
6703
7057
  workflowId: lastKnownWorkflowId,
6704
7058
  dashboardUrl,
6705
7059
  jsonOutput: input.jsonOutput,
@@ -6735,6 +7089,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6735
7089
  });
6736
7090
  return waitForPlayCompletionByStream({
6737
7091
  client: input.client,
7092
+ playName: input.playName,
6738
7093
  workflowId: lastKnownWorkflowId,
6739
7094
  dashboardUrl,
6740
7095
  jsonOutput: input.jsonOutput,
@@ -8118,6 +8473,10 @@ async function handlePlayCheck(args) {
8118
8473
  });
8119
8474
  const enrichedResult = {
8120
8475
  ...result,
8476
+ errors: result.valid ? result.errors : addPlayCheckRepairHints({
8477
+ errors: result.errors,
8478
+ sourceCode: graph.root.sourceCode
8479
+ }),
8121
8480
  toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
8122
8481
  };
8123
8482
  if (options.jsonOutput) {
@@ -8903,25 +9262,13 @@ function parsePlaySearchOptions(args) {
8903
9262
  const query = args[0]?.trim();
8904
9263
  if (!query) {
8905
9264
  throw new Error(
8906
- "Usage: deepline plays search <query> [--origin prebuilt|owned] [--compact] [--json]"
9265
+ "Usage: deepline plays search <query> [--compact] [--json]"
8907
9266
  );
8908
9267
  }
8909
- let origin;
8910
- for (let index = 1; index < args.length; index += 1) {
8911
- const arg = args[index];
8912
- if (arg === "--origin" && args[index + 1]) {
8913
- const rawOrigin = args[++index].trim().toLowerCase();
8914
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8915
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8916
- }
8917
- origin = rawOrigin;
8918
- }
8919
- }
8920
9268
  return {
8921
9269
  query,
8922
9270
  jsonOutput: argsWantJson(args),
8923
- compact: args.includes("--compact"),
8924
- origin
9271
+ compact: args.includes("--compact")
8925
9272
  };
8926
9273
  }
8927
9274
  function printPlayDescription(play) {
@@ -8970,10 +9317,10 @@ function printPlayDescription(play) {
8970
9317
  }
8971
9318
  }
8972
9319
  console.log(` Run: ${play.runCommand}`);
8973
- if (play.origin === "prebuilt" && !play.canEdit) {
8974
- console.log(
8975
- ` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
8976
- );
9320
+ const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
9321
+ if (cloneEditStarter) {
9322
+ console.log(` Clone/edit starter: ${cloneEditStarter.command}`);
9323
+ console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
8977
9324
  }
8978
9325
  }
8979
9326
  function compactPlaySchema(schema) {
@@ -9005,6 +9352,7 @@ function summarizePlayListItemForCli(play, options) {
9005
9352
  const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
9006
9353
  const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
9007
9354
  const runCommand2 = playRunCommand(play, { csvInput });
9355
+ const cloneEditStarter = buildCloneEditStarter(play);
9008
9356
  return {
9009
9357
  name: play.name,
9010
9358
  ...play.reference ? { reference: play.reference } : {},
@@ -9020,6 +9368,7 @@ function summarizePlayListItemForCli(play, options) {
9020
9368
  ...rowOutputSchema ? { rowOutputSchema } : {},
9021
9369
  runCommand: runCommand2,
9022
9370
  examples: [runCommand2],
9371
+ ...cloneEditStarter ? { cloneEditStarter } : {},
9023
9372
  currentPublishedVersion: play.currentPublishedVersion ?? null,
9024
9373
  isDraftDirty: play.isDraftDirty
9025
9374
  };
@@ -9035,7 +9384,6 @@ async function handlePlaySearch(args) {
9035
9384
  const client = new DeeplineClient();
9036
9385
  const plays = await client.searchPlays({
9037
9386
  query: options.query,
9038
- ...options.origin ? { origin: options.origin } : {},
9039
9387
  compact: options.compact
9040
9388
  });
9041
9389
  if (options.jsonOutput) {
@@ -9081,20 +9429,12 @@ function matchesPlayGrepQuery(value, query, mode) {
9081
9429
  async function handlePlayGrep(args) {
9082
9430
  const query = args[0]?.trim();
9083
9431
  if (!query) {
9084
- console.error("Usage: deepline plays grep <query> [--origin prebuilt|owned] [--compact] [--json]");
9432
+ console.error("Usage: deepline plays grep <query> [--mode all|any|phrase] [--compact] [--json]");
9085
9433
  return 1;
9086
9434
  }
9087
- let origin;
9088
9435
  let mode = "all";
9089
9436
  for (let index = 1; index < args.length; index += 1) {
9090
9437
  const arg = args[index];
9091
- if (arg === "--origin" && args[index + 1]) {
9092
- const rawOrigin = args[++index].trim().toLowerCase();
9093
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
9094
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
9095
- }
9096
- origin = rawOrigin;
9097
- }
9098
9438
  if (arg === "--mode" && args[index + 1]) {
9099
9439
  const rawMode = args[++index].trim().toLowerCase();
9100
9440
  mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
@@ -9104,13 +9444,8 @@ async function handlePlayGrep(args) {
9104
9444
  const client = new DeeplineClient();
9105
9445
  const plays = (await client.listPlays({
9106
9446
  grep: query,
9107
- grepMode: mode,
9108
- ...origin ? { origin } : {}
9109
- })).filter((play) => {
9110
- if (!origin) return true;
9111
- const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
9112
- return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
9113
- }).filter(
9447
+ grepMode: mode
9448
+ })).filter(
9114
9449
  (play) => matchesPlayGrepQuery(
9115
9450
  {
9116
9451
  name: play.name,
@@ -9131,8 +9466,7 @@ async function handlePlayGrep(args) {
9131
9466
  plays,
9132
9467
  count: plays.length,
9133
9468
  query,
9134
- grep: { mode, terms: parsePlayGrepTerms(query, mode) },
9135
- filters: { origin: origin ?? null }
9469
+ grep: { mode, terms: parsePlayGrepTerms(query, mode) }
9136
9470
  })}
9137
9471
  `);
9138
9472
  return 0;
@@ -9468,7 +9802,7 @@ Notes:
9468
9802
  Examples:
9469
9803
  deepline plays list
9470
9804
  deepline plays list --origin prebuilt --json
9471
- deepline plays search email --origin prebuilt --json
9805
+ deepline plays search email --json
9472
9806
  `
9473
9807
  ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9474
9808
  process.exitCode = await handlePlayList([
@@ -9480,20 +9814,18 @@ Examples:
9480
9814
  "after",
9481
9815
  `
9482
9816
  Notes:
9483
- Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
9484
- you need to narrow results. Use describe on a result before running it.
9817
+ Ranked discovery for workflows. Use describe on a result before running it.
9485
9818
  The grep alias is the same ranked retrieval surface with a more literal name
9486
9819
  for agents that are filtering the play registry.
9487
9820
 
9488
9821
  Examples:
9489
9822
  deepline plays search email
9490
- deepline plays grep "linkedin to email" --origin prebuilt --compact --json
9823
+ deepline plays grep "linkedin to email" --compact --json
9491
9824
  deepline plays describe person-linkedin-to-email --json
9492
9825
  `
9493
- ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9826
+ ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9494
9827
  process.exitCode = await handlePlaySearch([
9495
9828
  query,
9496
- ...options.origin ? ["--origin", options.origin] : [],
9497
9829
  ...options.compact ? ["--compact"] : [],
9498
9830
  ...options.json ? ["--json"] : []
9499
9831
  ]);
@@ -9509,14 +9841,13 @@ Notes:
9509
9841
  --mode all for AND.
9510
9842
 
9511
9843
  Examples:
9512
- deepline plays grep email --origin prebuilt --json
9513
- deepline plays grep "company contact" --origin prebuilt --mode all --json
9844
+ deepline plays grep email --json
9845
+ deepline plays grep "company contact" --mode all --json
9514
9846
  deepline plays describe prebuilt/company-to-contact --json
9515
9847
  `
9516
- ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--compact", "Emit compact schemas").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9848
+ ).option("--compact", "Emit compact schemas").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9517
9849
  process.exitCode = await handlePlayGrep([
9518
9850
  query,
9519
- ...options.origin ? ["--origin", options.origin] : [],
9520
9851
  ...options.compact ? ["--compact"] : [],
9521
9852
  ...options.mode ? ["--mode", options.mode] : [],
9522
9853
  ...options.json ? ["--json"] : []
@@ -9532,6 +9863,7 @@ Notes:
9532
9863
  Examples:
9533
9864
  deepline plays describe person-linkedin-to-email
9534
9865
  deepline plays describe person-linkedin-to-email --json
9866
+ deepline plays get prebuilt/person-linkedin-to-email --source --out ./person-linkedin-to-email.play.ts
9535
9867
  `
9536
9868
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
9537
9869
  process.exitCode = await handlePlayDescribe([
@@ -10356,6 +10688,11 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10356
10688
  const cost = recordField(tool, "cost");
10357
10689
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
10358
10690
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
10691
+ const starterScript = seedToolListScript({
10692
+ toolId,
10693
+ payload: samplePayloadForInputFields(inputFields),
10694
+ rows: []
10695
+ });
10359
10696
  return {
10360
10697
  schemaVersion: 1,
10361
10698
  toolId,
@@ -10380,7 +10717,16 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10380
10717
  extractedLists,
10381
10718
  extractedValues
10382
10719
  },
10383
- executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`
10720
+ executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`,
10721
+ starterScript: {
10722
+ path: starterScript.path,
10723
+ sourceCode: starterScript.sourceCode,
10724
+ projectDir: starterScript.projectDir,
10725
+ copyToProject: {
10726
+ macosLinux: starterScript.macCopyCommand,
10727
+ windowsPowerShell: starterScript.windowsCopyCommand
10728
+ }
10729
+ }
10384
10730
  };
10385
10731
  }
10386
10732
  function extractionContractEntries(entries) {
@@ -10422,6 +10768,13 @@ function printCompactToolContract(tool, requestedToolId) {
10422
10768
  }
10423
10769
  console.log("");
10424
10770
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
10771
+ const starterScript = isRecord4(contract.starterScript) ? contract.starterScript : {};
10772
+ const starterPath = stringField(starterScript, "path");
10773
+ if (starterPath) {
10774
+ console.log("");
10775
+ console.log(`Starter script: ${starterPath}`);
10776
+ console.log("Copy it into your project and replace the sample input with the proven probe payload.");
10777
+ }
10425
10778
  if (listGetters.length || valueGetters.length) {
10426
10779
  console.log("");
10427
10780
  console.log("Getters:");
@@ -10473,14 +10826,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
10473
10826
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
10474
10827
  const toolId = String(contract.toolId);
10475
10828
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10476
- const sampleInput = Object.fromEntries(
10477
- inputFields.slice(0, 4).flatMap((field) => {
10478
- if (!isRecord4(field)) return [];
10479
- const name = stringField(field, "name");
10480
- if (!name) return [];
10481
- return [[name, sampleValueForField(field)]];
10482
- })
10483
- );
10829
+ const sampleInput = samplePayloadForInputFields(inputFields);
10484
10830
  console.log(`Use in a play:`);
10485
10831
  console.log("```ts");
10486
10832
  console.log("const result = await ctx.tools.execute({");
@@ -10541,6 +10887,16 @@ function sampleValueForField(field) {
10541
10887
  if (type === "object") return {};
10542
10888
  return "...";
10543
10889
  }
10890
+ function samplePayloadForInputFields(fields) {
10891
+ return Object.fromEntries(
10892
+ fields.slice(0, 4).flatMap((field) => {
10893
+ if (!isRecord4(field)) return [];
10894
+ const name = stringField(field, "name");
10895
+ if (!name) return [];
10896
+ return [[name, sampleValueForField(field)]];
10897
+ })
10898
+ );
10899
+ }
10544
10900
  function stableStepIdForTool(toolId) {
10545
10901
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
10546
10902
  }
@@ -10553,6 +10909,12 @@ function playResultExpression(entry) {
10553
10909
  }
10554
10910
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
10555
10911
  const toolId = String(tool.toolId || requestedToolId);
10912
+ const inputFields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
10913
+ const starterScript = seedToolListScript({
10914
+ toolId,
10915
+ payload: samplePayloadForInputFields(inputFields),
10916
+ rows: []
10917
+ });
10556
10918
  const {
10557
10919
  cost: _cost,
10558
10920
  deeplineCreditsPerPricingUnit: _deeplineCreditsPerPricingUnit,
@@ -10572,12 +10934,48 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
10572
10934
  toolId,
10573
10935
  provider: tool.provider,
10574
10936
  displayName: tool.displayName,
10937
+ usageGuidance: usageGuidanceWithAccessDefaults(recordField(tool, "usageGuidance", "usage_guidance")),
10575
10938
  runtimeOutputHelp: {
10576
- contract: "tools describe shows the declared schema and semantic getters; it is not an observed provider response.",
10939
+ contract: "tools describe shows declared schema and Deepline getters; it is not an observed provider response.",
10940
+ getterScope: "extractedValues/extractedLists .get() only works for declared Deepline getters listed in usageGuidance.toolExecutionResult.",
10941
+ rawToolResponse: "Use toolExecutionResult.toolResponse.raw for provider/tool-specific fields, fields in outputSchema that are not declared getters, and debugging.",
10942
+ invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
10577
10943
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
10578
10944
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
10579
10945
  forPlayGetterBugs: "Run the play, then inspect the emitted table commands from runs get. Use deepline db query against the run tables before editing getters.",
10580
10946
  executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run tables."
10947
+ },
10948
+ starterScript: {
10949
+ path: starterScript.path,
10950
+ sourceCode: starterScript.sourceCode,
10951
+ projectDir: starterScript.projectDir,
10952
+ copyToProject: {
10953
+ macosLinux: starterScript.macCopyCommand,
10954
+ windowsPowerShell: starterScript.windowsCopyCommand
10955
+ }
10956
+ }
10957
+ };
10958
+ }
10959
+ function usageGuidanceWithAccessDefaults(usageGuidance) {
10960
+ if (Object.keys(usageGuidance).length === 0) return usageGuidance;
10961
+ const existingAccess = recordField(usageGuidance, "access");
10962
+ return {
10963
+ ...usageGuidance,
10964
+ access: {
10965
+ extractedLists: {
10966
+ expression: "toolExecutionResult.extractedLists.<name>.get()",
10967
+ meaning: "Declared Deepline list extractors only."
10968
+ },
10969
+ extractedValues: {
10970
+ expression: "toolExecutionResult.extractedValues.<name>.get()",
10971
+ meaning: "Declared Deepline scalar extractors only."
10972
+ },
10973
+ rawToolResponse: {
10974
+ expression: "toolExecutionResult.toolResponse.raw",
10975
+ meaning: "Raw tool response for provider/tool-specific fields and debugging."
10976
+ },
10977
+ invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter; use toolResponse.raw for provider/tool-specific fields.",
10978
+ ...existingAccess
10581
10979
  }
10582
10980
  };
10583
10981
  }
@@ -10794,6 +11192,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
10794
11192
  (0, import_node_fs10.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
10795
11193
  return {
10796
11194
  path: scriptPath,
11195
+ sourceCode: script,
10797
11196
  projectDir,
10798
11197
  macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
10799
11198
  windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`