deepline 0.1.12 → 0.1.19

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.
Files changed (80) hide show
  1. package/README.md +14 -6
  2. package/dist/cli/index.js +1298 -711
  3. package/dist/cli/index.mjs +1294 -707
  4. package/dist/index.d.mts +199 -23
  5. package/dist/index.d.ts +199 -23
  6. package/dist/index.js +219 -13
  7. package/dist/index.mjs +219 -13
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +68 -12
  9. package/dist/repo/apps/play-runner-workers/src/entry.ts +241 -51
  10. package/dist/repo/sdk/src/client.ts +237 -0
  11. package/dist/repo/sdk/src/config.ts +125 -8
  12. package/dist/repo/sdk/src/http.ts +10 -2
  13. package/dist/repo/sdk/src/play.ts +19 -36
  14. package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
  15. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  16. package/dist/repo/sdk/src/types.ts +25 -0
  17. package/dist/repo/sdk/src/version.ts +2 -2
  18. package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
  19. package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
  20. package/dist/repo/shared_libs/plays/dataset.ts +28 -0
  21. package/package.json +5 -4
  22. package/dist/cli/index.js.map +0 -1
  23. package/dist/cli/index.mjs.map +0 -1
  24. package/dist/index.js.map +0 -1
  25. package/dist/index.mjs.map +0 -1
  26. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  27. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  28. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  29. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  30. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  31. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  32. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  33. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  34. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  35. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  36. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
  37. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  38. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
  39. package/dist/repo/sdk/src/cli/index.ts +0 -148
  40. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  41. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  42. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  43. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  44. package/dist/repo/sdk/src/compat.ts +0 -77
  45. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  46. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  47. package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
  48. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  49. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
  50. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  51. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  52. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  53. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  54. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  55. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  56. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  57. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  58. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  59. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  60. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  61. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  62. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  63. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  64. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  65. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  66. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  67. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  68. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  69. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  70. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  71. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  72. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  73. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  74. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  75. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  76. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  77. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  78. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
  79. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  80. package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
@@ -43,7 +43,7 @@ export {
43
43
  extractDefinedPlayName,
44
44
  } from '../../../shared_libs/plays/bundling/index.js';
45
45
 
46
- const PLAY_BUNDLE_CACHE_VERSION = 24;
46
+ const PLAY_BUNDLE_CACHE_VERSION = 26;
47
47
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
48
48
  const SDK_PACKAGE_ROOT = resolve(MODULE_DIR, '..', '..');
49
49
  const SOURCE_REPO_ROOT = resolve(SDK_PACKAGE_ROOT, '..');
@@ -66,13 +66,28 @@ const SDK_SOURCE_ROOT = HAS_SOURCE_BUNDLING_SOURCES
66
66
  : resolve(SDK_PACKAGE_ROOT, 'src');
67
67
  const SDK_PACKAGE_JSON = resolve(SDK_PACKAGE_ROOT, 'package.json');
68
68
  const SDK_ENTRY_FILE = resolve(SDK_SOURCE_ROOT, 'index.ts');
69
- const SDK_TYPES_ENTRY_FILE = resolve(SDK_PACKAGE_ROOT, 'dist', 'index.d.ts');
69
+ const SDK_TYPES_ENTRY_FILE = HAS_SOURCE_BUNDLING_SOURCES
70
+ ? SDK_ENTRY_FILE
71
+ : resolve(SDK_PACKAGE_ROOT, 'dist', 'index.d.ts');
70
72
  const SDK_WORKERS_ENTRY_FILE = resolve(SDK_SOURCE_ROOT, 'worker-play-entry.ts');
71
73
  const WORKERS_HARNESS_ENTRY_FILE = resolve(PROJECT_ROOT, 'apps', 'play-runner-workers', 'src', 'entry.ts');
72
74
  const WORKERS_HARNESS_FILES_DIR = resolve(PROJECT_ROOT, 'apps', 'play-runner-workers', 'src');
73
-
74
75
  let hasWarnedAboutNonDevelopmentBundling = false;
75
76
 
