deepline 0.1.12 → 0.1.20
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/README.md +14 -6
- package/dist/cli/index.js +1346 -717
- package/dist/cli/index.mjs +1342 -713
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.js +221 -14
- package/dist/index.mjs +221 -14
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
- package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
- package/dist/repo/sdk/src/client.ts +237 -0
- package/dist/repo/sdk/src/config.ts +125 -8
- package/dist/repo/sdk/src/http.ts +29 -5
- package/dist/repo/sdk/src/play.ts +19 -36
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
- package/dist/repo/sdk/src/types.ts +25 -0
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
- package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
- package/dist/repo/shared_libs/plays/dataset.ts +28 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
- package/package.json +5 -4
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
- package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
- package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
- package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
- package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
- package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
- package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
- package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
- package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
- package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
- package/dist/repo/sdk/src/cli/index.ts +0 -148
- package/dist/repo/sdk/src/cli/progress.ts +0 -149
- package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
- package/dist/repo/sdk/src/cli/trace.ts +0 -61
- package/dist/repo/sdk/src/cli/utils.ts +0 -145
- package/dist/repo/sdk/src/compat.ts +0 -77
- package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
- package/dist/repo/shared_libs/observability/tracing.ts +0 -98
- package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
- package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
- package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
- package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
- package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
- package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
- package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
- package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
- package/dist/repo/shared_libs/plays/definition.ts +0 -264
- package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
- package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
- package/dist/repo/shared_libs/temporal/constants.ts +0 -39
- package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
|
@@ -70,6 +70,7 @@ import type {
|
|
|
70
70
|
PlayDataset,
|
|
71
71
|
PlayDatasetInput,
|
|
72
72
|
} from '../../shared_libs/plays/dataset.js';
|
|
73
|
+
import type { ToolExecuteResult } from '../../shared_libs/play-runtime/tool-result-types.js';
|
|
73
74
|
import type {
|
|
74
75
|
DeeplineClientOptions,
|
|
75
76
|
PlayDetail,
|
|
@@ -140,36 +141,14 @@ export type LoosePlayObject = {
|
|
|
140
141
|
[key: string]: LoosePlayObject;
|
|
141
142
|
};
|
|
142
143
|
|
|
143
|
-
export type
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
> = {
|
|
152
|
-
data: TData;
|
|
153
|
-
meta?: TMeta;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export type ToolExecuteResult<
|
|
157
|
-
TData = unknown,
|
|
158
|
-
TMeta = Record<string, unknown>,
|
|
159
|
-
> = {
|
|
160
|
-
status: string;
|
|
161
|
-
result: ToolResultEnvelope<TData, TMeta>;
|
|
162
|
-
extracted: Record<string, ToolExtractedValue>;
|
|
163
|
-
lists: Record<
|
|
164
|
-
string,
|
|
165
|
-
{
|
|
166
|
-
path: string;
|
|
167
|
-
count: number | null;
|
|
168
|
-
keys: Record<string, string>;
|
|
169
|
-
get(): Record<string, unknown>[];
|
|
170
|
-
}
|
|
171
|
-
>;
|
|
172
|
-
};
|
|
144
|
+
export type {
|
|
145
|
+
ToolExecuteResult,
|
|
146
|
+
ToolExecuteResultAccessors,
|
|
147
|
+
ToolExecuteResultBase,
|
|
148
|
+
ToolResultEnvelope,
|
|
149
|
+
ToolResultListAccessor,
|
|
150
|
+
ToolResultTargetAccessor as ToolExtractedValue,
|
|
151
|
+
} from '../../shared_libs/play-runtime/tool-result-types.js';
|
|
173
152
|
|
|
174
153
|
export type ToolExecutionRequest = {
|
|
175
154
|
id: string;
|
|
@@ -242,7 +221,6 @@ export type MapStepBuilder<
|
|
|
242
221
|
run(options?: {
|
|
243
222
|
description?: string;
|
|
244
223
|
staleAfterSeconds?: number;
|
|
245
|
-
concurrency?: number;
|
|
246
224
|
key?:
|
|
247
225
|
| (keyof InputRow & string)
|
|
248
226
|
| readonly (keyof InputRow & string)[]
|
|
@@ -270,10 +248,11 @@ export type FileInput<TMetadata = unknown> = string & {
|
|
|
270
248
|
* stage local paths passed to that flag, and use `TRow` for row-contract
|
|
271
249
|
* discovery.
|
|
272
250
|
*/
|
|
273
|
-
export type CsvInput<TRow extends object = Record<string, unknown>> =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
251
|
+
export type CsvInput<TRow extends object = Record<string, unknown>> =
|
|
252
|
+
FileInput<{
|
|
253
|
+
readonly kind: 'csv';
|
|
254
|
+
readonly row: TRow;
|
|
255
|
+
}>;
|
|
277
256
|
|
|
278
257
|
export type ColumnMap<TRow extends object> = Partial<
|
|
279
258
|
Record<Extract<keyof TRow, string>, string | readonly string[]>
|
|
@@ -943,7 +922,11 @@ export class DeeplineContext {
|
|
|
943
922
|
toolId: string,
|
|
944
923
|
input: Record<string, unknown>,
|
|
945
924
|
): Promise<ToolExecuteResult> =>
|
|
946
|
-
this.client.executeTool(
|
|
925
|
+
this.client.executeTool(
|
|
926
|
+
toolId,
|
|
927
|
+
input,
|
|
928
|
+
{ includeToolMetadata: true },
|
|
929
|
+
) as unknown as Promise<ToolExecuteResult>,
|
|
947
930
|
};
|
|
948
931
|
}
|
|
949
932
|
|
|
@@ -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 =
|
|
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 =
|
|
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:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
64
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
102
|
+
if (parts.length === 0) return null;
|
|
103
|
+
parts.push(expression.slice(start));
|
|
104
|
+
return parts;
|
|
73
105
|
}
|
|
74
106
|
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
function isRuntimeInputExpression(expression: string): boolean {
|
|
116
|
+
return /(^|[^\w$])input([^\w$]|$)/.test(expression);
|
|
117
|
+
}
|
|
89
118
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
115
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
166
|
+
continue;
|
|
130
167
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return
|
|
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
|
|
147
|
-
|
|
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
|
-
|
|
150
|
-
|
|
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
|
-
|
|
211
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
212
|
+
quote = char;
|
|
155
213
|
continue;
|
|
156
214
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
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
|
|
227
|
-
|
|
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
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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:
|
|
242
|
-
message:
|
|
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
|
|
245
|
-
const
|
|
246
|
-
if (resolvedPath
|
|
324
|
+
} else {
|
|
325
|
+
const absoluteCsvPath = resolve(dirname(absolutePath), resolvedPath);
|
|
326
|
+
if (isAbsolute(resolvedPath) || !isPathInsideDirectory(absoluteCsvPath, packagingRoot)) {
|
|
247
327
|
unresolved.push({
|
|
248
|
-
sourceFragment:
|
|
328
|
+
sourceFragment: sourceCode.slice(argument.start, argument.end).trim(),
|
|
249
329
|
message:
|
|
250
|
-
'
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
)
|
|
282
|
-
|
|
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.
|
|
2
|
-
export const SDK_API_CONTRACT = "2026-
|
|
1
|
+
export const SDK_VERSION = "0.1.20";
|
|
2
|
+
export const SDK_API_CONTRACT = "2026-05-runs-v2";
|