deepline 0.1.54 → 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.54",
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.54",
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
  );
@@ -4009,7 +4027,14 @@ import { createHash } from "crypto";
4009
4027
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
4010
4028
  import { mkdir as mkdir3, readFile, realpath, stat, writeFile as writeFile3 } from "fs/promises";
4011
4029
  import { tmpdir } from "os";
4012
- import { basename, dirname as dirname5, extname, isAbsolute, join as join3, resolve as resolve6 } from "path";
4030
+ import {
4031
+ basename,
4032
+ dirname as dirname5,
4033
+ extname,
4034
+ isAbsolute,
4035
+ join as join3,
4036
+ resolve as resolve6
4037
+ } from "path";
4013
4038
  import { builtinModules } from "module";
4014
4039
  import { build } from "esbuild";
4015
4040
 
@@ -4073,11 +4098,23 @@ var PLAY_ARTIFACT_CACHE_DIR = join3(
4073
4098
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`
4074
4099
  );
4075
4100
  var PLAY_PROXY_NAMESPACE = "deepline-play-runtime-ref";
4076
- var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
4101
+ var SOURCE_EXTENSIONS = [
4102
+ ".ts",
4103
+ ".tsx",
4104
+ ".mts",
4105
+ ".cts",
4106
+ ".js",
4107
+ ".jsx",
4108
+ ".mjs",
4109
+ ".cjs",
4110
+ ".json"
4111
+ ];
4077
4112
  var WORKERS_PLAY_ENTRY_VIRTUAL = "deepline-play-entry";
4078
4113
  var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
4079
4114
  var NODE_BUILTIN_SET = new Set(
4080
- builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
4115
+ builtinModules.flatMap(
4116
+ (name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`]
4117
+ )
4081
4118
  );
