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.
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.54",
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.54",
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
  );
@@ -4085,11 +4103,23 @@ var PLAY_ARTIFACT_CACHE_DIR = (0, import_node_path7.join)(
4085
4103
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`
4086
4104
  );
4087
4105
  var PLAY_PROXY_NAMESPACE = "deepline-play-runtime-ref";
4088
- var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
4106
+ var SOURCE_EXTENSIONS = [
4107
+ ".ts",
4108
+ ".tsx",
4109
+ ".mts",
4110
+ ".cts",
4111
+ ".js",
4112
+ ".jsx",
4113
+ ".mjs",
4114
+ ".cjs",
4115
+ ".json"
4116
+ ];
4089
4117
  var WORKERS_PLAY_ENTRY_VIRTUAL = "deepline-play-entry";
4090
4118
  var PLAY_SOURCE_FILE_PATTERN = /\.play\.(?:[cm]?[jt]sx?)$/i;
4091
4119
  var NODE_BUILTIN_SET = new Set(
4092
- import_node_module.builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`])
4120
+ import_node_module.builtinModules.flatMap(
4121
+ (name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`]
4122
+ )
4093
4123
  );
4094
4124
  function assertValidExportName(exportName) {
4095
4125
  if (exportName === "default") return;
@@ -4169,15 +4199,27 @@ function findSourceImportReferences(sourceCode) {
4169
4199
  };
4170
4200
  const staticImportPattern = /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?(['"])([^'"\n]+)\1/g;
4171
4201
  for (const match of source.matchAll(staticImportPattern)) {
4172
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "static");
4202
+ addReference(
4203
+ match[2],
4204
+ match.index + match[0].lastIndexOf(match[1]),
4205
+ "static"
4206
+ );
4173
4207
  }
4174
4208
  const dynamicImportPattern = /\bimport\s*\(\s*(['"])([^'"\n]+)\1/g;
4175
4209
  for (const match of source.matchAll(dynamicImportPattern)) {
4176
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "dynamic-import");
4210
+ addReference(
4211
+ match[2],
4212
+ match.index + match[0].lastIndexOf(match[1]),
4213
+ "dynamic-import"
4214
+ );
4177
4215
  }
4178
4216
  const requirePattern = /\brequire\s*\(\s*(['"])([^'"\n]+)\1/g;
4179
4217
  for (const match of source.matchAll(requirePattern)) {
4180
- addReference(match[2], match.index + match[0].lastIndexOf(match[1]), "require");
4218
+ addReference(
4219
+ match[2],
4220
+ match.index + match[0].lastIndexOf(match[1]),
4221
+ "require"
4222
+ );
4181
4223
  }
4182
4224
  const literalDynamicImportIndexes = new Set(
4183
4225
  [...source.matchAll(dynamicImportPattern)].map((match) => match.index)
@@ -4210,7 +4252,9 @@ function unquoteStringLiteral(literal) {
4210
4252
  return null;
4211
4253
  }
4212
4254
  try {
4213
- return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
4255
+ return JSON.parse(
4256
+ quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`
4257
+ );
4214
4258
  } catch {
4215
4259
  return trimmed.slice(1, -1);
4216
4260
  }
@@ -4262,7 +4306,9 @@ function extractDefinedPlayName(sourceCode) {
4262
4306
  const closeBrace = findMatchingBrace(source, argIndex);
4263
4307
  if (closeBrace < 0) continue;
4264
4308
  const objectSource = source.slice(argIndex + 1, closeBrace);
4265
- const idMatch = objectSource.match(/(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/);
4309
+ const idMatch = objectSource.match(
4310
+ /(?:^|[,{\s])(?:id|['"]id['"])\s*:\s*(['"])([\s\S]*?)\1/
4311
+ );
4266
4312
  if (idMatch?.[2]?.trim()) {
4267
4313
  return idMatch[2].trim();
4268
4314
  }
@@ -4371,7 +4417,12 @@ function workersNamedPlayEntryAliasPlugin(playFilePath, exportName) {
4371
4417
  };
4372
4418
  }
4373
4419
  function workersNodeBuiltinStubPlugin() {
4374
- const UNSUPPORTED = /* @__PURE__ */ new Set(["node:fs", "node:fs/promises", "node:os", "node:child_process"]);
4420
+ const UNSUPPORTED = /* @__PURE__ */ new Set([
4421
+ "node:fs",
4422
+ "node:fs/promises",
4423
+ "node:os",
4424
+ "node:child_process"
4425
+ ]);
4375
4426
  return {
4376
4427
  name: "deepline-workers-node-builtin-stub",
4377
4428
  setup(buildContext) {
@@ -4420,16 +4471,13 @@ function zodNonEnglishLocaleStubPlugin() {
4420
4471
  }
4421
4472
  return { path: args.path, namespace: NAMESPACE };
4422
4473
  });
4423
- buildContext.onLoad(
4424
- { filter: /.*/, namespace: NAMESPACE },
4425
- () => ({
4426
- // zod locales export a default object literal. Empty object is
4427
- // structurally compatible — accessing any locale key returns
4428
- // undefined and zod falls back to its built-in English.
4429
- contents: "export default {};",
4430
- loader: "js"
4431
- })
4432
- );
4474
+ buildContext.onLoad({ filter: /.*/, namespace: NAMESPACE }, () => ({
4475
+ // zod locales export a default object literal. Empty object is
4476
+ // structurally compatible — accessing any locale key returns
4477
+ // undefined and zod falls back to its built-in English.
4478
+ contents: "export default {};",
4479
+ loader: "js"
4480
+ }));
4433
4481
  }
4434
4482
  };
4435
4483
  }