77
+ /**
78
+ * SDK/local bundling deliberately stays tool-metadata agnostic.
79
+ *
80
+ * The SDK's job is to turn a local play file into a portable artifact using
81
+ * only SDK/runtime sources. It must work for installed packages, source
82
+ * checkouts, CI smoke jobs, and clean worktrees without app-generated files
83
+ * such as `src/lib/generated/tool-typecheck-catalog.jsonl`.
84
+ *
85
+ * Tool-aware validation belongs to the Deepline API after upload:
86
+ * `compilePlayManifest` / server preflight runs the cloud typechecker inside
87
+ * the app deployment, where the generated catalog is a normal build artifact.
88
+ * Do not import `src/lib/plays/cloud-tool-typecheck` or any generated app
89
+ * catalog from this SDK adapter.
90
+ */
76
91
  function warnAboutNonDevelopmentBundling(filePath: string): void {
77
92
  if (hasWarnedAboutNonDevelopmentBundling) {
78
93
  return;
@@ -109,11 +124,10 @@ export function createSdkPlayBundlingAdapter(): PlayBundlingAdapter {
109
124
  sdkSourceRoot: SDK_SOURCE_ROOT,
110
125
  sdkPackageJson: SDK_PACKAGE_JSON,
111
126
  sdkEntryFile: SDK_ENTRY_FILE,
112
- sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES
113
- ? SDK_ENTRY_FILE
114
- : existsSync(SDK_TYPES_ENTRY_FILE)
115
- ? SDK_TYPES_ENTRY_FILE
116
- : SDK_ENTRY_FILE,
127
+ sdkTypesEntryFile:
128
+ HAS_SOURCE_BUNDLING_SOURCES || !existsSync(SDK_TYPES_ENTRY_FILE)
129
+ ? SDK_ENTRY_FILE
130
+ : SDK_TYPES_ENTRY_FILE,
117
131
  sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
118
132
  workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
119
133
  workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
@@ -1,7 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { readFile, stat } from 'node:fs/promises';
3
3
  import { basename, dirname, extname, isAbsolute, join, relative, resolve } from 'node:path';
4
- import ts from 'typescript';
5
4
 
6
5
  export interface PlayLocalFileReference {
7
6
  sourceFragment: string;
@@ -48,126 +47,198 @@ function contentTypeForFile(filePath: string): string {
48
47
  return 'application/octet-stream';
49
48
  }
50
49
 
51
- function isCtxCsvCall(node: ts.CallExpression): boolean {
52
- if (!ts.isPropertyAccessExpression(node.expression)) {
53
- return false;
54
- }
55
- const target = node.expression.expression;
56
- return (
57
- ts.isIdentifier(target) &&
58
- (target.text === 'ctx' || target.text.endsWith('Ctx')) &&
59
- node.expression.name.text === 'csv'
60
- );
50
+ function stripCommentsToSpaces(source: string): string {
51
+ return source
52
+ .replace(/\/\*[\s\S]*?\*\//g, (match) => match.replace(/[^\n]/g, ' '))
53
+ .replace(/(^|[^:])\/\/.*$/gm, (match, prefix: string) =>
54
+ prefix + ' '.repeat(Math.max(0, match.length - prefix.length)),
55
+ );
61
56
  }
62
57
 
63
- function extractSourceFragment(source: string, node: ts.Node): string {
64
- return source.slice(node.getStart(), node.getEnd()).trim();
58
+ function unquoteStringLiteral(literal: string): string | null {
59
+ const trimmed = literal.trim();
60
+ const quote = trimmed[0];
61
+ if ((quote !== '"' && quote !== "'") || trimmed[trimmed.length - 1] !== quote) {
62
+ return null;
63
+ }
64
+ try {
65
+ return JSON.parse(quote === '"' ? trimmed : `"${trimmed.slice(1, -1).replace(/"/g, '\\"')}"`);
66
+ } catch {
67
+ return trimmed.slice(1, -1);
68
+ }
65
69
  }
66
70
 
67
- function referencesInputIdentifier(node: ts.Node): boolean {
68
- if (ts.isIdentifier(node) && node.text === 'input') {
69
- return true;
71
+ function splitTopLevelPlus(expression: string): string[] | null {
72
+ const parts: string[] = [];
73
+ let start = 0;
74
+ let depth = 0;
75
+ let quote: string | null = null;
76
+ let escaped = false;
77
+
78
+ for (let index = 0; index < expression.length; index += 1) {
79
+ const char = expression[index]!;
80
+ if (quote) {
81
+ if (escaped) {
82
+ escaped = false;
83
+ } else if (char === '\\') {
84
+ escaped = true;
85
+ } else if (char === quote) {
86
+ quote = null;
87
+ }
88
+ continue;
89
+ }
90
+ if (char === '"' || char === "'" || char === '`') {
91
+ quote = char;
92
+ continue;
93
+ }
94
+ if (char === '(' || char === '[' || char === '{') depth += 1;
95
+ if (char === ')' || char === ']' || char === '}') depth -= 1;
96
+ if (char === '+' && depth === 0) {
97
+ parts.push(expression.slice(start, index));
98
+ start = index + 1;
99
+ }
70
100
  }
71
101
 
72
- return node.getChildren().some((child) => referencesInputIdentifier(child));
102
+ if (parts.length === 0) return null;
103
+ parts.push(expression.slice(start));
104
+ return parts;
73
105
  }
74
106
 
75
- function isRuntimeInputExpression(node: ts.Expression): boolean {
76
- if (ts.isPropertyAccessExpression(node)) {
77
- return ts.isIdentifier(node.expression) && node.expression.text === 'input';
78
- }
79
- if (ts.isElementAccessExpression(node)) {
80
- return ts.isIdentifier(node.expression) && node.expression.text === 'input';
81
- }
82
- if (ts.isIdentifier(node)) {
83
- return node.text === 'input';
107
+ function stripOuterParens(expression: string): string {
108
+ let value = expression.trim();
109
+ while (value.startsWith('(') && value.endsWith(')')) {
110
+ value = value.slice(1, -1).trim();
84
111
  }
112
+ return value;
113
+ }
85
114
 
86
- if (ts.isParenthesizedExpression(node)) {
87
- return isRuntimeInputExpression(node.expression);
88
- }
115
+ function isRuntimeInputExpression(expression: string): boolean {
116
+ return /(^|[^\w$])input([^\w$]|$)/.test(expression);
117
+ }
89
118
 
90
- if (
91
- ts.isBinaryExpression(node) &&
92
- (node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken ||
93
- node.operatorToken.kind === ts.SyntaxKind.BarBarToken)
94
- ) {
95
- return isRuntimeInputExpression(node.left) || isRuntimeInputExpression(node.right);
119
+ function resolveStringExpression(expression: string, constants: ConstMap): string | null {
120
+ const value = stripOuterParens(expression);
121
+ if (/^(['"])(?:\\.|(?!\1)[\s\S])*\1$/.test(value)) {
122
+ return unquoteStringLiteral(value);
96
123
  }
97
-
98
- if (ts.isConditionalExpression(node)) {
99
- return (
100
- isRuntimeInputExpression(node.condition) ||
101
- isRuntimeInputExpression(node.whenTrue) ||
102
- isRuntimeInputExpression(node.whenFalse)
103
- );
124
+ if (/^`(?:\\.|[^`$]|\$(?!\{))*`$/.test(value)) {
125
+ return value.slice(1, -1);
104
126
  }
105
-
106
- return referencesInputIdentifier(node);
107
- }
108
-
109
- function resolveStringExpression(node: ts.Expression, constants: ConstMap): string | null {
110
- if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
111
- return node.text;
127
+ if (/^[A-Za-z_$][\w$]*$/.test(value)) {
128
+ return constants.get(value) ?? null;
112
129
  }
113
-
114
- if (ts.isParenthesizedExpression(node)) {
115
- return resolveStringExpression(node.expression, constants);
130
+ const parts = splitTopLevelPlus(value);
131
+ if (parts) {
132
+ const resolved = parts.map((part) => resolveStringExpression(part, constants));
133
+ return resolved.every((part): part is string => part != null)
134
+ ? resolved.join('')
135
+ : null;
116
136
  }
137
+ return null;
138
+ }
117
139
 
118
- if (ts.isIdentifier(node)) {
119
- return constants.get(node.text) ?? null;
140
+ function collectTopLevelStringConstants(sourceCode: string): ConstMap {
141
+ const constants: ConstMap = new Map();
142
+ const source = stripCommentsToSpaces(sourceCode);
143
+ for (const match of source.matchAll(/(?:^|\n)\s*const\s+([A-Za-z_$][\w$]*)\s*=\s*([^;\n]+)/g)) {
144
+ const resolved = resolveStringExpression(match[2]!, constants);
145
+ if (resolved != null) {
146
+ constants.set(match[1]!, resolved);
147
+ }
120
148
  }
149
+ return constants;
150
+ }
121
151
 
122
- if (ts.isTemplateExpression(node)) {
123
- let value = node.head.text;
124
- for (const span of node.templateSpans) {
125
- const resolved = resolveStringExpression(span.expression, constants);
126
- if (resolved == null) {
127
- return null;
152
+ function findMatchingGenericEnd(source: string, openIndex: number): number {
153
+ let depth = 0;
154
+ let quote: string | null = null;
155
+ let escaped = false;
156
+ for (let index = openIndex; index < source.length; index += 1) {
157
+ const char = source[index]!;
158
+ if (quote) {
159
+ if (escaped) {
160
+ escaped = false;
161
+ } else if (char === '\\') {
162
+ escaped = true;
163
+ } else if (char === quote) {
164
+ quote = null;
128
165
  }
129
- value += resolved + span.literal.text;
166
+ continue;
130
167
  }
131
- return value;
132
- }
133
-
134
- if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
135
- const left = resolveStringExpression(node.left, constants);
136
- const right = resolveStringExpression(node.right, constants);
137
- if (left == null || right == null) {
138
- return null;
168
+ if (char === '"' || char === "'" || char === '`') {
169
+ quote = char;
170
+ continue;
171
+ }
172
+ if (char === '<') depth += 1;
173
+ if (char === '>') {
174
+ depth -= 1;
175
+ if (depth === 0) return index;
139
176
  }
140
- return left + right;
141
177
  }
142
-
143
- return null;
178
+ return -1;
144
179
  }
145
180
 
146
- function collectTopLevelStringConstants(sourceFile: ts.SourceFile): ConstMap {
147
- const constants: ConstMap = new Map();
181
+ function findCallOpenParen(source: string, afterCsvIndex: number): number {
182
+ let index = afterCsvIndex;
183
+ while (/\s/.test(source[index] ?? '')) index += 1;
184
+ if (source[index] === '<') {
185
+ const genericEnd = findMatchingGenericEnd(source, index);
186
+ if (genericEnd < 0) return -1;
187
+ index = genericEnd + 1;
188
+ while (/\s/.test(source[index] ?? '')) index += 1;
189
+ }
190
+ return source[index] === '(' ? index : -1;
191
+ }
148
192
 
149
- for (const statement of sourceFile.statements) {
150
- if (!ts.isVariableStatement(statement)) {
193
+ function firstCallArgument(source: string, openParen: number): { text: string; start: number; end: number } | null {
194
+ let depth = 0;
195
+ let quote: string | null = null;
196
+ let escaped = false;
197
+ const start = openParen + 1;
198
+
199
+ for (let index = start; index < source.length; index += 1) {
200
+ const char = source[index]!;
201
+ if (quote) {
202
+ if (escaped) {
203
+ escaped = false;
204
+ } else if (char === '\\') {
205
+ escaped = true;
206
+ } else if (char === quote) {
207
+ quote = null;
208
+ }
151
209
  continue;
152
210
  }
153
-
154
- if (!(statement.declarationList.flags & ts.NodeFlags.Const)) {
211
+ if (char === '"' || char === "'" || char === '`') {
212
+ quote = char;
155
213
  continue;
156
214
  }
157
-
158
- for (const declaration of statement.declarationList.declarations) {
159
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
160
- continue;
161
- }
162
-
163
- const resolved = resolveStringExpression(declaration.initializer, constants);
164
- if (resolved != null) {
165
- constants.set(declaration.name.text, resolved);
166
- }
215
+ if (char === '(' || char === '[' || char === '{') depth += 1;
216
+ if (char === ')' && depth === 0) {
217
+ const text = source.slice(start, index).trim();
218
+ return text ? { text, start, end: index } : null;
167
219
  }
220
+ if (char === ',' && depth === 0) {
221
+ const text = source.slice(start, index).trim();
222
+ return text ? { text, start, end: index } : null;
223
+ }
224
+ if (char === ')' || char === ']' || char === '}') depth -= 1;
168
225
  }
169
226
 
170
- return constants;
227
+ return null;
228
+ }
229
+
230
+ function localImportSpecifiers(sourceCode: string): string[] {
231
+ const source = stripCommentsToSpaces(sourceCode);
232
+ const specifiers: string[] = [];
233
+ for (const match of source.matchAll(
234
+ /\b(?:import|export)\s+(?!type\b)(?:[\s\S]*?\s+from\s*)?['"]([^'"]+)['"]/g,
235
+ )) {
236
+ if (match[1]?.startsWith('.')) specifiers.push(match[1]);
237
+ }
238
+ for (const match of source.matchAll(/\brequire\s*\(\s*(['"])(\.[^'"]*)\1\s*\)/g)) {
239
+ specifiers.push(match[2]!);
240
+ }
241
+ return specifiers;
171
242
  }
172
243
 
173
244
  async function fileExists(filePath: string): Promise<boolean> {
@@ -223,88 +294,64 @@ export async function discoverPackagedLocalFiles(
223
294
  visitedFiles.add(absolutePath);
224
295
 
225
296
  const sourceCode = await readFile(absolutePath, 'utf-8');
226
- const sourceFile = ts.createSourceFile(
227
- absolutePath,
228
- sourceCode,
229
- ts.ScriptTarget.Latest,
230
- true,
231
- ts.ScriptKind.TS,
232
- );
233
- const constants = collectTopLevelStringConstants(sourceFile);
297
+ const scanSource = stripCommentsToSpaces(sourceCode);
298
+ const constants = collectTopLevelStringConstants(sourceCode);
234
299
  const childVisits: Promise<void>[] = [];
235
300
 
236
- const visitNode = async (node: ts.Node): Promise<void> => {
237
- if (ts.isCallExpression(node) && isCtxCsvCall(node)) {
238
- const argument = node.arguments[0];
239
- if (!argument) {
301
+ for (const match of scanSource.matchAll(/\b([A-Za-z_$][\w$]*)\s*\.\s*csv\b/g)) {
302
+ const target = match[1]!;
303
+ if (target !== 'ctx' && !target.endsWith('Ctx')) {
304
+ continue;
305
+ }
306
+ const openParen = findCallOpenParen(scanSource, match.index! + match[0].length);
307
+ if (openParen < 0) {
308
+ continue;
309
+ }
310
+ const argument = firstCallArgument(scanSource, openParen);
311
+ if (!argument) {
312
+ unresolved.push({
313
+ sourceFragment: 'ctx.csv()',
314
+ message: 'ctx.csv() requires a file path string or input reference.',
315
+ });
316
+ } else if (!isRuntimeInputExpression(argument.text)) {
317
+ const resolvedPath = resolveStringExpression(argument.text, constants);
318
+ if (resolvedPath == null) {
240
319
  unresolved.push({
241
- sourceFragment: 'ctx.csv()',
242
- message: 'ctx.csv() requires a file path string or input reference.',
320
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
321
+ message:
322
+ 'Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file.',
243
323
  });
244
- } else if (!isRuntimeInputExpression(argument)) {
245
- const resolvedPath = resolveStringExpression(argument, constants);
246
- if (resolvedPath == null) {
324
+ } else {
325
+ const absoluteCsvPath = resolve(dirname(absolutePath), resolvedPath);
326
+ if (isAbsolute(resolvedPath) || !isPathInsideDirectory(absoluteCsvPath, packagingRoot)) {
247
327
  unresolved.push({
248
- sourceFragment: extractSourceFragment(sourceCode, argument),
328
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
249
329
  message:
250
- 'Could not resolve this ctx.csv(...) path at submit time. Use a string literal, a top-level const string, or pass a runtime input like input.file.',
251
- });
252
- } else {
253
- const absoluteCsvPath = resolve(dirname(absolutePath), resolvedPath);
254
- if (isAbsolute(resolvedPath) || !isPathInsideDirectory(absoluteCsvPath, packagingRoot)) {
255
- unresolved.push({
256
- sourceFragment: extractSourceFragment(sourceCode, argument),
257
- message:
258
- 'ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead.',
259
- });
260
- return;
261
- }
262
- const buffer = await readFile(absoluteCsvPath);
263
- const stats = await stat(absoluteCsvPath);
264
- files.set(absoluteCsvPath, {
265
- sourceFragment: extractSourceFragment(sourceCode, argument),
266
- logicalPath: resolvedPath,
267
- absolutePath: absoluteCsvPath,
268
- bytes: stats.size,
269
- contentHash: sha256(buffer),
270
- contentType: contentTypeForFile(absoluteCsvPath),
330
+ 'ctx.csv(...) packaged file paths must be relative paths inside the play directory. Pass external files at runtime with input.file instead.',
271
331
  });
332
+ continue;
272
333
  }
334
+ const buffer = await readFile(absoluteCsvPath);
335
+ const stats = await stat(absoluteCsvPath);
336
+ files.set(absoluteCsvPath, {
337
+ sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
338
+ logicalPath: resolvedPath,
339
+ absolutePath: absoluteCsvPath,
340
+ bytes: stats.size,
341
+ contentHash: sha256(buffer),
342
+ contentType: contentTypeForFile(absoluteCsvPath),
343
+ });
273
344
  }
274
345
  }
346
+ }
275
347
 
276
- if (
277
- ts.isImportDeclaration(node) &&
278
- !node.importClause?.isTypeOnly &&
279
- ts.isStringLiteral(node.moduleSpecifier) &&
280
- node.moduleSpecifier.text.startsWith('.')
281
- ) {
282
- childVisits.push(
283
- resolveLocalImport(absolutePath, node.moduleSpecifier.text).then((resolvedImport) =>
284
- visitSourceFile(resolvedImport),
285
- ),
286
- );
287
- }
288
-
289
- if (
290
- ts.isCallExpression(node) &&
291
- ts.isIdentifier(node.expression) &&
292
- node.expression.text === 'require' &&
293
- node.arguments.length === 1 &&
294
- ts.isStringLiteral(node.arguments[0]) &&
295
- node.arguments[0].text.startsWith('.')
296
- ) {
297
- childVisits.push(
298
- resolveLocalImport(absolutePath, node.arguments[0].text).then((resolvedImport) =>
299
- visitSourceFile(resolvedImport),
300
- ),
301
- );
302
- }
303
-
304
- await Promise.all(node.getChildren(sourceFile).map((child) => visitNode(child)));
305
- };
306
-
307
- await visitNode(sourceFile);
348
+ for (const specifier of localImportSpecifiers(sourceCode)) {
349
+ childVisits.push(
350
+ resolveLocalImport(absolutePath, specifier).then((resolvedImport) =>
351
+ visitSourceFile(resolvedImport),
352
+ ),
353
+ );
354
+ }
308
355
  await Promise.all(childVisits);
309
356
  };
310
357
 
@@ -114,6 +114,27 @@ export interface ToolDefinition {
114
114
  resultIdentityGetters?: Record<string, string[]>;
115
115
  /** Whether the output is a direct object or wrapped in a `result` envelope. */
116
116
  rowContextShape?: 'direct' | 'result_envelope';
117
+ /** Search relevance score returned by ranked tool search. */
118
+ search_score?: number;
119
+ /** Search match snippets returned by ranked tool search. */
120
+ search_matches?: Array<{
121
+ field: string;
122
+ value: string;
123
+ term?: string;
124
+ }>;
125
+ }
126
+
127
+ export interface ToolSearchOptions {
128
+ query?: string;
129
+ categories?: string;
130
+ searchTerms?: string;
131
+ searchMode?: 'v1' | 'v2';
132
+ includeSearchDebug?: boolean;
133
+ }
134
+
135
+ export interface ToolSearchResult {
136
+ tools: ToolDefinition[];
137
+ search_fallback_to_category?: boolean;
117
138
  }
118
139
 
119
140
  /**
@@ -201,6 +222,8 @@ export interface PlayProgressStatus {
201
222
  totalRows?: number;
202
223
  /** Accumulated log lines from `ctx.log()`. Grows monotonically. */
203
224
  logs: string[];
225
+ /** Zero-based offset of the first returned log line when a tail cursor is used. */
226
+ logOffset?: number;
204
227
  /** Error message if the play has failed. */
205
228
  error?: string;
206
229
  }
@@ -606,6 +629,8 @@ export interface StartPlayRunRequest {
606
629
  artifactStorageKey?: string;
607
630
  /** Source snapshot already validated while registering this artifact. */
608
631
  sourceCode?: string;
632
+ /** Source graph snapshots for local helper files included in cloud preflight. */
633
+ sourceFiles?: Record<string, string>;
609
634
  /** Static pipeline already produced while registering this artifact. */
610
635
  staticPipeline?: unknown;
611
636
  /** Artifact content hash already validated while registering this artifact. */
@@ -1,2 +1,2 @@
1
- export const SDK_VERSION = "0.1.12";
2
- export const SDK_API_CONTRACT = "2026-04-plays-v1";
1
+ export const SDK_VERSION = "0.1.19";
2
+ export const SDK_API_CONTRACT = "2026-05-runs-v2";