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.
@@ -197,10 +197,10 @@ function resolveConfig(options) {
197
197
 
198
198
  // src/release.ts
199
199
  var SDK_RELEASE = {
200
- version: "0.1.55",
201
- apiContract: "2026-05-run-response-package",
200
+ version: "0.1.56",
201
+ apiContract: "2026-05-play-tool-describe-starters",
202
202
  supportPolicy: {
203
- latest: "0.1.55",
203
+ latest: "0.1.56",
204
204
  minimumSupported: "0.1.53",
205
205
  deprecatedBelow: "0.1.53"
206
206
  }
@@ -704,6 +704,23 @@ var DeeplineClient = class {
704
704
  }
705
705
  return `deepline plays run ${target} --input '{...}' --watch`;
706
706
  }
707
+ starterPlayPath(play) {
708
+ const target = play.reference || play.name;
709
+ const unqualifiedName = target.split("/").pop() || play.name;
710
+ const safeName = unqualifiedName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
711
+ return `./${safeName || "play"}.play.ts`;
712
+ }
713
+ playCloneEditStarter(play) {
714
+ const readonlyPrebuilt = (play.origin === "prebuilt" || play.ownerType === "deepline") && !play.canEdit;
715
+ if (!play.canClone && !readonlyPrebuilt) return void 0;
716
+ const target = play.reference || play.name;
717
+ const path = this.starterPlayPath(play);
718
+ return {
719
+ path,
720
+ command: `deepline plays get ${target} --source --out ${path}`,
721
+ checkCommand: `deepline plays check ${path}`
722
+ };
723
+ }
707
724
  summarizePlayListItem(play, options) {
708
725
  const aliases = play.aliases?.length ? play.aliases : [play.name];
709
726
  const csvInput = this.schemaMetadata(play.inputSchema, "csvInput");
@@ -712,6 +729,7 @@ var DeeplineClient = class {
712
729
  "rowOutputSchema"
713
730
  );
714
731
  const runCommand2 = this.playRunCommand(play, { csvInput });
732
+ const cloneEditStarter = this.playCloneEditStarter(play);
715
733
  return {
716
734
  name: play.name,
717
735
  ...play.reference ? { reference: play.reference } : {},
@@ -727,6 +745,7 @@ var DeeplineClient = class {
727
745
  ...rowOutputSchema ? { rowOutputSchema } : {},
728
746
  runCommand: runCommand2,
729
747
  examples: [runCommand2],
748
+ ...cloneEditStarter ? { cloneEditStarter } : {},
730
749
  currentPublishedVersion: play.currentPublishedVersion ?? null,
731
750
  isDraftDirty: play.isDraftDirty
732
751
  };
@@ -1429,7 +1448,6 @@ var DeeplineClient = class {
1429
1448
  async searchPlays(options) {
1430
1449
  const params = new URLSearchParams();
1431
1450
  params.set("search", options.query.trim());
1432
- if (options.origin) params.set("origin", options.origin);
1433
1451
  const response = await this.http.get(
1434
1452
  `/api/v2/plays?${params.toString()}`
1435
1453
  );
@@ -5711,6 +5729,204 @@ function createCliProgress(enabled) {
5711
5729
  return progress;
5712
5730
  }
5713
5731
 
5732
+ // ../shared_libs/plays/row-identity.ts
5733
+ var POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
5734
+ var PLAY_NAME_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
5735
+ var MAP_KEY_NAMESPACE_MAX_LENGTH = POSTGRES_IDENTIFIER_MAX_LENGTH;
5736
+ var SHA256_INITIAL_HASH = [
5737
+ 1779033703,
5738
+ 3144134277,
5739
+ 1013904242,
5740
+ 2773480762,
5741
+ 1359893119,
5742
+ 2600822924,
5743
+ 528734635,
5744
+ 1541459225
5745
+ ];
5746
+ var SHA256_ROUND_CONSTANTS = [
5747
+ 1116352408,
5748
+ 1899447441,
5749
+ 3049323471,
5750
+ 3921009573,
5751
+ 961987163,
5752
+ 1508970993,
5753
+ 2453635748,
5754
+ 2870763221,
5755
+ 3624381080,
5756
+ 310598401,
5757
+ 607225278,
5758
+ 1426881987,
5759
+ 1925078388,
5760
+ 2162078206,
5761
+ 2614888103,
5762
+ 3248222580,
5763
+ 3835390401,
5764
+ 4022224774,
5765
+ 264347078,
5766
+ 604807628,
5767
+ 770255983,
5768
+ 1249150122,
5769
+ 1555081692,
5770
+ 1996064986,
5771
+ 2554220882,
5772
+ 2821834349,
5773
+ 2952996808,
5774
+ 3210313671,
5775
+ 3336571891,
5776
+ 3584528711,
5777
+ 113926993,
5778
+ 338241895,
5779
+ 666307205,
5780
+ 773529912,
5781
+ 1294757372,
5782
+ 1396182291,
5783
+ 1695183700,
5784
+ 1986661051,
5785
+ 2177026350,
5786
+ 2456956037,
5787
+ 2730485921,
5788
+ 2820302411,
5789
+ 3259730800,
5790
+ 3345764771,
5791
+ 3516065817,
5792
+ 3600352804,
5793
+ 4094571909,
5794
+ 275423344,
5795
+ 430227734,
5796
+ 506948616,
5797
+ 659060556,
5798
+ 883997877,
5799
+ 958139571,
5800
+ 1322822218,
5801
+ 1537002063,
5802
+ 1747873779,
5803
+ 1955562222,
5804
+ 2024104815,
5805
+ 2227730452,
5806
+ 2361852424,
5807
+ 2428436474,
5808
+ 2756734187,
5809
+ 3204031479,
5810
+ 3329325298
5811
+ ];
5812
+ function rightRotate32(value, bits) {
5813
+ return value >>> bits | value << 32 - bits;
5814
+ }
5815
+ function sha256Hex(input) {
5816
+ const bytes = Array.from(new TextEncoder().encode(input));
5817
+ const bitLength = bytes.length * 8;
5818
+ bytes.push(128);
5819
+ while (bytes.length % 64 !== 56) {
5820
+ bytes.push(0);
5821
+ }
5822
+ const highBits = Math.floor(bitLength / 4294967296);
5823
+ const lowBits = bitLength >>> 0;
5824
+ bytes.push(
5825
+ highBits >>> 24 & 255,
5826
+ highBits >>> 16 & 255,
5827
+ highBits >>> 8 & 255,
5828
+ highBits & 255,
5829
+ lowBits >>> 24 & 255,
5830
+ lowBits >>> 16 & 255,
5831
+ lowBits >>> 8 & 255,
5832
+ lowBits & 255
5833
+ );
5834
+ const hash = [...SHA256_INITIAL_HASH];
5835
+ const words = new Array(64).fill(0);
5836
+ for (let offset = 0; offset < bytes.length; offset += 64) {
5837
+ for (let index = 0; index < 16; index += 1) {
5838
+ const wordOffset = offset + index * 4;
5839
+ words[index] = (bytes[wordOffset] ?? 0) << 24 | (bytes[wordOffset + 1] ?? 0) << 16 | (bytes[wordOffset + 2] ?? 0) << 8 | (bytes[wordOffset + 3] ?? 0);
5840
+ }
5841
+ for (let index = 16; index < 64; index += 1) {
5842
+ const s0 = rightRotate32(words[index - 15], 7) ^ rightRotate32(words[index - 15], 18) ^ words[index - 15] >>> 3;
5843
+ const s1 = rightRotate32(words[index - 2], 17) ^ rightRotate32(words[index - 2], 19) ^ words[index - 2] >>> 10;
5844
+ words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
5845
+ }
5846
+ let [a, b, c, d, e, f, g, h] = hash;
5847
+ for (let index = 0; index < 64; index += 1) {
5848
+ const s1 = rightRotate32(e, 6) ^ rightRotate32(e, 11) ^ rightRotate32(e, 25);
5849
+ const ch = e & f ^ ~e & g;
5850
+ const temp1 = h + s1 + ch + SHA256_ROUND_CONSTANTS[index] + words[index] >>> 0;
5851
+ const s0 = rightRotate32(a, 2) ^ rightRotate32(a, 13) ^ rightRotate32(a, 22);
5852
+ const maj = a & b ^ a & c ^ b & c;
5853
+ const temp2 = s0 + maj >>> 0;
5854
+ h = g;
5855
+ g = f;
5856
+ f = e;
5857
+ e = d + temp1 >>> 0;
5858
+ d = c;
5859
+ c = b;
5860
+ b = a;
5861
+ a = temp1 + temp2 >>> 0;
5862
+ }
5863
+ hash[0] = hash[0] + a >>> 0;
5864
+ hash[1] = hash[1] + b >>> 0;
5865
+ hash[2] = hash[2] + c >>> 0;
5866
+ hash[3] = hash[3] + d >>> 0;
5867
+ hash[4] = hash[4] + e >>> 0;
5868
+ hash[5] = hash[5] + f >>> 0;
5869
+ hash[6] = hash[6] + g >>> 0;
5870
+ hash[7] = hash[7] + h >>> 0;
5871
+ }
5872
+ return hash.map((word) => word.toString(16).padStart(8, "0")).join("");
5873
+ }
5874
+ function sanitizeIdentifierPart(value) {
5875
+ return value.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
5876
+ }
5877
+ function validateIdentifierPart(rawValue, label, maxLength) {
5878
+ const sanitized = sanitizeIdentifierPart(rawValue);
5879
+ if (!sanitized) {
5880
+ throw new Error(
5881
+ `${label} must contain at least one letter or number after normalization. Use only letters, numbers, underscores, or hyphens.`
5882
+ );
5883
+ }
5884
+ if (sanitized.length > maxLength) {
5885
+ throw new Error(
5886
+ `${label} is too long after normalization (${sanitized.length}/${maxLength}). Shorten it to ${maxLength} characters or fewer. Normalized value: "${sanitized}".`
5887
+ );
5888
+ }
5889
+ return sanitized;
5890
+ }
5891
+ function normalizePlayName(value) {
5892
+ if (value.includes("/")) {
5893
+ throw new Error(
5894
+ 'Play name cannot contain "/". Slash is reserved for qualified play references like "prebuilt/example" or "self/example".'
5895
+ );
5896
+ }
5897
+ return validateIdentifierPart(value, "Play name", PLAY_NAME_MAX_LENGTH);
5898
+ }
5899
+ function normalizePlayNameForSheet(value) {
5900
+ if (!value.includes("/")) {
5901
+ return normalizePlayName(value);
5902
+ }
5903
+ const digest = sha256Hex(value).slice(0, 12);
5904
+ const normalizedReference = sanitizeIdentifierPart(
5905
+ value.replace(/\//g, "__")
5906
+ );
5907
+ const prefixLength = Math.max(1, PLAY_NAME_MAX_LENGTH - digest.length - 1);
5908
+ const prefix = normalizedReference.slice(0, prefixLength).replace(/_+$/g, "") || "qualified_play";
5909
+ return `${prefix}_${digest}`;
5910
+ }
5911
+ function normalizeTableNamespace(value) {
5912
+ return validateIdentifierPart(
5913
+ value,
5914
+ "ctx.map() key",
5915
+ MAP_KEY_NAMESPACE_MAX_LENGTH
5916
+ );
5917
+ }
5918
+ function validatePlaySheetTableName(playName, tableNamespace) {
5919
+ const playSegment = normalizePlayNameForSheet(playName);
5920
+ const keySegment = normalizeTableNamespace(tableNamespace);
5921
+ const resolved = `${playSegment}_${keySegment}`;
5922
+ if (resolved.length > POSTGRES_IDENTIFIER_MAX_LENGTH) {
5923
+ throw new Error(
5924
+ `Play sheet table name is too long after normalization (${resolved.length}/63). Shorten the play name or ctx.map() key. Resolved table name: "${resolved}".`
5925
+ );
5926
+ }
5927
+ return resolved;
5928
+ }
5929
+
5714
5930
  // src/cli/trace.ts
5715
5931
  var cliTraceStartedAt = Date.now();
5716
5932
  function isTruthyEnv(value) {
@@ -5759,6 +5975,31 @@ async function traceCliSpan(phase, fields, run) {
5759
5975
  }
5760
5976
  }
5761
5977
 
5978
+ // src/cli/play-check-hints.ts
5979
+ 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.";
5980
+ function sourceLineForError(sourceCode, error) {
5981
+ const match = error.match(/:(\d+):(\d+)\s/);
5982
+ const lineNumber = match?.[1] ? Number(match[1]) : NaN;
5983
+ if (!Number.isInteger(lineNumber) || lineNumber < 1) return "";
5984
+ return sourceCode.split(/\r?\n/)[lineNumber - 1] ?? "";
5985
+ }
5986
+ function looksLikeInvalidExtractedGetter(error, sourceLine) {
5987
+ if (!/Property '[^']+' does not exist on type/.test(error)) return false;
5988
+ return /\bextracted(?:Values|Lists)\s*\./.test(sourceLine);
5989
+ }
5990
+ function addPlayCheckRepairHints(input) {
5991
+ let addedHint = false;
5992
+ return input.errors.map((error) => {
5993
+ const line = sourceLineForError(input.sourceCode, error);
5994
+ if (addedHint || !looksLikeInvalidExtractedGetter(error, line) || error.includes(EXTRACTED_GETTER_ERROR_HINT)) {
5995
+ return error;
5996
+ }
5997
+ addedHint = true;
5998
+ return `${error}
5999
+ ${EXTRACTED_GETTER_ERROR_HINT}`;
6000
+ });
6001
+ }
6002
+
5762
6003
  // src/cli/commands/play.ts
5763
6004
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
5764
6005
  "--json",
@@ -5838,9 +6079,23 @@ function formatPlayListReference(play) {
5838
6079
  return play.reference || play.name;
5839
6080
  }
5840
6081
  function defaultMaterializedPlayPath(reference) {
6082
+ return resolve9(defaultStarterPlayPath(reference));
6083
+ }
6084
+ function defaultStarterPlayPath(reference) {
5841
6085
  const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
5842
6086
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5843
- return resolve9(`${safeName || "play"}.play.ts`);
6087
+ return `./${safeName || "play"}.play.ts`;
6088
+ }
6089
+ function buildCloneEditStarter(play) {
6090
+ const readonlyPrebuilt = (play.origin === "prebuilt" || play.ownerType === "deepline") && !play.canEdit;
6091
+ if (!play.canClone && !readonlyPrebuilt) return void 0;
6092
+ const reference = play.reference || play.name;
6093
+ const path = defaultStarterPlayPath(reference);
6094
+ return {
6095
+ path,
6096
+ command: `deepline plays get ${reference} --source --out ${path}`,
6097
+ checkCommand: `deepline plays check ${path}`
6098
+ };
5844
6099
  }
5845
6100
  function materializeRemotePlaySource(input) {
5846
6101
  if (isFileTarget(input.target)) {
@@ -6286,6 +6541,8 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
6286
6541
  "cancelled"
6287
6542
  ]);
6288
6543
  var PLAY_START_TRANSIENT_RETRY_DELAYS_MS = [500, 1500];
6544
+ var PLAY_CUSTOMER_STORAGE_SCHEMA_NAME = "storage";
6545
+ var PLAY_INTERNAL_STEP_RECEIPT_TABLE = "_deepline_step_receipts";
6289
6546
  function getEventPayload(event) {
6290
6547
  return event.payload && typeof event.payload === "object" ? event.payload : {};
6291
6548
  }
@@ -6339,6 +6596,86 @@ function getLogLinesFromLiveEvent(event) {
6339
6596
  const lines = getEventPayload(event).lines;
6340
6597
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
6341
6598
  }
6599
+ function quoteSqlIdentifier(identifier) {
6600
+ return `"${identifier.replace(/"/g, '""')}"`;
6601
+ }
6602
+ function quoteSqlLiteral(value) {
6603
+ return `'${value.replace(/'/g, "''")}'`;
6604
+ }
6605
+ function buildDebugDbQueryCommand(sql) {
6606
+ return `deepline db query --sql ${shellSingleQuote(sql)} --max-rows 20 --json`;
6607
+ }
6608
+ function buildStepReceiptsDebugCommand(runId) {
6609
+ const table = `${quoteSqlIdentifier(
6610
+ PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
6611
+ )}.${quoteSqlIdentifier(PLAY_INTERNAL_STEP_RECEIPT_TABLE)}`;
6612
+ 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`;
6613
+ return buildDebugDbQueryCommand(sql);
6614
+ }
6615
+ function buildMapTableDebugCommand(input) {
6616
+ try {
6617
+ const tableName = validatePlaySheetTableName(
6618
+ input.playName,
6619
+ input.tableNamespace
6620
+ );
6621
+ const table = `${quoteSqlIdentifier(
6622
+ PLAY_CUSTOMER_STORAGE_SCHEMA_NAME
6623
+ )}.${quoteSqlIdentifier(tableName)}`;
6624
+ const sql = `select * from ${table} where _run_id = ${quoteSqlLiteral(input.runId)} limit 20`;
6625
+ return buildDebugDbQueryCommand(sql);
6626
+ } catch {
6627
+ return null;
6628
+ }
6629
+ }
6630
+ function extractTableNamespaceFromLiveEvent(event) {
6631
+ const payload = getEventPayload(event);
6632
+ const candidates = [
6633
+ payload.artifactTableNamespace,
6634
+ payload.tableNamespace,
6635
+ payload.mapNodeId
6636
+ ];
6637
+ for (const candidate of candidates) {
6638
+ if (typeof candidate === "string" && candidate.trim()) {
6639
+ return candidate.trim();
6640
+ }
6641
+ }
6642
+ return null;
6643
+ }
6644
+ function emitLiveDebugTableHints(input) {
6645
+ if (input.jsonOutput || !input.runId || input.runId === "pending") {
6646
+ return;
6647
+ }
6648
+ input.state.emittedDebugKeys ??= /* @__PURE__ */ new Set();
6649
+ const receiptsKey = `receipts:${input.runId}`;
6650
+ if (!input.state.emittedDebugKeys.has(receiptsKey)) {
6651
+ input.state.emittedDebugKeys.add(receiptsKey);
6652
+ input.progress.writeLine(
6653
+ `Debug top-level outputs: ${buildStepReceiptsDebugCommand(input.runId)}`,
6654
+ process.stdout
6655
+ );
6656
+ }
6657
+ const tableNamespace = extractTableNamespaceFromLiveEvent(input.event);
6658
+ if (!tableNamespace) {
6659
+ return;
6660
+ }
6661
+ const tableKey = `table:${input.runId}:${tableNamespace}`;
6662
+ if (input.state.emittedDebugKeys.has(tableKey)) {
6663
+ return;
6664
+ }
6665
+ const command = buildMapTableDebugCommand({
6666
+ playName: input.playName,
6667
+ runId: input.runId,
6668
+ tableNamespace
6669
+ });
6670
+ if (!command) {
6671
+ return;
6672
+ }
6673
+ input.state.emittedDebugKeys.add(tableKey);
6674
+ input.progress.writeLine(
6675
+ `Debug rows for ${tableNamespace}: ${command}`,
6676
+ process.stdout
6677
+ );
6678
+ }
6342
6679
  function describeLiveEventPhase(event) {
6343
6680
  const payload = getEventPayload(event);
6344
6681
  if (event.type === "play.run.status") {
@@ -6491,6 +6828,14 @@ async function waitForPlayCompletionByStream(input) {
6491
6828
  lastPhase = phase;
6492
6829
  input.progress.phase(phase);
6493
6830
  }
6831
+ emitLiveDebugTableHints({
6832
+ event,
6833
+ playName: input.playName,
6834
+ runId: input.workflowId,
6835
+ jsonOutput: input.jsonOutput,
6836
+ state: input.state,
6837
+ progress: input.progress
6838
+ });
6494
6839
  printPlayLogLines({
6495
6840
  lines: getLogLinesFromLiveEvent(event),
6496
6841
  status: null,
@@ -6639,6 +6984,14 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6639
6984
  state,
6640
6985
  progress: input.progress
6641
6986
  });
6987
+ emitLiveDebugTableHints({
6988
+ event,
6989
+ playName: input.playName,
6990
+ runId: workflowId,
6991
+ jsonOutput: input.jsonOutput,
6992
+ state,
6993
+ progress: input.progress
6994
+ });
6642
6995
  if (!input.jsonOutput) {
6643
6996
  printPlayProgressLines({
6644
6997
  lines: getProgressLinesFromLiveEvent(event),
@@ -6694,6 +7047,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6694
7047
  });
6695
7048
  return waitForPlayCompletionByStream({
6696
7049
  client: input.client,
7050
+ playName: input.playName,
6697
7051
  workflowId: lastKnownWorkflowId,
6698
7052
  dashboardUrl,
6699
7053
  jsonOutput: input.jsonOutput,
@@ -6729,6 +7083,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6729
7083
  });
6730
7084
  return waitForPlayCompletionByStream({
6731
7085
  client: input.client,
7086
+ playName: input.playName,
6732
7087
  workflowId: lastKnownWorkflowId,
6733
7088
  dashboardUrl,
6734
7089
  jsonOutput: input.jsonOutput,
@@ -8112,6 +8467,10 @@ async function handlePlayCheck(args) {
8112
8467
  });
8113
8468
  const enrichedResult = {
8114
8469
  ...result,
8470
+ errors: result.valid ? result.errors : addPlayCheckRepairHints({
8471
+ errors: result.errors,
8472
+ sourceCode: graph.root.sourceCode
8473
+ }),
8115
8474
  toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
8116
8475
  };
8117
8476
  if (options.jsonOutput) {
@@ -8897,25 +9256,13 @@ function parsePlaySearchOptions(args) {
8897
9256
  const query = args[0]?.trim();
8898
9257
  if (!query) {
8899
9258
  throw new Error(
8900
- "Usage: deepline plays search <query> [--origin prebuilt|owned] [--compact] [--json]"
9259
+ "Usage: deepline plays search <query> [--compact] [--json]"
8901
9260
  );
8902
9261
  }
8903
- let origin;
8904
- for (let index = 1; index < args.length; index += 1) {
8905
- const arg = args[index];
8906
- if (arg === "--origin" && args[index + 1]) {
8907
- const rawOrigin = args[++index].trim().toLowerCase();
8908
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8909
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8910
- }
8911
- origin = rawOrigin;
8912
- }
8913
- }
8914
9262
  return {
8915
9263
  query,
8916
9264
  jsonOutput: argsWantJson(args),
8917
- compact: args.includes("--compact"),
8918
- origin
9265
+ compact: args.includes("--compact")
8919
9266
  };
8920
9267
  }
8921
9268
  function printPlayDescription(play) {
@@ -8964,10 +9311,10 @@ function printPlayDescription(play) {
8964
9311
  }
8965
9312
  }
8966
9313
  console.log(` Run: ${play.runCommand}`);
8967
- if (play.origin === "prebuilt" && !play.canEdit) {
8968
- console.log(
8969
- ` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
8970
- );
9314
+ const cloneEditStarter = play.cloneEditStarter ?? buildCloneEditStarter(play);
9315
+ if (cloneEditStarter) {
9316
+ console.log(` Clone/edit starter: ${cloneEditStarter.command}`);
9317
+ console.log(` Check starter: ${cloneEditStarter.checkCommand}`);
8971
9318
  }
8972
9319
  }
8973
9320
  function compactPlaySchema(schema) {
@@ -8999,6 +9346,7 @@ function summarizePlayListItemForCli(play, options) {
8999
9346
  const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
9000
9347
  const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
9001
9348
  const runCommand2 = playRunCommand(play, { csvInput });
9349
+ const cloneEditStarter = buildCloneEditStarter(play);
9002
9350
  return {
9003
9351
  name: play.name,
9004
9352
  ...play.reference ? { reference: play.reference } : {},
@@ -9014,6 +9362,7 @@ function summarizePlayListItemForCli(play, options) {
9014
9362
  ...rowOutputSchema ? { rowOutputSchema } : {},
9015
9363
  runCommand: runCommand2,
9016
9364
  examples: [runCommand2],
9365
+ ...cloneEditStarter ? { cloneEditStarter } : {},
9017
9366
  currentPublishedVersion: play.currentPublishedVersion ?? null,
9018
9367
  isDraftDirty: play.isDraftDirty
9019
9368
  };
@@ -9029,7 +9378,6 @@ async function handlePlaySearch(args) {
9029
9378
  const client = new DeeplineClient();
9030
9379
  const plays = await client.searchPlays({
9031
9380
  query: options.query,
9032
- ...options.origin ? { origin: options.origin } : {},
9033
9381
  compact: options.compact
9034
9382
  });
9035
9383
  if (options.jsonOutput) {
@@ -9075,20 +9423,12 @@ function matchesPlayGrepQuery(value, query, mode) {
9075
9423
  async function handlePlayGrep(args) {
9076
9424
  const query = args[0]?.trim();
9077
9425
  if (!query) {
9078
- console.error("Usage: deepline plays grep <query> [--origin prebuilt|owned] [--compact] [--json]");
9426
+ console.error("Usage: deepline plays grep <query> [--mode all|any|phrase] [--compact] [--json]");
9079
9427
  return 1;
9080
9428
  }
9081
- let origin;
9082
9429
  let mode = "all";
9083
9430
  for (let index = 1; index < args.length; index += 1) {
9084
9431
  const arg = args[index];
9085
- if (arg === "--origin" && args[index + 1]) {
9086
- const rawOrigin = args[++index].trim().toLowerCase();
9087
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
9088
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
9089
- }
9090
- origin = rawOrigin;
9091
- }
9092
9432
  if (arg === "--mode" && args[index + 1]) {
9093
9433
  const rawMode = args[++index].trim().toLowerCase();
9094
9434
  mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
@@ -9098,13 +9438,8 @@ async function handlePlayGrep(args) {
9098
9438
  const client = new DeeplineClient();
9099
9439
  const plays = (await client.listPlays({
9100
9440
  grep: query,
9101
- grepMode: mode,
9102
- ...origin ? { origin } : {}
9103
- })).filter((play) => {
9104
- if (!origin) return true;
9105
- const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
9106
- return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
9107
- }).filter(
9441
+ grepMode: mode
9442
+ })).filter(
9108
9443
  (play) => matchesPlayGrepQuery(
9109
9444
  {
9110
9445
  name: play.name,
@@ -9125,8 +9460,7 @@ async function handlePlayGrep(args) {
9125
9460
  plays,
9126
9461
  count: plays.length,
9127
9462
  query,
9128
- grep: { mode, terms: parsePlayGrepTerms(query, mode) },
9129
- filters: { origin: origin ?? null }
9463
+ grep: { mode, terms: parsePlayGrepTerms(query, mode) }
9130
9464
  })}
9131
9465
  `);
9132
9466
  return 0;
@@ -9462,7 +9796,7 @@ Notes:
9462
9796
  Examples:
9463
9797
  deepline plays list
9464
9798
  deepline plays list --origin prebuilt --json
9465
- deepline plays search email --origin prebuilt --json
9799
+ deepline plays search email --json
9466
9800
  `
9467
9801
  ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9468
9802
  process.exitCode = await handlePlayList([
@@ -9474,20 +9808,18 @@ Examples:
9474
9808
  "after",
9475
9809
  `
9476
9810
  Notes:
9477
- Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
9478
- you need to narrow results. Use describe on a result before running it.
9811
+ Ranked discovery for workflows. Use describe on a result before running it.
9479
9812
  The grep alias is the same ranked retrieval surface with a more literal name
9480
9813
  for agents that are filtering the play registry.
9481
9814
 
9482
9815
  Examples:
9483
9816
  deepline plays search email
9484
- deepline plays grep "linkedin to email" --origin prebuilt --compact --json
9817
+ deepline plays grep "linkedin to email" --compact --json
9485
9818
  deepline plays describe person-linkedin-to-email --json
9486
9819
  `
9487
- ).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) => {
9820
+ ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
9488
9821
  process.exitCode = await handlePlaySearch([
9489
9822
  query,
9490
- ...options.origin ? ["--origin", options.origin] : [],
9491
9823
  ...options.compact ? ["--compact"] : [],
9492
9824
  ...options.json ? ["--json"] : []
9493
9825
  ]);
@@ -9503,14 +9835,13 @@ Notes:
9503
9835
  --mode all for AND.
9504
9836
 
9505
9837
  Examples:
9506
- deepline plays grep email --origin prebuilt --json
9507
- deepline plays grep "company contact" --origin prebuilt --mode all --json
9838
+ deepline plays grep email --json
9839
+ deepline plays grep "company contact" --mode all --json
9508
9840
  deepline plays describe prebuilt/company-to-contact --json
9509
9841
  `
9510
- ).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) => {
9842
+ ).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) => {
9511
9843
  process.exitCode = await handlePlayGrep([
9512
9844
  query,
9513
- ...options.origin ? ["--origin", options.origin] : [],
9514
9845
  ...options.compact ? ["--compact"] : [],
9515
9846
  ...options.mode ? ["--mode", options.mode] : [],
9516
9847
  ...options.json ? ["--json"] : []
@@ -9526,6 +9857,7 @@ Notes:
9526
9857
  Examples:
9527
9858
  deepline plays describe person-linkedin-to-email
9528
9859
  deepline plays describe person-linkedin-to-email --json
9860
+ deepline plays get prebuilt/person-linkedin-to-email --source --out ./person-linkedin-to-email.play.ts
9529
9861
  `
9530
9862
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
9531
9863
  process.exitCode = await handlePlayDescribe([
@@ -10350,6 +10682,11 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10350
10682
  const cost = recordField(tool, "cost");
10351
10683
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
10352
10684
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
10685
+ const starterScript = seedToolListScript({
10686
+ toolId,
10687
+ payload: samplePayloadForInputFields(inputFields),
10688
+ rows: []
10689
+ });
10353
10690
  return {
10354
10691
  schemaVersion: 1,
10355
10692
  toolId,
@@ -10374,7 +10711,16 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10374
10711
  extractedLists,
10375
10712
  extractedValues
10376
10713
  },
10377
- executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`
10714
+ executeCommand: `deepline tools execute ${toolId} --input '{...}' --json`,
10715
+ starterScript: {
10716
+ path: starterScript.path,
10717
+ sourceCode: starterScript.sourceCode,
10718
+ projectDir: starterScript.projectDir,
10719
+ copyToProject: {
10720
+ macosLinux: starterScript.macCopyCommand,
10721
+ windowsPowerShell: starterScript.windowsCopyCommand
10722
+ }
10723
+ }
10378
10724
  };
10379
10725
  }
10380
10726
  function extractionContractEntries(entries) {
@@ -10416,6 +10762,13 @@ function printCompactToolContract(tool, requestedToolId) {
10416
10762
  }
10417
10763
  console.log("");
10418
10764
  printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
10765
+ const starterScript = isRecord4(contract.starterScript) ? contract.starterScript : {};
10766
+ const starterPath = stringField(starterScript, "path");
10767
+ if (starterPath) {
10768
+ console.log("");
10769
+ console.log(`Starter script: ${starterPath}`);
10770
+ console.log("Copy it into your project and replace the sample input with the proven probe payload.");
10771
+ }
10419
10772
  if (listGetters.length || valueGetters.length) {
10420
10773
  console.log("");
10421
10774
  console.log("Getters:");
@@ -10467,14 +10820,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
10467
10820
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
10468
10821
  const toolId = String(contract.toolId);
10469
10822
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10470
- const sampleInput = Object.fromEntries(
10471
- inputFields.slice(0, 4).flatMap((field) => {
10472
- if (!isRecord4(field)) return [];
10473
- const name = stringField(field, "name");
10474
- if (!name) return [];
10475
- return [[name, sampleValueForField(field)]];
10476
- })
10477
- );
10823
+ const sampleInput = samplePayloadForInputFields(inputFields);
10478
10824
  console.log(`Use in a play:`);
10479
10825
  console.log("```ts");
10480
10826
  console.log("const result = await ctx.tools.execute({");
@@ -10535,6 +10881,16 @@ function sampleValueForField(field) {
10535
10881
  if (type === "object") return {};
10536
10882
  return "...";
10537
10883
  }
10884
+ function samplePayloadForInputFields(fields) {
10885
+ return Object.fromEntries(
10886
+ fields.slice(0, 4).flatMap((field) => {
10887
+ if (!isRecord4(field)) return [];
10888
+ const name = stringField(field, "name");
10889
+ if (!name) return [];
10890
+ return [[name, sampleValueForField(field)]];
10891
+ })
10892
+ );
10893
+ }
10538
10894
  function stableStepIdForTool(toolId) {
10539
10895
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
10540
10896
  }
@@ -10547,6 +10903,12 @@ function playResultExpression(entry) {
10547
10903
  }
10548
10904
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
10549
10905
  const toolId = String(tool.toolId || requestedToolId);
10906
+ const inputFields = toolInputFieldsForDisplay(recordField(tool, "inputSchema", "input_schema"));
10907
+ const starterScript = seedToolListScript({
10908
+ toolId,
10909
+ payload: samplePayloadForInputFields(inputFields),
10910
+ rows: []
10911
+ });
10550
10912
  const {
10551
10913
  cost: _cost,
10552
10914
  deeplineCreditsPerPricingUnit: _deeplineCreditsPerPricingUnit,
@@ -10566,12 +10928,48 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
10566
10928
  toolId,
10567
10929
  provider: tool.provider,
10568
10930
  displayName: tool.displayName,
10931
+ usageGuidance: usageGuidanceWithAccessDefaults(recordField(tool, "usageGuidance", "usage_guidance")),
10569
10932
  runtimeOutputHelp: {
10570
- contract: "tools describe shows the declared schema and semantic getters; it is not an observed provider response.",
10933
+ contract: "tools describe shows declared schema and Deepline getters; it is not an observed provider response.",
10934
+ getterScope: "extractedValues/extractedLists .get() only works for declared Deepline getters listed in usageGuidance.toolExecutionResult.",
10935
+ rawToolResponse: "Use toolExecutionResult.toolResponse.raw for provider/tool-specific fields, fields in outputSchema that are not declared getters, and debugging.",
10936
+ invalidGetterHint: "If TypeScript says an extractedValues/extractedLists property does not exist, that field is not a declared Deepline getter.",
10571
10937
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
10572
10938
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
10573
10939
  forPlayGetterBugs: "Run the play, then inspect the emitted table commands from runs get. Use deepline db query against the run tables before editing getters.",
10574
10940
  executeOutputFields: "tools execute JSON may include output_preview for this direct probe only; play debugging uses run tables."
10941
+ },
10942
+ starterScript: {
10943
+ path: starterScript.path,
10944
+ sourceCode: starterScript.sourceCode,
10945
+ projectDir: starterScript.projectDir,
10946
+ copyToProject: {
10947
+ macosLinux: starterScript.macCopyCommand,
10948
+ windowsPowerShell: starterScript.windowsCopyCommand
10949
+ }
10950
+ }
10951
+ };
10952
+ }
10953
+ function usageGuidanceWithAccessDefaults(usageGuidance) {
10954
+ if (Object.keys(usageGuidance).length === 0) return usageGuidance;
10955
+ const existingAccess = recordField(usageGuidance, "access");
10956
+ return {
10957
+ ...usageGuidance,
10958
+ access: {
10959
+ extractedLists: {
10960
+ expression: "toolExecutionResult.extractedLists.<name>.get()",
10961
+ meaning: "Declared Deepline list extractors only."
10962
+ },
10963
+ extractedValues: {
10964
+ expression: "toolExecutionResult.extractedValues.<name>.get()",
10965
+ meaning: "Declared Deepline scalar extractors only."
10966
+ },
10967
+ rawToolResponse: {
10968
+ expression: "toolExecutionResult.toolResponse.raw",
10969
+ meaning: "Raw tool response for provider/tool-specific fields and debugging."
10970
+ },
10971
+ 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.",
10972
+ ...existingAccess
10575
10973
  }
10576
10974
  };
10577
10975
  }
@@ -10788,6 +11186,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
10788
11186
  writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
10789
11187
  return {
10790
11188
  path: scriptPath,
11189
+ sourceCode: script,
10791
11190
  projectDir,
10792
11191
  macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
10793
11192
  windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`