@@ -4500,7 +4548,10 @@ function importedPlayProxyPlugin(importedPlayDependencies) {
4500
4548
  return null;
4501
4549
  }
4502
4550
  const dependenciesByPath = new Map(
4503
- importedPlayDependencies.map((dependency) => [dependency.filePath, dependency])
4551
+ importedPlayDependencies.map((dependency) => [
4552
+ dependency.filePath,
4553
+ dependency
4554
+ ])
4504
4555
  );
4505
4556
  return {
4506
4557
  name: "deepline-imported-play-proxy",
@@ -4520,17 +4571,20 @@ function importedPlayProxyPlugin(importedPlayDependencies) {
4520
4571
  pluginData: dependency
4521
4572
  };
4522
4573
  });
4523
- buildContext.onLoad({ filter: /.*/, namespace: PLAY_PROXY_NAMESPACE }, async (args) => {
4524
- const dependency = args.pluginData ?? dependenciesByPath.get(args.path);
4525
- if (!dependency) {
4526
- return null;
4574
+ buildContext.onLoad(
4575
+ { filter: /.*/, namespace: PLAY_PROXY_NAMESPACE },
4576
+ async (args) => {
4577
+ const dependency = args.pluginData ?? dependenciesByPath.get(args.path);
4578
+ if (!dependency) {
4579
+ return null;
4580
+ }
4581
+ return {
4582
+ contents: buildImportedPlayProxyModule(dependency.playName),
4583
+ loader: "ts",
4584
+ resolveDir: (0, import_node_path7.dirname)(args.path)
4585
+ };
4527
4586
  }
4528
- return {
4529
- contents: buildImportedPlayProxyModule(dependency.playName),
4530
- loader: "ts",
4531
- resolveDir: (0, import_node_path7.dirname)(args.path)
4532
- };
4533
- });
4587
+ );
4534
4588
  }
4535
4589
  };
4536
4590
  }
@@ -4550,18 +4604,26 @@ async function resolveLocalImport(fromFile, specifier) {
4550
4604
  const candidates = [base];
4551
4605
  const explicitExtension = (0, import_node_path7.extname)(base).toLowerCase();
4552
4606
  if (!explicitExtension) {
4553
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`));
4554
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => (0, import_node_path7.join)(base, `index${extension}`)));
4607
+ candidates.push(
4608
+ ...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`)
4609
+ );
4610
+ candidates.push(
4611
+ ...SOURCE_EXTENSIONS.map((extension) => (0, import_node_path7.join)(base, `index${extension}`))
4612
+ );
4555
4613
  } else if ([".js", ".jsx", ".mjs", ".cjs"].includes(explicitExtension)) {
4556
4614
  const stem = base.slice(0, -explicitExtension.length);
4557
- candidates.push(...SOURCE_EXTENSIONS.map((extension) => `${stem}${extension}`));
4615
+ candidates.push(
4616
+ ...SOURCE_EXTENSIONS.map((extension) => `${stem}${extension}`)
4617
+ );
4558
4618
  }
4559
4619
  for (const candidate of candidates) {
4560
4620
  if (await fileExists(candidate)) {
4561
4621
  return normalizeLocalPath(candidate);
4562
4622
  }
4563
4623
  }
4564
- throw new Error(`Could not resolve local import "${specifier}" from ${fromFile}`);
4624
+ throw new Error(
4625
+ `Could not resolve local import "${specifier}" from ${fromFile}`
4626
+ );
4565
4627
  }
4566
4628
  function resolvePackageImport(specifier, fromFile, adapter) {
4567
4629
  const packageName = getPackageName(specifier);
@@ -4609,7 +4671,9 @@ async function analyzeSourceGraph(entryFile, adapter) {
4609
4671
  );
4610
4672
  }
4611
4673
  if (NODE_BUILTIN_SET.has(specifier)) {
4612
- nodeBuiltins.add(specifier.startsWith("node:") ? specifier : `node:${specifier}`);
4674
+ nodeBuiltins.add(
4675
+ specifier.startsWith("node:") ? specifier : `node:${specifier}`
4676
+ );
4613
4677
  return;
4614
4678
  }
4615
4679
  if (isLocalSpecifier(specifier)) {
@@ -4644,7 +4708,11 @@ async function analyzeSourceGraph(entryFile, adapter) {
4644
4708
  `${absolutePath}:${line}:${column} Unsupported import specifier "${specifier}". Allowed imports are relative files, Node builtins, and installed packages.`
4645
4709
  );
4646
4710
  }