4082
4119
  function assertValidExportName(exportName) {
4083
4120
  if (exportName === "default") return;
@@ -4157,15 +4194,27 @@ function findSourceImportReferences(sourceCode) {
4157
4194
  };
4158
4195
  const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
4159
4196
  for (const match of source.matchAll(staticImportPattern)) {
4160
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
4197
+ addReference(
4198
+ match[2],
4199
+ match.index + match[0].lastIndexOf(match[1]),
4200
+ "static"
4201
+ );
4161
4202
  }
4162
4203
  const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
4163
4204
  for (const match of source.matchAll(dynamicImportPattern)) {
4164
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
4205
+ addReference(
4206
+ match[2],
4207
+ match.index + match[0].lastIndexOf(match[1]),
4208
+ "dynamic-import"
4209
+ );
4165
4210
  }
4166
4211
  const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
4167
4212
  for (const match of source.matchAll(requirePattern)) {
4168
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
4213
+ addReference(
4214
+ match[2],
4215
+ match.index + match[0].lastIndexOf(match[1]),
4216
+ "require"
4217
+ );
4169
4218
  }
4170
4219
  const literalDynamicImportIndexes = new Set(
4171
4220
  [...source.matchAll(dynamicImportPattern)].map((match) => match.index)
@@ -4198,7 +4247,9 @@ function unquoteStringLiteral(literal) {
4198
4247
  return null;
4199
4248
  }
4200
4249
  try {
4201
- return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
4250
+ return JSON.parse(
4251
+ quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`
4252
+ );
4202
4253
  } catch {
4203
4254
  return trimmed.slice(1, -1);
4204
4255
  }
@@ -4250,7 +4301,9 @@ function extractDefinedPlayName(sourceCode) {
4250
4301
  const closeBrace = findMatchingBrace(source, argIndex);
4251
4302
  if (closeBrace < 0) continue;
4252
4303
  const objectSource = source.slice(argIndex + 1, closeBrace);
4253
- const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
4304
+ const idMatch = objectSource.match(
4305
+ /(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/
4306
+ );
4254
4307
  if (idMatch?.[2]?.trim()) {
4255
4308
  return idMatch[2].trim();
4256
4309
  }
@@ -4359,7 +4412,12 @@ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
4359
4412
  };
4360
4413
  }
4361
4414
  function workersNodeBuiltinStubPlugin() {
4362
- const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
4415
+ const UNSUPPORTED = /* @__PURE__ */ new Set([
4416
+ "node:fs",
4417
+ "node:fs/promises",
4418
+ "node:os",
4419
+ "node:child_process"
4420
+ ]);
4363
4421
  return {
4364
4422
  name: "deepline-workers-node-builtin-stub",
4365
4423
  setup(buildContext) {
@@ -4408,16 +4466,13 @@ function zodNonEnglishLocaleStubPlugin() {
4408
4466
  }
4409
4467
  return { path: args.path, namespace: NAMESPACE };
4410
4468
  });
4411
- buildContext.onLoad(
4412
- { filter: /.*/, namespace: NAMESPACE },
4413
- () => ({
4414
- // zod locales export a default object literal. Empty object is
4415
- // structurally compatible — accessing any locale key returns
4416
- // undefined and zod falls back to its built-in English.
4417
- contents: "export default {};",
4418
- loader: "js"
4419
- })
4420
- );
4469
+ buildContext.onLoad({ filter: /.*/, namespace: NAMESPACE }, () => ({
4470
+ // zod locales export a default object literal. Empty object is
4471
+ // structurally compatible — accessing any locale key returns
4472
+ // undefined and zod falls back to its built-in English.
4473
+ contents: "export default {};",
4474
+ loader: "js"
4475
+ }));
4421
4476
  }
4422
4477
  };
4423
4478
  }
@@ -4488,7 +4543,10 @@ function importedPlayProxyPlugin(importedPlayDependencies) {
4488
4543
  return null;
4489
4544
  }
4490
4545
  const dependenciesByPath = new Map(
4491
- importedPlayDependencies.map((dependency) => [dependency.filePath, dependency])
4546
+ importedPlayDependencies.map((dependency) => [
4547
+ dependency.filePath,
4548
+ dependency
4549
+ ])
4492
4550
  );
4493
4551
  return {
4494
4552
  name: "deepline-imported-play-proxy",
@@ -4508,17 +4566,20 @@ function importedPlayProxyPlugin(importedPlayDependencies) {
4508
4566
  pluginData: dependency
4509
4567
  };
4510
4568
  });
4511
- buildContext.onLoad({ filter: /.*/, namespace: PLAY_PROXY_NAMESPACE }, async (args) => {
4512
- const dependency = args.pluginData ?? dependenciesByPath.get(args.path);
4513
- if (!dependency) {
4514
- return null;
4569
+ buildContext.onLoad(
4570
+ { filter: /.*/, namespace: PLAY_PROXY_NAMESPACE },
4571
+ async (args) => {
4572
+ const dependency = args.pluginData ?? dependenciesByPath.get(args.path);
4573
+ if (!dependency) {
4574
+ return null;
4575
+ }
4576
+ return {
4577
+ contents: buildImportedPlayProxyModule(dependency.playName),
4578
+ loader: "ts",
4579
+ resolveDir: dirname5(args.path)
4580
+ };
4515
4581
  }
4516
- return {
4517
- contents: buildImportedPlayProxyModule(dependency.playName),
4518
- loader: "ts",
4519
- resolveDir: dirname5(args.path)
4520
- };
4521
- });
4582
+ );
4522
4583
  }
4523
4584
  };
4524
4585
  }
@@ -4538,18 +4599,26 @@ async function resolveLocalImport(fromFile, specifier) {
4538
4599
  const candidates = [base];
4539
4600
  const explicitExtension = extname(base).toLowerCase();
4540
4601
  if (!explicitExtension) {
4541
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`));
4542
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => join3(base, `index${extension}`)));
4602
+ candidates.push(
4603
+ ...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`)
4604
+ );
4605
+ candidates.push(
4606
+ ...SOURCE_EXTENSIONS.map((extension) => join3(base, `index${extension}`))
4607
+ );
4543
4608
  } else if ([".js", ".jsx", ".mjs", ".cjs"].includes(explicitExtension)) {
4544
4609
  const stem = base.slice(0, -explicitExtension.length);
4545
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${stem}${extension}`));
4610
+ candidates.push(
4611
+ ...SOURCE_EXTENSIONS.map((extension) => `${stem}${extension}`)
4612
+ );
4546
4613
  }
4547
4614
  for (const candidate of candidates) {
4548
4615
  if (await fileExists(candidate)) {
4549
4616
  return normalizeLocalPath(candidate);
4550
4617
  }
4551
4618
  }
4552
- throw new Error(`Could not resolve local import "${specifier}" from ${fromFile}`);
4619
+ throw new Error(
4620
+ `Could not resolve local import "${specifier}" from ${fromFile}`
4621
+ );
4553
4622
  }
4554
4623
  function resolvePackageImport(specifier, fromFile, adapter) {
4555
4624
  const packageName = getPackageName(specifier);
@@ -4597,7 +4666,9 @@ async function analyzeSourceGraph(entryFile, adapter) {
4597
4666
  );
4598
4667
  }
4599
4668
  if (NODE_BUILTIN_SET.has(specifier)) {
4600
- nodeBuiltins.add(specifier.startsWith("node:") ? specifier : `node:${specifier}`);
4669
+ nodeBuiltins.add(
4670
+ specifier.startsWith("node:") ? specifier : `node:${specifier}`
4671
+ );
4601
4672
  return;
4602
4673
  }