4647
- const packageImport = resolvePackageImport(specifier, absolutePath, adapter);
4711
+ const packageImport = resolvePackageImport(
4712
+ specifier,
4713
+ absolutePath,
4714
+ adapter
4715
+ );
4648
4716
  packages.set(packageImport.name, packageImport.version);
4649
4717
  };
4650
4718
  try {
@@ -4694,16 +4762,23 @@ async function analyzeSourceGraph(entryFile, adapter) {
4694
4762
  packages: [...packages.entries()].map(([name, version]) => ({ name, version })).sort((left, right) => left.name.localeCompare(right.name))
4695
4763
  },
4696
4764
  playName,
4697
- importedPlayDependencies: [...importedPlayDependencies.values()].sort((left, right) => left.filePath.localeCompare(right.filePath))
4765
+ importedPlayDependencies: [...importedPlayDependencies.values()].sort(
4766
+ (left, right) => left.filePath.localeCompare(right.filePath)
4767
+ )
4698
4768
  };
4699
4769
  }
4700
4770
  async function computeWorkersHarnessFingerprintWithAdapter(adapter) {
4701
4771
  const { readdir } = await import("fs/promises");
4702
- const entries = await readdir(adapter.workersHarnessFilesDir, { withFileTypes: true });
4772
+ const entries = await readdir(adapter.workersHarnessFilesDir, {
4773
+ withFileTypes: true
4774
+ });
4703
4775
  const tsFiles = entries.filter((e) => e.isFile() && /\.[cm]?ts$/.test(e.name)).map((e) => e.name).sort();
4704
4776
  const parts = [];
4705
4777
  for (const name of tsFiles) {
4706
- const contents = await (0, import_promises3.readFile)((0, import_node_path7.join)(adapter.workersHarnessFilesDir, name), "utf-8");
4778
+ const contents = await (0, import_promises3.readFile)(
4779
+ (0, import_node_path7.join)(adapter.workersHarnessFilesDir, name),
4780
+ "utf-8"
4781
+ );
4707
4782
  parts.push({ name, hash: sha256(contents) });
4708
4783
  }
4709
4784
  return sha256(JSON.stringify(parts));
@@ -4879,9 +4954,47 @@ async function runEsbuildForEsmWorkers(playEntryFile, importedPlayDependencies,
4879
4954
  outputExtension: "mjs"
4880
4955
  };
4881
4956
  }
4957
+ var PLAY_ARTIFACT_TARGET_ADAPTERS = {
4958
+ [PLAY_ARTIFACT_KINDS.cjsNode20]: {
4959
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
4960
+ codeFormat: "cjs_module",
4961
+ includeWorkersHarnessInGraphHash: false,
4962
+ runEsbuild: ({
4963
+ entryFile,
4964
+ importedPlayDependencies,
4965
+ adapter,
4966
+ exportName
4967
+ }) => runEsbuildForCjsNode(
4968
+ entryFile,
4969
+ importedPlayDependencies,
4970
+ adapter,
4971
+ exportName
4972
+ )
4973
+ },
4974
+ [PLAY_ARTIFACT_KINDS.esmWorkers]: {
4975
+ artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
4976
+ codeFormat: "esm_module",
4977
+ includeWorkersHarnessInGraphHash: true,
4978
+ runEsbuild: ({
4979
+ entryFile,
4980
+ importedPlayDependencies,
4981
+ adapter,
4982
+ exportName
4983
+ }) => runEsbuildForEsmWorkers(
4984
+ entryFile,
4985
+ importedPlayDependencies,
4986
+ adapter,
4987
+ exportName
4988
+ )
4989
+ }
4990
+ };
4991
+ function resolvePlayArtifactTargetAdapter(artifactKind) {
4992
+ return PLAY_ARTIFACT_TARGET_ADAPTERS[artifactKind];
4993
+ }
4882
4994
  async function bundlePlayFile(filePath, options) {
4883
4995
  const adapter = options.adapter;
4884
4996
  const target = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
4997
+ const targetAdapter = resolvePlayArtifactTargetAdapter(target);
4885
4998
  const exportName = options.exportName?.trim() || "default";
4886
4999
  assertValidExportName(exportName);
4887
5000
  const absolutePath = await normalizeLocalPath(filePath);
@@ -4892,7 +5005,7 @@ async function bundlePlayFile(filePath, options) {
4892
5005
  `${analysis.graphHash}
4893
5006
  entry-export:${exportName}`
4894
5007
  );
4895
- if (target === PLAY_ARTIFACT_KINDS.esmWorkers) {
5008
+ if (targetAdapter.includeWorkersHarnessInGraphHash) {
4896
5009
  const harnessFingerprint = await computeWorkersHarnessFingerprintWithAdapter(adapter);
4897
5010
  analysis.graphHash = sha256(
4898
5011
  `${analysis.graphHash}
@@ -4905,7 +5018,9 @@ workers-harness:${harnessFingerprint}`
4905
5018
  sourcePath: absolutePath,
4906
5019
  importedFilePaths: [
4907
5020
  ...analysis.importPolicy.localFiles,
4908
- ...analysis.importedPlayDependencies.map((dependency) => dependency.filePath)
5021
+ ...analysis.importedPlayDependencies.map(
5022
+ (dependency) => dependency.filePath
5023
+ )
4909
5024
  ]
4910
5025
  }) ?? []
4911
5026
  ];
@@ -4916,7 +5031,11 @@ workers-harness:${harnessFingerprint}`
4916
5031
  errors: typecheckErrors
4917
5032
  };
4918
5033
  }
4919
- const cachedArtifact = await readArtifactCache(analysis.graphHash, target, adapter);
5034
+ const cachedArtifact = await readArtifactCache(
5035
+ analysis.graphHash,
5036
+ target,
5037
+ adapter
5038
+ );
4920
5039
  const discoveredFiles = await adapter.discoverPackagedLocalFiles(absolutePath);
4921
5040
  if (cachedArtifact) {
4922
5041
  const cachedArtifactSizeError = getBundleSizeError(
@@ -4943,7 +5062,12 @@ workers-harness:${harnessFingerprint}`
4943
5062
  importedPlayDependencies: analysis.importedPlayDependencies
4944
5063
  };
4945
5064
  }
4946
- const buildOutcome = target === PLAY_ARTIFACT_KINDS.esmWorkers ? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName) : await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
5065
+ const buildOutcome = await targetAdapter.runEsbuild({
5066
+ entryFile: absolutePath,
5067
+ importedPlayDependencies: analysis.importedPlayDependencies,
5068
+ adapter,
5069
+ exportName
5070
+ });
4947
5071
  if (Array.isArray(buildOutcome)) {
4948
5072
  return {
4949
5073
  success: false,
@@ -4970,9 +5094,8 @@ workers-harness:${harnessFingerprint}`
4970
5094
  errors: [bundleSizeError]
4971
5095
  };
4972
5096
  }
4973
- const codeFormat = target === PLAY_ARTIFACT_KINDS.esmWorkers ? "esm_module" : "cjs_module";
4974
5097
  const artifact = {
4975
- codeFormat,
5098
+ codeFormat: targetAdapter.codeFormat,
4976
5099
  artifactKind: target,
4977
5100
  entryFile: absolutePath,
4978
5101
  virtualFilename,
@@ -5016,6 +5139,13 @@ workers-harness:${harnessFingerprint}`
5016
5139
  }
5017
5140
  }
5018
5141
 
5142
+ // ../shared_libs/play-runtime/dedup-backend.ts
5143
+ var PLAY_DEDUP_BACKENDS = {
5144
+ inMemory: "in_memory",
5145
+ neonTable: "neon_table",
5146
+ durableObject: "durable_object"
5147
+ };
5148
+
5019
5149
  // ../shared_libs/play-runtime/scheduler-backend.ts