4603
4674
  if (isLocalSpecifier(specifier)) {
@@ -4632,7 +4703,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
4632
4703
  `${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
4633
4704
  );
4634
4705
  }
4635
- const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
4706
+ const packageImport = resolvePackageImport(
4707
+ specifier,
4708
+ absolutePath,
4709
+ adapter
4710
+ );
4636
4711
  packages.set(packageImport.name, packageImport.version);
4637
4712
  };
4638
4713
  try {
@@ -4682,16 +4757,23 @@ async function analyzeSourceGraph(entryFile, adapter) {
4682
4757
  packages: [...packages.entries()].map(([name, version]) => ({ name, version })).sort((left, right) => left.name.localeCompare(right.name))
4683
4758
  },
4684
4759
  playName,
4685
- importedPlayDependencies: [...importedPlayDependencies.values()].sort((left, right) => left.filePath.localeCompare(right.filePath))
4760
+ importedPlayDependencies: [...importedPlayDependencies.values()].sort(
4761
+ (left, right) => left.filePath.localeCompare(right.filePath)
4762
+ )
4686
4763
  };
4687
4764
  }
4688
4765
  async function computeWorkersHarnessFingerprintWithAdapter(adapter) {
4689
4766
  const { readdir } = await import("fs/promises");
4690
- const entries = await readdir(adapter.workersHarnessFilesDir, { withFileTypes: true });
4767
+ const entries = await readdir(adapter.workersHarnessFilesDir, {
4768
+ withFileTypes: true
4769
+ });
4691
4770
  const tsFiles = entries.filter((e) => e.isFile() && /\.[cm]?ts$/.test(e.name)).map((e) => e.name).sort();
4692
4771
  const parts = [];
4693
4772
  for (const name of tsFiles) {
4694
- const contents = await readFile(join3(adapter.workersHarnessFilesDir, name), "utf-8");
4773
+ const contents = await readFile(
4774
+ join3(adapter.workersHarnessFilesDir, name),
4775
+ "utf-8"
4776
+ );
4695
4777
  parts.push({ name, hash: sha256(contents) });
4696
4778
  }
4697
4779
  return sha256(JSON.stringify(parts));
@@ -4867,9 +4949,47 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
4867
4949
  outputExtension: "mjs"
4868
4950
  };
4869
4951
  }
4952
+ var PLAY_ARTIFACT_TARGET_ADAPTERS = {
4953
+ [PLAY_ARTIFACT_KINDS.cjsNode20]: {
4954
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
4955
+ codeFormat: "cjs_module",
4956
+ includeWorkersHarnessInGraphHash: false,
4957
+ runEsbuild: ({
4958
+ entryFile,
4959
+ importedPlayDependencies,
4960
+ adapter,
4961
+ exportName
4962
+ }) => runEsbuildForCjsNode(
4963
+ entryFile,
4964
+ importedPlayDependencies,
4965
+ adapter,
4966
+ exportName
4967
+ )
4968
+ },
4969
+ [PLAY_ARTIFACT_KINDS.esmWorkers]: {
4970
+ artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
4971
+ codeFormat: "esm_module",
4972
+ includeWorkersHarnessInGraphHash: true,
4973
+ runEsbuild: ({
4974
+ entryFile,
4975
+ importedPlayDependencies,
4976
+ adapter,
4977
+ exportName
4978
+ }) => runEsbuildForEsmWorkers(
4979
+ entryFile,
4980
+ importedPlayDependencies,
4981
+ adapter,
4982
+ exportName
4983
+ )
4984
+ }
4985
+ };
4986
+ function resolvePlayArtifactTargetAdapter(artifactKind) {
4987
+ return PLAY_ARTIFACT_TARGET_ADAPTERS[artifactKind];
4988
+ }
4870
4989
  async function bundlePlayFile(filePath, options) {
4871
4990
  const adapter = options.adapter;
4872
4991
  const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
4992
+ const targetAdapter = resolvePlayArtifactTargetAdapter(target);
4873
4993
  const exportName = options.exportName?.trim() || "default";
4874
4994
  assertValidExportName(exportName);
4875
4995
  const absolutePath = await normalizeLocalPath(filePath);
@@ -4880,7 +5000,7 @@ async function bundlePlayFile(filePath, options) {
4880
5000
  `${analysis.graphHash}
4881
5001
  entry-export:${exportName}`
4882
5002
  );
4883
- if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
5003
+ if (targetAdapter.includeWorkersHarnessInGraphHash) {
4884
5004
  const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
4885
5005
  analysis.graphHash = sha256(
4886
5006
  `${analysis.graphHash}
@@ -4893,7 +5013,9 @@ workers-harness:${harnessFingerprint}`
4893
5013
  sourcePath: absolutePath,
4894
5014
  importedFilePaths: [
4895
5015
  ...analysis.importPolicy.localFiles,
4896
- ...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
5016
+ ...analysis.importedPlayDependencies.map(
5017
+ (dependency) => dependency.filePath
5018
+ )
4897
5019
  ]
4898
5020
  }) ?? []
4899
5021
  ];
@@ -4904,7 +5026,11 @@ workers-harness:${harnessFingerprint}`
4904
5026
  errors: typecheckErrors
4905
5027
  };