5020
5150
  var PLAY_SCHEDULER_BACKENDS = {
5021
5151
  temporal: "temporal",
@@ -5023,44 +5153,65 @@ var PLAY_SCHEDULER_BACKENDS = {
5023
5153
  inProcess: "in-process"
5024
5154
  };
5025
5155
 
5026
- // ../shared_libs/play-runtime/dedup-backend.ts
5027
- var PLAY_DEDUP_BACKENDS = {
5028
- inMemory: "in_memory",
5029
- neonTable: "neon_table",
5030
- durableObject: "durable_object"
5156
+ // ../shared_libs/play-runtime/providers.ts
5157
+ var PLAY_RUNTIME_PROVIDER_IDS = {
5158
+ workersEdge: "workers_edge",
5159
+ local: "local"
5031
5160
  };
5032
-
5033
- // ../shared_libs/play-runtime/profiles.ts
5034
- var PLAY_EXECUTION_PROFILES = {
5161
+ var PLAY_RUNTIME_PROVIDERS = {
5035
5162
  workers_edge: {
5036
- id: "workers_edge",
5163
+ id: PLAY_RUNTIME_PROVIDER_IDS.workersEdge,
5037
5164
  scheduler: PLAY_SCHEDULER_BACKENDS.cfWorkflows,
5038
5165
  runner: PLAY_RUNTIME_BACKENDS.cloudflareWorkers,
5039
5166
  dedup: PLAY_DEDUP_BACKENDS.durableObject,
5167
+ artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
5040
5168
  label: "Cloudflare Dynamic Workflows + Dynamic Workers + DO dedup"
5041
5169
  },
5042
5170
  local: {
5043
- id: "local",
5171
+ id: PLAY_RUNTIME_PROVIDER_IDS.local,
5044
5172
  scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
5045
5173
  runner: PLAY_RUNTIME_BACKENDS.localProcess,
5046
5174
  dedup: PLAY_DEDUP_BACKENDS.inMemory,
5175
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
5047
5176
  label: "Local Temporal scheduler + local subprocess runner (tests)"
5048
5177
  }
5049
5178
  };
5050
- function defaultExecutionProfile() {
5051
- return PLAY_EXECUTION_PROFILES.workers_edge;
5179
+ function defaultPlayRuntimeProvider() {
5180
+ return PLAY_RUNTIME_PROVIDERS.workers_edge;
5052
5181
  }
5053
- function resolveExecutionProfile(override) {
5182
+ function resolvePlayRuntimeProvider(override) {
5054
5183
  if (override?.trim()) {
5055
5184
  const id = override.trim();
5056
- if (id in PLAY_EXECUTION_PROFILES) {
5057
- return PLAY_EXECUTION_PROFILES[id];
5185
+ if (id in PLAY_RUNTIME_PROVIDERS) {
5186
+ return PLAY_RUNTIME_PROVIDERS[id];
5058
5187
  }
5059
5188
  throw new Error(
5060
- `Unknown execution profile "${id}". Expected one of: ${Object.keys(PLAY_EXECUTION_PROFILES).join(", ")}.`
5189
+ `Unknown play runtime provider "${id}". Expected one of: ${Object.keys(
5190
+ PLAY_RUNTIME_PROVIDERS
5191
+ ).join(", ")}.`
5061
5192
  );
5062
5193
  }
5063
- return defaultExecutionProfile();
5194
+ return defaultPlayRuntimeProvider();
5195
+ }
5196
+
5197
+ // ../shared_libs/play-runtime/profiles.ts
5198
+ var PLAY_EXECUTION_PROFILE_IDS = {
5199
+ ...PLAY_RUNTIME_PROVIDER_IDS
5200
+ };
5201
+ var PLAY_EXECUTION_PROFILES = PLAY_RUNTIME_PROVIDERS;
5202
+ function resolveExecutionProfile(override) {
5203
+ try {
5204
+ return resolvePlayRuntimeProvider(override);
5205
+ } catch (error) {
5206
+ if (override?.trim()) {
5207
+ throw new Error(
5208
+ `Unknown execution profile "${override.trim()}". Expected one of: ${Object.keys(
5209
+ PLAY_EXECUTION_PROFILES
5210
+ ).join(", ")}.`
5211
+ );
5212
+ }
5213
+ throw error;
5214
+ }
5064
5215
  }
5065
5216
 
5066
5217
  // src/plays/local-file-discovery.ts
@@ -5379,8 +5530,19 @@ var SDK_PACKAGE_JSON = (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "package
5379
5530
  var SDK_ENTRY_FILE = (0, import_node_path9.resolve)(SDK_SOURCE_ROOT, "index.ts");
5380
5531
  var SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES ? SDK_ENTRY_FILE : (0, import_node_path9.resolve)(SDK_PACKAGE_ROOT, "dist", "index.d.ts");
5381
5532
  var SDK_WORKERS_ENTRY_FILE = (0, import_node_path9.resolve)(SDK_SOURCE_ROOT, "worker-play-entry.ts");
5382
- var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path9.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src", "entry.ts");
5383
- var WORKERS_HARNESS_FILES_DIR = (0, import_node_path9.resolve)(PROJECT_ROOT, "apps", "play-runner-workers", "src");
5533
+ var WORKERS_HARNESS_ENTRY_FILE = (0, import_node_path9.resolve)(
5534
+ PROJECT_ROOT,
5535
+ "apps",
5536
+ "play-runner-workers",
5537
+ "src",
5538
+ "entry.ts"
5539
+ );
5540
+ var WORKERS_HARNESS_FILES_DIR = (0, import_node_path9.resolve)(
5541
+ PROJECT_ROOT,
5542
+ "apps",
5543
+ "play-runner-workers",
5544
+ "src"
5545
+ );
5384
5546
  var hasWarnedAboutNonDevelopmentBundling = false;
5385
5547
  function warnAboutNonDevelopmentBundling(filePath) {
5386
5548
  if (hasWarnedAboutNonDevelopmentBundling) {
@@ -5399,13 +5561,16 @@ function warnAboutNonDevelopmentBundling(filePath) {
5399
5561
  );
5400
5562
  }
5401
5563
  function defaultPlayBundleTarget() {
5402
- return PLAY_BACKEND_DESCRIPTORS[resolveExecutionProfile(null).runner].artifactKind;
5564
+ return resolveExecutionProfile(null).artifactKind;
5403
5565
  }
5404
5566
  function createSdkPlayBundlingAdapter() {
5405
5567
  return {
5406
5568
  projectRoot: PROJECT_ROOT,
5407
5569
  nodeModulesDir: (0, import_node_path9.resolve)(PROJECT_ROOT, "node_modules"),
5408
- cacheDir: (0, import_node_path9.join)((0, import_node_os5.tmpdir)(), `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`),
5570
+ cacheDir: (0, import_node_path9.join)(
5571
+ (0, import_node_os5.tmpdir)(),
5572
+ `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`
5573
+ ),
5409
5574
  sdkSourceRoot: SDK_SOURCE_ROOT,
5410
5575
  sdkPackageJson: SDK_PACKAGE_JSON,
5411
5576
  sdkEntryFile: SDK_ENTRY_FILE,
@@ -5570,6 +5735,204 @@ function createCliProgress(enabled) {
5570
5735
  return progress;
5571
5736
  }
5572
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
+
5573
5936
  // src/cli/trace.ts
5574
5937
  var cliTraceStartedAt = Date.now();
5575
5938
  function isTruthyEnv(value) {
@@ -5618,6 +5981,31 @@ async function traceCliSpan(phase, fields, run) {
5618
5981
  }
5619
5982
  }
5620
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
+
5621
6009
  // src/cli/commands/play.ts
5622
6010
  var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
5623
6011
  "--json",
@@ -5697,9 +6085,23 @@ function formatPlayListReference(play) {
5697
6085
  return play.reference || play.name;
5698
6086
  }
5699
6087
  function defaultMaterializedPlayPath(reference) {
6088
+ return (0, import_node_path10.resolve)(defaultStarterPlayPath(reference));
6089
+ }
6090
+ function defaultStarterPlayPath(reference) {
5700
6091
  const playName = parseReferencedPlayTarget(reference).unqualifiedPlayName;
5701
6092
  const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5702
- 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
+ };
5703
6105
  }
5704
6106
  function materializeRemotePlaySource(input) {
5705
6107
  if (isFileTarget(input.target)) {
@@ -6145,6 +6547,8 @@ var TERMINAL_PLAY_STATUSES2 = /* @__PURE__ */ new Set([
6145
6547
  "cancelled"
6146
6548
  ]);
6147
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";
6148
6552
  function getEventPayload(event) {
6149
6553
  return event.payload && typeof event.payload === "object" ? event.payload : {};
6150
6554
  }
@@ -6198,6 +6602,86 @@ function getLogLinesFromLiveEvent(event) {
6198
6602
  const lines = getEventPayload(event).lines;
6199
6603
  return Array.isArray(lines) ? lines.filter((line) => typeof line === "string") : [];
6200
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
+ }
6201
6685
  function describeLiveEventPhase(event) {
6202
6686
  const payload = getEventPayload(event);
6203
6687
  if (event.type === "play.run.status") {
@@ -6350,6 +6834,14 @@ async function waitForPlayCompletionByStream(input) {
6350
6834
  lastPhase = phase;
6351
6835
  input.progress.phase(phase);
6352
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
+ });
6353
6845
  printPlayLogLines({
6354
6846
  lines: getLogLinesFromLiveEvent(event),
6355
6847
  status: null,
@@ -6498,6 +6990,14 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6498
6990
  state,
6499
6991
  progress: input.progress
6500
6992
  });
6993
+ emitLiveDebugTableHints({
6994
+ event,
6995
+ playName: input.playName,
6996
+ runId: workflowId,
6997
+ jsonOutput: input.jsonOutput,
6998
+ state,
6999
+ progress: input.progress
7000
+ });
6501
7001
  if (!input.jsonOutput) {
6502
7002
  printPlayProgressLines({
6503
7003
  lines: getProgressLinesFromLiveEvent(event),
@@ -6553,6 +7053,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6553
7053
  });
6554
7054
  return waitForPlayCompletionByStream({
6555
7055
  client: input.client,
7056
+ playName: input.playName,
6556
7057
  workflowId: lastKnownWorkflowId,
6557
7058
  dashboardUrl,
6558
7059
  jsonOutput: input.jsonOutput,
@@ -6588,6 +7089,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input) {
6588
7089
  });
6589
7090
  return waitForPlayCompletionByStream({
6590
7091
  client: input.client,
7092
+ playName: input.playName,
6591
7093
  workflowId: lastKnownWorkflowId,
6592
7094
  dashboardUrl,
6593
7095
  jsonOutput: input.jsonOutput,
@@ -7971,6 +8473,10 @@ async function handlePlayCheck(args) {
7971
8473
  });
7972
8474
  const enrichedResult = {
7973
8475
  ...result,
8476
+ errors: result.valid ? result.errors : addPlayCheckRepairHints({
8477
+ errors: result.errors,
8478
+ sourceCode: graph.root.sourceCode
8479
+ }),
7974
8480
  toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
7975
8481
  };
7976
8482
  if (options.jsonOutput) {
@@ -8756,25 +9262,13 @@ function parsePlaySearchOptions(args) {
8756
9262
  const query = args[0]?.trim();
8757
9263
  if (!query) {
8758
9264
  throw new Error(
8759
- "Usage: deepline plays search <query> [--origin prebuilt|owned] [--compact] [--json]"
9265
+ "Usage: deepline plays search <query> [--compact] [--json]"
8760
9266
  );
8761
9267
  }
8762
- let origin;
8763
- for (let index = 1; index < args.length; index += 1) {
8764
- const arg = args[index];
8765
- if (arg === "--origin" && args[index + 1]) {
8766
- const rawOrigin = args[++index].trim().toLowerCase();
8767
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8768
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8769
- }
8770
- origin = rawOrigin;
8771
- }
8772
- }
8773
9268
  return {
8774
9269
  query,
8775
9270
  jsonOutput: argsWantJson(args),
8776
- compact: args.includes("--compact"),
8777
- origin
9271
+ compact: args.includes("--compact")
8778
9272
  };
8779
9273
  }
8780
9274
  function printPlayDescription(play) {
@@ -8823,10 +9317,10 @@ function printPlayDescription(play) {
8823
9317
  }
8824
9318
  }
8825
9319
  console.log(` Run: ${play.runCommand}`);
8826
- if (play.origin === "prebuilt" && !play.canEdit) {
8827
- console.log(
8828
- ` Customize: deepline plays get ${reference} --source --out ./my-play.play.ts`
8829
- );
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}`);
8830
9324
  }
8831
9325
  }
8832
9326
  function compactPlaySchema(schema) {
@@ -8858,6 +9352,7 @@ function summarizePlayListItemForCli(play, options) {
8858
9352
  const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
8859
9353
  const rowOutputSchema = playSchemaMetadata(play.outputSchema, "rowOutputSchema");
8860
9354
  const runCommand2 = playRunCommand(play, { csvInput });
9355
+ const cloneEditStarter = buildCloneEditStarter(play);
8861
9356
  return {
8862
9357
  name: play.name,
8863
9358
  ...play.reference ? { reference: play.reference } : {},
@@ -8873,6 +9368,7 @@ function summarizePlayListItemForCli(play, options) {
8873
9368
  ...rowOutputSchema ? { rowOutputSchema } : {},
8874
9369
  runCommand: runCommand2,
8875
9370
  examples: [runCommand2],
9371
+ ...cloneEditStarter ? { cloneEditStarter } : {},
8876
9372
  currentPublishedVersion: play.currentPublishedVersion ?? null,
8877
9373
  isDraftDirty: play.isDraftDirty
8878
9374
  };
@@ -8888,7 +9384,6 @@ async function handlePlaySearch(args) {
8888
9384
  const client = new DeeplineClient();
8889
9385
  const plays = await client.searchPlays({
8890
9386
  query: options.query,
8891
- ...options.origin ? { origin: options.origin } : {},
8892
9387
  compact: options.compact
8893
9388
  });
8894
9389
  if (options.jsonOutput) {
@@ -8934,20 +9429,12 @@ function matchesPlayGrepQuery(value, query, mode) {
8934
9429
  async function handlePlayGrep(args) {
8935
9430
  const query = args[0]?.trim();
8936
9431
  if (!query) {
8937
- 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]");
8938
9433
  return 1;
8939
9434
  }
8940
- let origin;
8941
9435
  let mode = "all";
8942
9436
  for (let index = 1; index < args.length; index += 1) {
8943
9437
  const arg = args[index];
8944
- if (arg === "--origin" && args[index + 1]) {
8945
- const rawOrigin = args[++index].trim().toLowerCase();
8946
- if (rawOrigin !== "prebuilt" && rawOrigin !== "owned") {
8947
- throw new Error(`Invalid value for --origin: ${rawOrigin}`);
8948
- }
8949
- origin = rawOrigin;
8950
- }
8951
9438
  if (arg === "--mode" && args[index + 1]) {
8952
9439
  const rawMode = args[++index].trim().toLowerCase();
8953
9440
  mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
@@ -8957,13 +9444,8 @@ async function handlePlayGrep(args) {
8957
9444
  const client = new DeeplineClient();
8958
9445
  const plays = (await client.listPlays({
8959
9446
  grep: query,
8960
- grepMode: mode,
8961
- ...origin ? { origin } : {}
8962
- })).filter((play) => {
8963
- if (!origin) return true;
8964
- const isPrebuilt = play.origin === "prebuilt" || play.ownerType === "deepline";
8965
- return origin === "prebuilt" ? isPrebuilt : !isPrebuilt;
8966
- }).filter(
9447
+ grepMode: mode
9448
+ })).filter(
8967
9449
  (play) => matchesPlayGrepQuery(
8968
9450
  {
8969
9451
  name: play.name,
@@ -8984,8 +9466,7 @@ async function handlePlayGrep(args) {
8984
9466
  plays,
8985
9467
  count: plays.length,
8986
9468
  query,
8987
- grep: { mode, terms: parsePlayGrepTerms(query, mode) },
8988
- filters: { origin: origin ?? null }
9469
+ grep: { mode, terms: parsePlayGrepTerms(query, mode) }
8989
9470
  })}
8990
9471
  `);
8991
9472
  return 0;
@@ -9321,7 +9802,7 @@ Notes:
9321
9802
  Examples:
9322
9803
  deepline plays list
9323
9804
  deepline plays list --origin prebuilt --json
9324
- deepline plays search email --origin prebuilt --json
9805
+ deepline plays search email --json
9325
9806
  `
9326
9807
  ).option("--origin <origin>", "Filter to prebuilt or owned plays").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
9327
9808
  process.exitCode = await handlePlayList([
@@ -9333,20 +9814,18 @@ Examples:
9333
9814
  "after",
9334
9815
  `
9335
9816
  Notes:
9336
- Ranked discovery for workflows. Use --origin prebuilt or --origin owned when
9337
- 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.
9338
9818
  The grep alias is the same ranked retrieval surface with a more literal name
9339
9819
  for agents that are filtering the play registry.
9340
9820
 
9341
9821
  Examples:
9342
9822
  deepline plays search email
9343
- deepline plays grep "linkedin to email" --origin prebuilt --compact --json
9823
+ deepline plays grep "linkedin to email" --compact --json
9344
9824
  deepline plays describe person-linkedin-to-email --json
9345
9825
  `
9346
- ).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) => {
9347
9827
  process.exitCode = await handlePlaySearch([
9348
9828
  query,
9349
- ...options.origin ? ["--origin", options.origin] : [],
9350
9829
  ...options.compact ? ["--compact"] : [],
9351
9830
  ...options.json ? ["--json"] : []
9352
9831
  ]);
@@ -9362,14 +9841,13 @@ Notes:
9362
9841
  --mode all for AND.
9363
9842
 
9364
9843
  Examples:
9365
- deepline plays grep email --origin prebuilt --json
9366
- 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
9367
9846
  deepline plays describe prebuilt/company-to-contact --json
9368
9847
  `
9369
- ).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) => {
9370
9849
  process.exitCode = await handlePlayGrep([
9371
9850
  query,
9372
- ...options.origin ? ["--origin", options.origin] : [],
9373
9851
  ...options.compact ? ["--compact"] : [],
9374
9852
  ...options.mode ? ["--mode", options.mode] : [],
9375
9853
  ...options.json ? ["--json"] : []
@@ -9385,6 +9863,7 @@ Notes:
9385
9863
  Examples:
9386
9864
  deepline plays describe person-linkedin-to-email
9387
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
9388
9867
  `
9389
9868
  ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
9390
9869
  process.exitCode = await handlePlayDescribe([
@@ -10209,6 +10688,11 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10209
10688
  const cost = recordField(tool, "cost");
10210
10689
  const deeplineCredits = numberField(tool, "deeplineCreditsPerPricingUnit", "deepline_credits_per_pricing_unit");
10211
10690
  const deeplineUsdPerPricingUnit = numberField(tool, "deeplineUsdPerPricingUnit", "deepline_usd_per_pricing_unit");
10691
+ const starterScript = seedToolListScript({
10692
+ toolId,
10693
+ payload: samplePayloadForInputFields(inputFields),
10694
+ rows: []
10695
+ });
10212
10696
  return {
10213
10697
  schemaVersion: 1,
10214
10698
  toolId,
@@ -10233,7 +10717,16 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
10233
10717
  extractedLists,
10234
10718
  extractedValues
10235
10719
  },
10236
- 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
+ }
10237
10730
  };
10238
10731
  }
10239
10732
  function extractionContractEntries(entries) {
@@ -10275,6 +10768,13 @@ function printCompactToolContract(tool, requestedToolId) {
10275
10768
  }
10276
10769
  console.log("");
10277
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
+ }
10278
10778
  if (listGetters.length || valueGetters.length) {
10279
10779
  console.log("");
10280
10780
  console.log("Getters:");
@@ -10326,14 +10826,7 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
10326
10826
  const contract = toolContractJsonForDescribe(tool, requestedToolId);
10327
10827
  const toolId = String(contract.toolId);
10328
10828
  const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
10329
- const sampleInput = Object.fromEntries(
10330
- inputFields.slice(0, 4).flatMap((field) => {
10331
- if (!isRecord4(field)) return [];
10332
- const name = stringField(field, "name");
10333
- if (!name) return [];
10334
- return [[name, sampleValueForField(field)]];
10335
- })
10336
- );
10829
+ const sampleInput = samplePayloadForInputFields(inputFields);
10337
10830
  console.log(`Use in a play:`);
10338
10831
  console.log("```ts");
10339
10832
  console.log("const result = await ctx.tools.execute({");
@@ -10394,6 +10887,16 @@ function sampleValueForField(field) {
10394
10887
  if (type === "object") return {};
10395
10888
  return "...";
10396
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
+ }
10397
10900
  function stableStepIdForTool(toolId) {
10398
10901
  return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
10399
10902
  }
@@ -10406,6 +10909,12 @@ function playResultExpression(entry) {
10406
10909
  }
10407
10910
  function toolMetadataJsonForDescribe(tool, requestedToolId) {
10408
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
+ });
10409
10918
  const {
10410
10919
  cost: _cost,
10411
10920
  deeplineCreditsPerPricingUnit: _deeplineCreditsPerPricingUnit,
@@ -10425,12 +10934,48 @@ function toolMetadataJsonForDescribe(tool, requestedToolId) {
10425
10934
  toolId,
10426
10935
  provider: tool.provider,
10427
10936
  displayName: tool.displayName,
10937
+ usageGuidance: usageGuidanceWithAccessDefaults(recordField(tool, "usageGuidance", "usage_guidance")),
10428
10938
  runtimeOutputHelp: {
10429
- 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.",
10430
10943
  observeActualShape: `deepline tools execute ${toolId} --input '{...}' --json`,
10431
10944
  observedOutput: `deepline tools execute ${toolId} --input '{...}' --json`,
10432
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.",
10433
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
10434
10979
  }
10435
10980
  };
10436
10981
  }
@@ -10647,6 +11192,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
10647
11192
  (0, import_node_fs10.writeFileSync)(scriptPath, script, { encoding: "utf-8", mode: 384 });
10648
11193
  return {
10649
11194
  path: scriptPath,
11195
+ sourceCode: script,
10650
11196
  projectDir,
10651
11197
  macCopyCommand: `mkdir -p ${shellQuote(projectDir)} && cp ${shellQuote(scriptPath)} ${shellQuote(`${projectDir}/${fileName}`)}`,
10652
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}`)}`