4906
5028
  }
4907
- const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
5029
+ const cachedArtifact = await readArtifactCache(
5030
+ analysis.graphHash,
5031
+ target,
5032
+ adapter
5033
+ );
4908
5034
  const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
4909
5035
  if (cachedArtifact) {
4910
5036
  const cachedArtifactSizeError = getBundleSizeError(
@@ -4931,7 +5057,12 @@ workers-harness:${harnessFingerprint}`
4931
5057
  importedPlayDependencies: analysis.importedPlayDependencies
4932
5058
  };
4933
5059
  }
4934
- const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
5060
+ const buildOutcome = await targetAdapter.runEsbuild({
5061
+ entryFile: absolutePath,
5062
+ importedPlayDependencies: analysis.importedPlayDependencies,
5063
+ adapter,
5064
+ exportName
5065
+ });
4935
5066
  if (Array.isArray(buildOutcome)) {
4936
5067
  return {
4937
5068
  success: false,
@@ -4958,9 +5089,8 @@ workers-harness:${harnessFingerprint}`
4958
5089
  errors: [bundleSizeError]
4959
5090
  };
4960
5091
  }
4961
- const codeFormat = target === PLAY_ARTIFACT_KINDS.esmWorkers ? "esm_module" : "cjs_module";
4962
5092
  const artifact = {
4963
- codeFormat,
5093
+ codeFormat: targetAdapter.codeFormat,
4964
5094
  artifactKind: target,
4965
5095
  entryFile: absolutePath,
4966
5096
  virtualFilename,
@@ -5004,6 +5134,13 @@ workers-harness:${harnessFingerprint}`
5004
5134
  }
5005
5135
  }
5006
5136
 
5137
+ // ../shared_libs/play-runtime/dedup-backend.ts
5138
+ var PLAY_DEDUP_BACKENDS = {
5139
+ inMemory: "in_memory",
5140
+ neonTable: "neon_table",
5141
+ durableObject: "durable_object"
5142
+ };
5143
+
5007
5144
  // ../shared_libs/play-runtime/scheduler-backend.ts
5008
5145
  var PLAY_SCHEDULER_BACKENDS = {
5009
5146
  temporal: "temporal",
@@ -5011,44 +5148,65 @@ var PLAY_SCHEDULER_BACKENDS = {
5011
5148
  inProcess: "in-process"
5012
5149
  };
5013
5150
 
5014
- // ../shared_libs/play-runtime/dedup-backend.ts
5015
- var PLAY_DEDUP_BACKENDS = {
5016
- inMemory: "in_memory",
5017
- neonTable: "neon_table",
5018
- durableObject: "durable_object"
5151
+ // ../shared_libs/play-runtime/providers.ts
5152
+ var PLAY_RUNTIME_PROVIDER_IDS = {
5153
+ workersEdge: "workers_edge",
5154
+ local: "local"
5019
5155
  };
5020
-
5021
- // ../shared_libs/play-runtime/profiles.ts
5022
- var PLAY_EXECUTION_PROFILES = {
5156
+ var PLAY_RUNTIME_PROVIDERS = {
5023
5157
  workers_edge: {
5024
- id: "workers_edge",
5158
+ id: PLAY_RUNTIME_PROVIDER_IDS.workersEdge,
5025
5159
  scheduler: PLAY_SCHEDULER_BACKENDS.cfWorkflows,
5026
5160
  runner: PLAY_RUNTIME_BACKENDS.cloudflareWorkers,
5027
5161
  dedup: PLAY_DEDUP_BACKENDS.durableObject,
5162
+ artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
5028
5163
  label: "Cloudflare Dynamic Workflows + Dynamic Workers + DO dedup"
5029
5164
  },
5030
5165
  local: {
5031
- id: "local",
5166
+ id: PLAY_RUNTIME_PROVIDER_IDS.local,
5032
5167
  scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
5033
5168
  runner: PLAY_RUNTIME_BACKENDS.localProcess,
5034
5169
  dedup: PLAY_DEDUP_BACKENDS.inMemory,
5170
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
5035
5171
  label: "Local Temporal scheduler + local subprocess runner (tests)"
5036
5172
  }
5037
5173
  };
5038
- function defaultExecutionProfile() {
5039
- return PLAY_EXECUTION_PROFILES.workers_edge;
5174
+ function defaultPlayRuntimeProvider() {
5175
+ return PLAY_RUNTIME_PROVIDERS.workers_edge;
5040
5176
  }
5041
- function resolveExecutionProfile(override) {
5177
+ function resolvePlayRuntimeProvider(override) {
5042
5178
  if (override?.trim()) {
5043
5179
  const id = override.trim();
5044
- if (id in PLAY_EXECUTION_PROFILES) {
5045
- return PLAY_EXECUTION_PROFILES[id];
5180
+ if (id in PLAY_RUNTIME_PROVIDERS) {
5181
+ return PLAY_RUNTIME_PROVIDERS[id];
5046
5182
  }
5047
5183
  throw new Error(
5048
- `Unknown execution profile "${id}". Expected one of: ${Object.keys(PLAY_EXECUTION_PROFILES).join(", ")}.`
5184
+ `Unknown play runtime provider "${id}". Expected one of: ${Object.keys(
5185
+ PLAY_RUNTIME_PROVIDERS
5186
+ ).join(", ")}.`
5049
5187
  );
5050
5188
  }
5051
- return defaultExecutionProfile();
5189
+ return defaultPlayRuntimeProvider();
5190
+ }
5191
+
5192
+ // ../shared_libs/play-runtime/profiles.ts
5193
+ var PLAY_EXECUTION_PROFILE_IDS = {
5194
+ ...PLAY_RUNTIME_PROVIDER_IDS
5195
+ };
5196
+ var PLAY_EXECUTION_PROFILES = PLAY_RUNTIME_PROVIDERS;
5197
+ function resolveExecutionProfile(override) {
5198
+ try {
5199
+ return resolvePlayRuntimeProvider(override);
5200
+ } catch (error) {
5201
+ if (override?.trim()) {
5202
+ throw new Error(
5203
+ `Unknown execution profile "${override.trim()}". Expected one of: ${Object.keys(
5204
+ PLAY_EXECUTION_PROFILES
5205
+ ).join(", ")}.`
5206
+ );
5207
+ }
5208
+ throw error;
5209
+ }
5052
5210
  }
5053
5211
 
5054
5212
  // src/plays/local-file-discovery.ts
@@ -5366,8 +5524,19 @@ var SDK_PACKAGE_JSON = resolve8(SDK_PACKAGE_ROOT, "package.json");
5366
5524
  var SDK_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "index.ts");
5367
5525
  var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : resolve8(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
5368
5526
  var SDK_WORKERS_ENTRY_FILE = resolve8(SDK_SOURCE_ROOT, "worker-play-entry.ts");
5369
- var WORKERS_HARNESS_ENTRY_FILE = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
5370
- var WORKERS_HARNESS_FILES_DIR = resolve8(PROJECT_ROOT, "apps", "play-runner-workers", "src");
5527
+ var WORKERS_HARNESS_ENTRY_FILE = resolve8(
5528
+ PROJECT_ROOT,
5529
+ "apps",
5530
+ "play-runner-workers",
5531
+ "src",
5532
+ "entry.ts"
5533
+ );
5534
+ var WORKERS_HARNESS_FILES_DIR = resolve8(
5535
+ PROJECT_ROOT,
5536
+ "apps",
5537
+ "play-runner-workers",
5538
+ "src"
5539
+ );
5371
5540
  var hasWarnedAboutNonDevelopmentBundling = false;
5372
5541
  function warnAboutNonDevelopmentBundling(filePath) {
5373
5542
  if (hasWarnedAboutNonDevelopmentBundling) {
@@ -5386,13 +5555,16 @@ function warnAboutNonDevelopmentBundling(filePath) {
5386
5555
  );
5387
5556
  }
5388
5557
  function defaultPlayBundleTarget() {
5389
- return PLAY_BACKEND_DESCRIPTORS[resolveExecutionProfile(null).runner].artifactKind;
5558
+ return resolveExecutionProfile(null).artifactKind;
5390
5559
  }
5391
5560
  function createSdkPlayBundlingAdapter() {
5392
5561
  return {
5393
5562
  projectRoot: PROJECT_ROOT,
5394
5563
  nodeModulesDir: resolve8(PROJECT_ROOT, "node_modules"),
5395
- cacheDir: join5(tmpdir2(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
5564
+ cacheDir: join5(
5565
+ tmpdir2(),
5566
+ `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`
5567
+ ),
5396
5568
  sdkSourceRoot: SDK_SOURCE_ROOT,
5397
5569
  sdkPackageJson: SDK_PACKAGE_JSON,
5398
5570
  sdkEntryFile: SDK_ENTRY_FILE,
@@ -5557,6 +5729,204 @@ function createCliProgress(enabled) {
5557
5729
  return progress;
5558
5730
  }
5559
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
+
5560
5930
  // src/cli/trace.ts
5561
5931
  var cliTraceStartedAt = Date.now();
5562
5932
  function isTruthyEnv(value) {
@@ -5605,6 +5975,31 @@ async function traceCliSpan(phase, fields, run) {
5605
5975
  }
5606
5976
  }
5607
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
+
5608
6003
  // src/cli/commands/play.ts
5609
6004
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
5610
6005
  "--json",
@@ -5684,9 +6079,23 @@ function formatPlayListReference(play) {
5684
6079
  return play.reference || play.name;
5685
6080
  }
5686
6081
  function defaultMaterializedPlayPath(reference) {
6082
+ return resolve9(defaultStarterPlayPath(reference));
6083
+ }
6084
+ function defaultStarterPlayPath(reference) {
5687
6085
  const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
5688
6086
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5689
- 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
+ };
5690
6099
  }
5691
6100
  function materializeRemotePlaySource(input) {
5692
6101
  if (isFileTarget(input.target)) {
@@ -6132,6 +6541,8 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
6132
6541
  "cancelled"
6133
6542
  ]);
6134
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";
6135
6546
  function getEventPayload(event) {
6136
6547
  return event.payload && typeof event.payload === "object" ? event.payload : {};
6137
6548
  }
@@ -6185,6 +6596,86 @@ function getLogLinesFromLiveEvent(event) {
6185
6596
  const lines = getEventPayload(event).lines;
6186
6597
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
6187
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
+ }
6188
6679
  function describeLiveEventPhase(event) {
6189
6680
  const payload = getEventPayload(event);
6190
6681
  if (event.type === "play.run.status") {
@@ -6337,6 +6828,14 @@ async function waitForPlayCompletionByStream(input) {
6337
6828
  lastPhase = phase;
6338
6829
  input.progress.phase(phase);
6339
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
+ });
6340
6839
  printPlayLogLines({
6341
6840
  lines: getLogLinesFromLiveEvent(event),
6342
6841
  status: null,
@@ -6485,6 +6984,14 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6485
6984
  state,
6486
6985
  progress: input.progress
6487
6986
  });
6987
+ emitLiveDebugTableHints({
6988
+ event,
6989
+ playName: input.playName,
6990
+ runId: workflowId,
6991
+ jsonOutput: input.jsonOutput,
6992
+ state,
6993
+ progress: input.progress
6994
+ });
6488
6995
  if (!input.jsonOutput) {
6489
6996
  printPlayProgressLines({
6490
6997
  lines: getProgressLinesFromLiveEvent(event),
@@ -6540,6 +7047,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6540
7047
  });
6541
7048
  return waitForPlayCompletionByStream({
6542
7049
  client: input.client,
7050
+ playName: input.playName,
6543
7051
  workflowId: lastKnownWorkflowId,
6544
7052
  dashboardUrl,
6545
7053
  jsonOutput: input.jsonOutput,
@@ -6575,6 +7083,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6575
7083
  });
6576
7084
  return waitForPlayCompletionByStream({
6577
7085
  client: input.client,
7086
+ playName: input.playName,
6578
7087
  workflowId: lastKnownWorkflowId,
6579
7088
  dashboardUrl,
6580
7089
  jsonOutput: input.jsonOutput,
@@ -7958,6 +8467,10 @@ async function handlePlayCheck(args) {
7958
8467
  });
7959
8468
  const enrichedResult = {
7960
8469
  ...result,
8470
+ errors: result.valid ? result.errors : addPlayCheckRepairHints({
8471
+ errors: result.errors,
8472
+ sourceCode: graph.root.sourceCode
8473
+ }),
7961
8474
  toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
7962
8475
  };
7963
8476
  if (options.jsonOutput) {
@@ -8743,25 +9256,13 @@ function parsePlaySearchOptions(args) {
8743
9256
  const query = args[0]?.trim();
8744
9257
  if (!query) {
8745
9258
  throw new Error(
8746
- "Usage: deepline plays search <query> [--origin prebuilt|owned] [--compact] [--json]"
9259
+ "Usage: deepline plays search <query> [--compact] [--json]"
8747
9260
  );
8748
9261
  }
8749
- let origin;
8750
- for (let index = 1; index < args.length; index += 1) {
8751
- const arg = args[index];
8752
- if (arg === "--origin" && args[index + 1]) {
8753
- const rawOrigin = args[++index].trim().toLowerCase();
8754
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8755
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8756
- }
8757
- origin = rawOrigin;
8758
- }
8759
- }
8760
9262
  return {
8761
9263
  query,
8762
9264
  jsonOutput: argsWantJson(args),
8763
- compact: args.includes("--compact"),
8764
- origin
9265
+ compact: args.includes("--compact")
8765
9266
  };
8766
9267
  }
8767
9268
  function printPlayDescription(play) {
@@ -8810,10 +9311,10 @@ function printPlayDescription(play) {
8810
9311
  }
8811
9312
  }
8812
9313
  console.log(` Run: ${play.runCommand}`);
8813
- if (play.origin === "prebuilt" && !play.canEdit) {
8814
- console.log(
8815
- ` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
8816
- );
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}`);
8817
9318
  }
8818
9319
  }
8819
9320
  function compactPlaySchema(schema) {
@@ -8845,6 +9346,7 @@ function summarizePlayListItemForCli(play, options) {
8845
9346
  const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
8846
9347
  const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
8847
9348
  const runCommand2 = playRunCommand(play, { csvInput });
9349
+ const cloneEditStarter = buildCloneEditStarter(play);
8848
9350
  return {
8849
9351
  name: play.name,
8850
9352
  ...play.reference ? { reference: play.reference } : {},
@@ -8860,6 +9362,7 @@ function summarizePlayListItemForCli(play, options) {
8860
9362
  ...rowOutputSchema ? { rowOutputSchema } : {},
8861
9363
  runCommand: runCommand2,
8862
9364
  examples: [runCommand2],
9365
+ ...cloneEditStarter ? { cloneEditStarter } : {},
8863
9366
  currentPublishedVersion: play.currentPublishedVersion ?? null,
8864
9367
  isDraftDirty: play.isDraftDirty
8865
9368
  };
@@ -8875,7 +9378,6 @@ async function handlePlaySearch(args) {
8875
9378
  const client = new DeeplineClient();
8876
9379
  const plays = await client.searchPlays({
8877
9380
  query: options.query,
8878
- ...options.origin ? { origin: options.origin } : {},
8879
9381
  compact: options.compact
8880
9382
  });
8881
9383
  if (options.jsonOutput) {
@@ -8921,20 +9423,12 @@ function matchesPlayGrepQuery(value, query, mode) {
8921
9423
  async function handlePlayGrep(args) {
8922
9424
  const query = args[0]?.trim();
8923
9425
  if (!query) {
8924
- 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]");
8925
9427
  return 1;
8926
9428
  }
8927
- let origin;
8928
9429
  let mode = "all";
8929
9430
  for (let index = 1; index < args.length; index += 1) {
8930
9431
  const arg = args[index];
8931
- if (arg === "--origin" && args[index + 1]) {
8932
- const rawOrigin = args[++index].trim().toLowerCase();
8933
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8934
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8935
- }
8936
- origin = rawOrigin;
8937
- }
8938
9432
  if (arg === "--mode" && args[index + 1]) {
8939
9433
  const rawMode = args[++index].trim().toLowerCase();
8940
9434
  mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
@@ -8944,13 +9438,8 @@ async function handlePlayGrep(args) {
8944
9438
  const client = new DeeplineClient();
8945
9439
  const plays = (await client.listPlays({
8946
9440
  grep: query,
8947
- grepMode: mode,
8948
- ...origin ? { origin } : {}
8949
- })).filter((play) => {
8950
- if (!origin) return true;
8951
- const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
8952
- return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
8953
- }).filter(
9441
+ grepMode: mode
9442
+ })).filter(
8954
9443
  (play) => matchesPlayGrepQuery(
8955
9444
  {
8956
9445
  name: play.name,
@@ -8971,8 +9460,7 @@ async function handlePlayGrep(args) {
8971
9460
  plays,
8972
9461
  count: plays.length,
8973
9462
  query,
8974
- grep: { mode, terms: parsePlayGrepTerms(query, mode) },
8975
- filters: { origin: origin ?? null }
9463
+ grep: { mode, terms: parsePlayGrepTerms(query, mode) }
8976
9464
  })}
8977
9465
  `);
8978
9466
  return 0;
@@ -9308,7 +9796,7 @@ Notes:
9308
9796
  Examples:
9309
9797
  deepline plays list
9310
9798
  deepline plays list --origin prebuilt --json
9311
- deepline plays search email --origin prebuilt --json
9799
+ deepline plays search email --json
9312
9800
  `
9313
9801
  ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9314
9802
  process.exitCode = await handlePlayList([
@@ -9320,20 +9808,18 @@ Examples:
9320
9808
  "after",
9321
9809
  `
9322
9810
  Notes:
9323
- Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
9324
- 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.
9325
9812
  The grep alias is the same ranked retrieval surface with a more literal name
9326
9813
  for agents that are filtering the play registry.
9327
9814
 
9328
9815
  Examples:
9329
9816
  deepline plays search email
9330
- deepline plays grep "linkedin to email" --origin prebuilt --compact --json
9817
+ deepline plays grep "linkedin to email" --compact --json
9331
9818
  deepline plays describe person-linkedin-to-email --json
9332
9819
  `
9333
- ).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) => {
9334
9821
  process.exitCode = await handlePlaySearch([
9335
9822
  query,
9336
- ...options.origin ? ["--origin", options.origin] : [],
9337
9823
  ...options.compact ? ["--compact"] : [],
9338
9824
  ...options.json ? ["--json"] : []
9339
9825
  ]);
@@ -9349,14 +9835,13 @@ Notes:
9349
9835
  --mode all for AND.
9350
9836
 
9351
9837
  Examples:
9352
- deepline plays grep email --origin prebuilt --json
9353
- 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
9354
9840
  deepline plays describe prebuilt/company-to-contact --json
9355
9841
  `
9356
- ).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) => {
9357
9843
  process.exitCode = await handlePlayGrep([
9358
9844
  query,
9359
- ...options.origin ? ["--origin", options.origin] : [],
9360
9845
  ...options.compact ? ["--compact"] : [],
9361
9846
  ...options.mode ? ["--mode", options.mode] : [],
9362
9847
  ...options.json ? ["--json"] : []
@@ -9372,6 +9857,7 @@ Notes:
9372
9857
  Examples:
9373
9858
  deepline plays describe person-linkedin-to-email
9374
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
9375
9861
  `
9376
9862
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
9377
9863
  process.exitCode = await handlePlayDescribe([
@@ -10196,6 +10682,11 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10196
10682
  const cost = recordField(tool, "cost");
10197
10683
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
10198
10684
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
10685
+ const starterScript = seedToolListScript({
10686
+ toolId,
10687
+ payload: samplePayloadForInputFields(inputFields),
10688
+ rows: []
10689
+ });
10199
10690
  return {
10200
10691
  schemaVersion: 1,
10201
10692
  toolId,
@@ -10220,7 +10711,16 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10220
10711
  extractedLists,
10221
10712
  extractedValues
10222
10713
  },
10223
- 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
+ }
10224
10724
  };
10225
10725
  }
10226
10726
  function extractionContractEntries(entries) {
@@ -10262,6 +10762,13 @@ function printCompactToolContract(tool, requestedToolId) {
10262
10762
  }
10263
10763
  console.log("");
10264
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
+ }
10265
10772
  if (listGetters.length || valueGetters.length) {
10266
10773
  console.log("");
10267
10774
  console.log("Getters:");
@@ -10313,14 +10820,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
10313
10820
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
10314
10821
  const toolId = String(contract.toolId);
10315
10822
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10316
- const sampleInput = Object.fromEntries(
10317
- inputFields.slice(0, 4).flatMap((field) => {
10318
- if (!isRecord4(field)) return [];
10319
- const name = stringField(field, "name");
10320
- if (!name) return [];
10321
- return [[name, sampleValueForField(field)]];
10322
- })
10323
- );
10823
+ const sampleInput = samplePayloadForInputFields(inputFields);
10324
10824
  console.log(`Use in a play:`);
10325
10825
  console.log("```ts");
10326
10826
  console.log("const result = await ctx.tools.execute({");
@@ -10381,6 +10881,16 @@ function sampleValueForField(field) {
10381
10881
  if (type === "object") return {};
10382
10882
  return "...";
10383
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
+ }
10384
10894
  function stableStepIdForTool(toolId) {
10385
10895
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
10386
10896
  }
@@ -10393,6 +10903,12 @@ function playResultExpression(entry) {
10393
10903
  }
10394
10904
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
10395
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
+ });
10396
10912
  const {
10397
10913
  cost: _cost,
10398
10914
  deeplineCreditsPerPricingUnit: _deeplineCreditsPerPricingUnit,
@@ -10412,12 +10928,48 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
10412
10928
  toolId,
10413
10929
  provider: tool.provider,
10414
10930
  displayName: tool.displayName,
10931
+ usageGuidance: usageGuidanceWithAccessDefaults(recordField(tool, "usageGuidance", "usage_guidance")),
10415
10932
  runtimeOutputHelp: {
10416
- 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.",
10417
10937
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
10418
10938
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
10419
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.",
10420
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
10421
10973
  }
10422
10974
  };
10423
10975
  }
@@ -10634,6 +11186,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
10634
11186
  writeFileSync8(scriptPath, script, { encoding: "utf-8", mode: 384 });
10635
11187
  return {
10636
11188
  path: scriptPath,
11189
+ sourceCode: script,
10637
11190
  projectDir,
10638
11191
  macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
10639
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}`)}`