deepline 0.1.46 → 0.1.48
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 -14
- package/dist/cli/index.js +85 -25
- package/dist/cli/index.mjs +85 -25
- package/dist/index.d.mts +59 -35
- package/dist/index.d.ts +59 -35
- package/dist/index.js +5 -9
- package/dist/index.mjs +5 -9
- package/dist/repo/apps/play-runner-workers/src/entry.ts +380 -305
- package/dist/repo/apps/play-runner-workers/src/runtime/receipts.ts +51 -21
- package/dist/repo/sdk/src/play.ts +35 -42
- package/dist/repo/sdk/src/plays/harness-stub.ts +1 -1
- package/dist/repo/sdk/src/types.ts +26 -3
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +17 -67
- package/dist/repo/shared_libs/plays/row-identity.ts +5 -59
- package/package.json +1 -1
|
@@ -48,6 +48,7 @@ export type WorkerRuntimeReceiptAction =
|
|
|
48
48
|
type RuntimeReceiptContext = {
|
|
49
49
|
baseUrl: string;
|
|
50
50
|
executorToken: string;
|
|
51
|
+
orgId?: string | null;
|
|
51
52
|
playName: string;
|
|
52
53
|
runId: string;
|
|
53
54
|
key: string;
|
|
@@ -59,11 +60,11 @@ type RuntimeReceiptContext = {
|
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
function scopedReceiptKey(input: {
|
|
63
|
+
orgId?: string | null;
|
|
62
64
|
playName: string;
|
|
63
|
-
runId: string;
|
|
64
65
|
key: string;
|
|
65
66
|
}): string {
|
|
66
|
-
return `
|
|
67
|
+
return `ctx:${input.orgId?.trim() || 'org'}:${input.playName.trim() || 'play'}:${input.key}`;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
function receiptOutput<T>(receipt: WorkerRuntimeReceipt): T {
|
|
@@ -74,13 +75,21 @@ function errorMessage(error: unknown): string {
|
|
|
74
75
|
return error instanceof Error ? error.message : String(error);
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
function runningReceiptError(key: string, receipt: WorkerRuntimeReceipt): Error {
|
|
79
|
+
return new Error(
|
|
80
|
+
`Runtime receipt ${key} is already running for run ${receipt.runId ?? 'unknown'}.`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
78
85
|
input: RuntimeReceiptContext & {
|
|
79
86
|
execute: () => Promise<T> | T;
|
|
80
87
|
},
|
|
81
88
|
): Promise<T> {
|
|
82
89
|
const key = scopedReceiptKey(input);
|
|
83
|
-
const
|
|
90
|
+
const postRuntimeReceiptAction = (body: WorkerRuntimeReceiptAction) =>
|
|
91
|
+
input.postRuntimeApi(input.baseUrl, input.executorToken, body);
|
|
92
|
+
const existing = await postRuntimeReceiptAction({
|
|
84
93
|
action: 'get_runtime_step_receipt',
|
|
85
94
|
playName: input.playName,
|
|
86
95
|
runId: input.runId,
|
|
@@ -93,46 +102,67 @@ export async function runWorkerRuntimeReceiptBoundary<T>(
|
|
|
93
102
|
return receiptOutput<T>(existing.receipt);
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
const claimed = await
|
|
105
|
+
const claimed = await postRuntimeReceiptAction({
|
|
97
106
|
action: 'claim_runtime_step_receipt',
|
|
98
107
|
playName: input.playName,
|
|
99
108
|
runId: input.runId,
|
|
100
109
|
key,
|
|
101
110
|
});
|
|
102
111
|
if (!claimed.receipt) {
|
|
112
|
+
const latest = await postRuntimeReceiptAction({
|
|
113
|
+
action: 'get_runtime_step_receipt',
|
|
114
|
+
playName: input.playName,
|
|
115
|
+
runId: input.runId,
|
|
116
|
+
key,
|
|
117
|
+
});
|
|
103
118
|
if (
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
latest.receipt?.status === 'completed' ||
|
|
120
|
+
latest.receipt?.status === 'skipped'
|
|
106
121
|
) {
|
|
107
|
-
|
|
108
|
-
`Runtime receipt ${key} is already running for run ${existing.receipt.runId ?? 'unknown'}.`,
|
|
109
|
-
);
|
|
122
|
+
return receiptOutput<T>(latest.receipt);
|
|
110
123
|
}
|
|
111
|
-
if (
|
|
124
|
+
if (latest.receipt?.status === 'running') {
|
|
125
|
+
throw runningReceiptError(key, latest.receipt);
|
|
126
|
+
}
|
|
127
|
+
if (latest.receipt?.status === 'failed') {
|
|
112
128
|
throw new Error(
|
|
113
|
-
`Runtime receipt ${key} is failed and could not be claimed: ${
|
|
129
|
+
`Runtime receipt ${key} is failed and could not be claimed: ${latest.receipt.error ?? 'unknown error'}`,
|
|
114
130
|
);
|
|
115
131
|
}
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Runtime receipt ${key} claim did not return execution ownership.`,
|
|
134
|
+
);
|
|
116
135
|
}
|
|
117
136
|
|
|
137
|
+
let output: T;
|
|
118
138
|
try {
|
|
119
|
-
|
|
120
|
-
await input.postRuntimeApi(input.baseUrl, input.executorToken, {
|
|
121
|
-
action: 'complete_runtime_step_receipt',
|
|
122
|
-
playName: input.playName,
|
|
123
|
-
runId: input.runId,
|
|
124
|
-
key,
|
|
125
|
-
output,
|
|
126
|
-
});
|
|
127
|
-
return output;
|
|
139
|
+
output = await input.execute();
|
|
128
140
|
} catch (error) {
|
|
129
|
-
await
|
|
141
|
+
const failed = await postRuntimeReceiptAction({
|
|
130
142
|
action: 'fail_runtime_step_receipt',
|
|
131
143
|
playName: input.playName,
|
|
132
144
|
runId: input.runId,
|
|
133
145
|
key,
|
|
134
146
|
error: errorMessage(error),
|
|
135
147
|
});
|
|
148
|
+
if (!failed.receipt) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Runtime receipt ${key} execution failed and failed receipt could not be persisted: ${errorMessage(error)}`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
136
153
|
throw error;
|
|
137
154
|
}
|
|
155
|
+
const completed = await postRuntimeReceiptAction({
|
|
156
|
+
action: 'complete_runtime_step_receipt',
|
|
157
|
+
playName: input.playName,
|
|
158
|
+
runId: input.runId,
|
|
159
|
+
key,
|
|
160
|
+
output,
|
|
161
|
+
});
|
|
162
|
+
if (!completed.receipt) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Runtime receipt ${key} execution completed but completed receipt could not be persisted.`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return output;
|
|
138
168
|
}
|
|
@@ -166,6 +166,7 @@ export type ToolExecutionRequest = {
|
|
|
166
166
|
tool: string;
|
|
167
167
|
input: Record<string, unknown>;
|
|
168
168
|
description?: string;
|
|
169
|
+
staleAfterSeconds?: number;
|
|
169
170
|
};
|
|
170
171
|
|
|
171
172
|
export type StepResolver<Row, Value> = (
|
|
@@ -297,7 +298,10 @@ export type CsvOptions = {
|
|
|
297
298
|
* const enriched = await ctx
|
|
298
299
|
* .map('companies', [{ domain: 'a.com' }, { domain: 'b.com' }])
|
|
299
300
|
* .step('company', (row, rowCtx) =>
|
|
300
|
-
* rowCtx.
|
|
301
|
+
* rowCtx.tools.execute({
|
|
302
|
+
* id: 'company_search',
|
|
303
|
+
* tool: 'test_company_search',
|
|
304
|
+
* input: { domain: row.domain },
|
|
301
305
|
* description: 'Look up company details by domain.',
|
|
302
306
|
* }))
|
|
303
307
|
* .run({ description: 'Look up company details.' });
|
|
@@ -362,30 +366,30 @@ export interface DeeplinePlayRuntimeContext {
|
|
|
362
366
|
*
|
|
363
367
|
* @example Single tool per row
|
|
364
368
|
* ```typescript
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
* const results = await ctx
|
|
370
|
+
* .map('companies', leads)
|
|
371
|
+
* .step('company', (row, ctx) =>
|
|
372
|
+
* ctx.tools.execute({
|
|
373
|
+
* id: 'company_search',
|
|
374
|
+
* tool: 'test_company_search',
|
|
375
|
+
* input: { domain: row.domain },
|
|
376
|
+
* description: 'Look up company details by domain.',
|
|
377
|
+
* }))
|
|
374
378
|
* .run({ description: 'Look up companies.' });
|
|
375
379
|
* // [{ domain: 'stripe.com', company: { name: 'Stripe', ... } }, ...]
|
|
376
380
|
* ```
|
|
377
381
|
*
|
|
378
382
|
* @example Multiple columns with pre/post logic
|
|
379
383
|
* ```typescript
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
384
|
+
* const results = await ctx
|
|
385
|
+
* .map('leads', leads)
|
|
386
|
+
* .step('company', (row, ctx) =>
|
|
387
|
+
* ctx.tools.execute({
|
|
388
|
+
* id: 'company_search',
|
|
389
|
+
* tool: 'test_company_search',
|
|
390
|
+
* input: { domain: row.domain },
|
|
391
|
+
* description: 'Look up company details by domain.',
|
|
392
|
+
* }))
|
|
389
393
|
* .step('score', (row) =>
|
|
390
394
|
* row.company?.employeeCount > 100 ? 'enterprise' : 'smb')
|
|
391
395
|
* .run({ description: 'Enrich leads.' });
|
|
@@ -407,31 +411,24 @@ export interface DeeplinePlayRuntimeContext {
|
|
|
407
411
|
* @returns The tool's output
|
|
408
412
|
*/
|
|
409
413
|
execute<TOutput = LoosePlayObject>(
|
|
410
|
-
request: ToolExecutionRequest,
|
|
414
|
+
request: ToolExecutionRequest & { staleAfterSeconds?: number },
|
|
411
415
|
): Promise<ToolExecuteResult<TOutput>>;
|
|
412
416
|
};
|
|
413
|
-
/**
|
|
414
|
-
* Execute a single tool by stable step key and tool ID.
|
|
415
|
-
*
|
|
416
|
-
* Shorthand for `ctx.tools.execute(...)`; this is the preferred spelling in
|
|
417
|
-
* row-level step programs.
|
|
418
|
-
*/
|
|
419
|
-
tool<TOutput = LoosePlayObject>(
|
|
420
|
-
key: string,
|
|
421
|
-
toolId: string,
|
|
422
|
-
input: Record<string, unknown>,
|
|
423
|
-
options?: { description?: string },
|
|
424
|
-
): Promise<ToolExecuteResult<TOutput>>;
|
|
425
417
|
runSteps<TInput extends Record<string, unknown>, TOutput>(
|
|
426
418
|
program: StepProgram<TInput, any, TOutput>,
|
|
427
419
|
input: TInput,
|
|
428
420
|
options?: { description?: string },
|
|
429
421
|
): Promise<TOutput>;
|
|
430
|
-
step<T>(
|
|
422
|
+
step<T>(
|
|
423
|
+
id: string,
|
|
424
|
+
run: () => T | Promise<T>,
|
|
425
|
+
options?: { staleAfterSeconds?: number },
|
|
426
|
+
): Promise<T>;
|
|
431
427
|
fetch(
|
|
432
428
|
key: string,
|
|
433
429
|
url: string | URL,
|
|
434
430
|
init?: RequestInit,
|
|
431
|
+
options?: { staleAfterSeconds?: number },
|
|
435
432
|
): Promise<{
|
|
436
433
|
ok: boolean;
|
|
437
434
|
status: number;
|
|
@@ -445,7 +442,7 @@ export interface DeeplinePlayRuntimeContext {
|
|
|
445
442
|
key: string,
|
|
446
443
|
playRef: string | PlayReferenceLike,
|
|
447
444
|
input: Record<string, unknown>,
|
|
448
|
-
options: { description?: string },
|
|
445
|
+
options: { description?: string; staleAfterSeconds?: number },
|
|
449
446
|
): Promise<Record<string, unknown>>;
|
|
450
447
|
|
|
451
448
|
/**
|
|
@@ -944,11 +941,9 @@ export class DeeplineContext {
|
|
|
944
941
|
toolId: string,
|
|
945
942
|
input: Record<string, unknown>,
|
|
946
943
|
): Promise<ToolExecuteResult> => {
|
|
947
|
-
const response = await this.client.executeTool(
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
{ includeToolMetadata: true },
|
|
951
|
-
);
|
|
944
|
+
const response = await this.client.executeTool(toolId, input, {
|
|
945
|
+
includeToolMetadata: true,
|
|
946
|
+
});
|
|
952
947
|
return toolExecutionEnvelopeToResult(toolId, response);
|
|
953
948
|
},
|
|
954
949
|
};
|
|
@@ -1150,9 +1145,7 @@ function toolExecutionEnvelopeToResult(
|
|
|
1150
1145
|
? toolMetadata.toolId
|
|
1151
1146
|
: fallbackToolId,
|
|
1152
1147
|
extractors: extractorDescriptorRecord(toolMetadata.extractors),
|
|
1153
|
-
targetGetters: stringArrayRecord(
|
|
1154
|
-
toolMetadata.targetGetters,
|
|
1155
|
-
),
|
|
1148
|
+
targetGetters: stringArrayRecord(toolMetadata.targetGetters),
|
|
1156
1149
|
listExtractorPaths: stringArray(toolMetadata.listExtractorPaths),
|
|
1157
1150
|
listIdentityGetters: stringArrayRecord(toolMetadata.listIdentityGetters),
|
|
1158
1151
|
},
|
|
@@ -53,7 +53,7 @@ export type HarnessBinding = PlayHarnessRpc;
|
|
|
53
53
|
*
|
|
54
54
|
* Why a module-level holder instead of threading the binding through
|
|
55
55
|
* every call site: most leaf-call sites are deep inside SDK utilities
|
|
56
|
-
* (`ctx.
|
|
56
|
+
* (`ctx.tools.execute`, `ctx.csv`, etc.) and threading a binding everywhere would
|
|
57
57
|
* be a structural mess. A single set-once holder, set very early in the
|
|
58
58
|
* worker's lifecycle, gives us clean call sites without sacrificing
|
|
59
59
|
* isolation — each per-play Worker is its own isolate, so the holder is
|
|
@@ -69,6 +69,23 @@ export interface CustomerDbQueryResult {
|
|
|
69
69
|
rows: unknown[];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
export interface ToolPricingSummary {
|
|
73
|
+
/** User-facing pricing text for quick discovery displays. */
|
|
74
|
+
displayText: string | null;
|
|
75
|
+
/** User-facing unit the price applies to. */
|
|
76
|
+
unit: 'call' | 'result' | 'page' | 'usage' | null;
|
|
77
|
+
/** Deepline credits charged per unit, when statically knowable. */
|
|
78
|
+
creditsPerUnit: number | null;
|
|
79
|
+
/** USD equivalent charged per unit, when statically knowable. */
|
|
80
|
+
usdPerUnit: number | null;
|
|
81
|
+
/** Currency for `usdPerUnit`. */
|
|
82
|
+
currency: 'USD';
|
|
83
|
+
/** Short user-facing pricing summary for variable pricing. */
|
|
84
|
+
summary: string | null;
|
|
85
|
+
/** Additional user-facing pricing notes. */
|
|
86
|
+
details: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
72
89
|
/**
|
|
73
90
|
* Summary definition of a tool, returned by {@link DeeplineClient.listTools}.
|
|
74
91
|
*
|
|
@@ -103,6 +120,8 @@ export interface ToolDefinition {
|
|
|
103
120
|
inputSchema?: Record<string, unknown>;
|
|
104
121
|
/** JSON Schema describing the tool's output shape. */
|
|
105
122
|
outputSchema?: Record<string, unknown>;
|
|
123
|
+
/** User-facing pricing summary. Internal provider/settlement costs are intentionally omitted. */
|
|
124
|
+
pricing?: ToolPricingSummary | null;
|
|
106
125
|
/** Copyable play-runtime guidance for V2 tool execution results. */
|
|
107
126
|
usageGuidance?: {
|
|
108
127
|
execute?: string;
|
|
@@ -177,6 +196,10 @@ export interface ToolSearchResult {
|
|
|
177
196
|
search_mode?: 'v1' | 'v2';
|
|
178
197
|
search_fallback_to_category?: boolean;
|
|
179
198
|
omitted_plays_hint?: string;
|
|
199
|
+
commandTemplates?: {
|
|
200
|
+
describe?: string;
|
|
201
|
+
execute?: string;
|
|
202
|
+
};
|
|
180
203
|
render?: {
|
|
181
204
|
sections?: Array<{
|
|
182
205
|
title: string;
|
|
@@ -208,11 +231,11 @@ export interface ToolMetadata extends ToolDefinition {
|
|
|
208
231
|
asyncGetAction?: string | null;
|
|
209
232
|
/** Known failure scenarios and their expected handling. */
|
|
210
233
|
failureMode?: Record<string, unknown> | null;
|
|
211
|
-
/**
|
|
234
|
+
/** Sanitized pricing model for describe metadata. Provider internal prices are intentionally omitted. */
|
|
212
235
|
cost?: Record<string, unknown> | null;
|
|
213
|
-
/**
|
|
236
|
+
/** Deepline credits charged for one user-visible pricing unit. */
|
|
214
237
|
deeplineCreditsPerPricingUnit?: number | null;
|
|
215
|
-
/** Whether
|
|
238
|
+
/** Whether the customer pays with Deepline credits or their own connected account. */
|
|
216
239
|
billingSource?: string;
|
|
217
240
|
/** Display label for the billing source. */
|
|
218
241
|
billingSourceLabel?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.1.
|
|
1
|
+
export const SDK_VERSION = "0.1.48";
|
|
2
2
|
export const SDK_API_CONTRACT = "2026-05-stripe-promo-checkout";
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import type {
|
|
12
12
|
ConditionalStepResolver,
|
|
13
13
|
DefinePlayConfig,
|
|
14
|
-
DeeplineNamedPlay,
|
|
15
14
|
DeeplinePlayRuntimeContext,
|
|
16
15
|
DefinedPlay,
|
|
17
16
|
PlayBindings,
|
|
@@ -57,9 +56,11 @@ type PlayMetadata = {
|
|
|
57
56
|
billing?: PlayBindings['billing'];
|
|
58
57
|
};
|
|
59
58
|
|
|
60
|
-
class WorkerConditionalStepResolver<
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
class WorkerConditionalStepResolver<
|
|
60
|
+
Row,
|
|
61
|
+
Value,
|
|
62
|
+
ElseValue,
|
|
63
|
+
> implements ConditionalStepResolver<Row, Value, ElseValue> {
|
|
63
64
|
readonly kind = 'conditional' as const;
|
|
64
65
|
|
|
65
66
|
constructor(
|
|
@@ -75,9 +76,11 @@ class WorkerConditionalStepResolver<Row, Value, ElseValue>
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
class WorkerStepProgram<Input, Output, ReturnValue>
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
class WorkerStepProgram<Input, Output, ReturnValue> implements StepProgram<
|
|
80
|
+
Input,
|
|
81
|
+
Output,
|
|
82
|
+
ReturnValue
|
|
83
|
+
> {
|
|
81
84
|
readonly kind = 'steps' as const;
|
|
82
85
|
declare readonly __inputType?: (input: Input) => void;
|
|
83
86
|
|
|
@@ -94,7 +97,7 @@ class WorkerStepProgram<Input, Output, ReturnValue>
|
|
|
94
97
|
| StepProgramResolver<Output, Value>,
|
|
95
98
|
): StepProgram<Input, Output & Record<Name, Value>, ReturnValue> {
|
|
96
99
|
if (!name.trim()) {
|
|
97
|
-
throw new Error('
|
|
100
|
+
throw new Error('Step name required.');
|
|
98
101
|
}
|
|
99
102
|
return new WorkerStepProgram(
|
|
100
103
|
[
|
|
@@ -118,26 +121,6 @@ class WorkerStepProgram<Input, Output, ReturnValue>
|
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
function createUnavailableNamedPlayHandle<TInput, TOutput extends PlayReturnObject>(
|
|
122
|
-
name: string,
|
|
123
|
-
): DeeplineNamedPlay<TInput, TOutput> {
|
|
124
|
-
const unavailable = () => {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`definePlay("${name}") remote lifecycle methods are unavailable inside workers_edge play execution. Use ctx.runPlay(...) for runtime composition.`,
|
|
127
|
-
);
|
|
128
|
-
};
|
|
129
|
-
return {
|
|
130
|
-
name,
|
|
131
|
-
get: unavailable,
|
|
132
|
-
runs: unavailable,
|
|
133
|
-
versions: unavailable,
|
|
134
|
-
publish: unavailable,
|
|
135
|
-
clearHistory: unavailable,
|
|
136
|
-
run: unavailable,
|
|
137
|
-
runSync: unavailable,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
124
|
export function steps<TInput>(): StepProgram<TInput, TInput, TInput> {
|
|
142
125
|
return new WorkerStepProgram<TInput, TInput, TInput>([]);
|
|
143
126
|
}
|
|
@@ -153,9 +136,7 @@ export function defineInput<TInput>(
|
|
|
153
136
|
schema: Record<string, unknown>,
|
|
154
137
|
): PlayInputContract<TInput> {
|
|
155
138
|
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
156
|
-
throw new Error(
|
|
157
|
-
'defineInput<T>(schema) requires a JSON-schema-like object.',
|
|
158
|
-
);
|
|
139
|
+
throw new Error('defineInput schema must be an object');
|
|
159
140
|
}
|
|
160
141
|
return { schema };
|
|
161
142
|
}
|
|
@@ -196,12 +177,10 @@ export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
|
196
177
|
const name = config.name;
|
|
197
178
|
const fn = config.fn;
|
|
198
179
|
if (typeof fn !== 'function') {
|
|
199
|
-
throw new Error('definePlay
|
|
180
|
+
throw new Error('definePlay run must be async');
|
|
200
181
|
}
|
|
201
182
|
if (name.includes('/')) {
|
|
202
|
-
throw new Error(
|
|
203
|
-
'definePlay(name, ...) play names cannot contain "/". Slash is reserved for qualified references like "prebuilt/example" or "self/example".',
|
|
204
|
-
);
|
|
183
|
+
throw new Error('Play name cannot contain /');
|
|
205
184
|
}
|
|
206
185
|
const normalizedName = name
|
|
207
186
|
.trim()
|
|
@@ -210,14 +189,10 @@ export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
|
210
189
|
.replace(/^_+|_+$/g, '')
|
|
211
190
|
.toLowerCase();
|
|
212
191
|
if (!normalizedName) {
|
|
213
|
-
throw new Error(
|
|
214
|
-
'definePlay(name, ...) requires a play name with at least one letter or number. Use only letters, numbers, underscores, or hyphens.',
|
|
215
|
-
);
|
|
192
|
+
throw new Error('Play name required');
|
|
216
193
|
}
|
|
217
194
|
if (normalizedName.length > 63) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`definePlay("${name}", ...) is too long after normalization (${normalizedName.length}/63). Shorten the play name to 63 characters or fewer. Normalized value: "${normalizedName}".`,
|
|
220
|
-
);
|
|
195
|
+
throw new Error('Play name >63');
|
|
221
196
|
}
|
|
222
197
|
|
|
223
198
|
const metadata: PlayMetadata = {
|
|
@@ -230,42 +205,17 @@ export function definePlay<TInput, TOutput extends PlayReturnObject>(
|
|
|
230
205
|
|
|
231
206
|
Object.defineProperty(play, PLAY_METADATA_SYMBOL, {
|
|
232
207
|
value: metadata,
|
|
233
|
-
enumerable: false,
|
|
234
|
-
configurable: false,
|
|
235
|
-
writable: false,
|
|
236
208
|
});
|
|
237
209
|
Object.defineProperty(play, 'playName', {
|
|
238
210
|
value: name,
|
|
239
211
|
enumerable: true,
|
|
240
|
-
configurable: false,
|
|
241
|
-
writable: false,
|
|
242
212
|
});
|
|
243
213
|
Object.defineProperty(play, 'bindings', {
|
|
244
214
|
value: config.bindings,
|
|
245
215
|
enumerable: true,
|
|
246
|
-
configurable: false,
|
|
247
|
-
writable: false,
|
|
248
216
|
});
|
|
249
217
|
|
|
250
|
-
|
|
251
|
-
for (const key of [
|
|
252
|
-
'name',
|
|
253
|
-
'get',
|
|
254
|
-
'runs',
|
|
255
|
-
'versions',
|
|
256
|
-
'publish',
|
|
257
|
-
'clearHistory',
|
|
258
|
-
'run',
|
|
259
|
-
'runSync',
|
|
260
|
-
] as const) {
|
|
261
|
-
Object.defineProperty(play, key, {
|
|
262
|
-
value: handle[key],
|
|
263
|
-
enumerable: false,
|
|
264
|
-
configurable: false,
|
|
265
|
-
writable: false,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
218
|
+
Object.defineProperty(play, 'name', { value: name });
|
|
269
219
|
return play;
|
|
270
220
|
}
|
|
271
221
|
|
|
@@ -38,7 +38,7 @@ function stableValue(value: unknown): unknown {
|
|
|
38
38
|
return value;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function stableStringify(value: unknown): string {
|
|
41
|
+
export function stableStringify(value: unknown): string {
|
|
42
42
|
return JSON.stringify(stableValue(value));
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ function rightRotate32(value: number, bits: number): number {
|
|
|
46
46
|
return (value >>> bits) | (value << (32 - bits));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function sha256Hex(input: string): string {
|
|
49
|
+
export function sha256Hex(input: string): string {
|
|
50
50
|
const bytes = Array.from(new TextEncoder().encode(input));
|
|
51
51
|
const bitLength = bytes.length * 8;
|
|
52
52
|
bytes.push(0x80);
|
|
@@ -232,20 +232,6 @@ export function resolveStaleMapTableNamespace(
|
|
|
232
232
|
return `${prefix}_${digest}`;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
export function maxArtifactTableNamespaceLength(
|
|
236
|
-
playName?: string | null,
|
|
237
|
-
): number {
|
|
238
|
-
if (!playName?.trim()) {
|
|
239
|
-
return POSTGRES_IDENTIFIER_MAX_LENGTH;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const normalizedPlayName = normalizePlayNameForSheet(playName);
|
|
243
|
-
return Math.max(
|
|
244
|
-
1,
|
|
245
|
-
POSTGRES_IDENTIFIER_MAX_LENGTH - normalizedPlayName.length - 1,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
235
|
export function validatePlaySheetTableName(
|
|
250
236
|
playName: string,
|
|
251
237
|
tableNamespace: string,
|
|
@@ -263,71 +249,37 @@ export function validatePlaySheetTableName(
|
|
|
263
249
|
return resolved;
|
|
264
250
|
}
|
|
265
251
|
|
|
266
|
-
export function resolvePlayRunTableNamespace(
|
|
267
|
-
tableNamespace: string,
|
|
268
|
-
_input?: Record<string, unknown> | null,
|
|
269
|
-
_options?: {
|
|
270
|
-
playName?: string | null;
|
|
271
|
-
},
|
|
272
|
-
): string {
|
|
273
|
-
return normalizeTableNamespace(tableNamespace);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
252
|
export function derivePlayRowIdentity(
|
|
277
253
|
row: Record<string, unknown>,
|
|
278
254
|
tableNamespace: string,
|
|
279
|
-
logicFingerprint?: string | null,
|
|
280
255
|
): string {
|
|
281
256
|
return deriveDerivedOutputIdentity({
|
|
282
257
|
inputItem: row,
|
|
283
258
|
operationNamespace: tableNamespace,
|
|
284
|
-
logicFingerprint,
|
|
285
259
|
});
|
|
286
260
|
}
|
|
287
261
|
|
|
288
262
|
export function deriveDerivedOutputIdentity(input: {
|
|
289
263
|
inputItem: Record<string, unknown>;
|
|
290
264
|
operationNamespace: string;
|
|
291
|
-
logicFingerprint?: string | null;
|
|
292
265
|
}): string {
|
|
293
|
-
const normalizedNamespace = normalizeTableNamespace(
|
|
294
|
-
input.operationNamespace,
|
|
295
|
-
);
|
|
266
|
+
const normalizedNamespace = normalizeTableNamespace(input.operationNamespace);
|
|
296
267
|
const canonicalRow = stableStringify(input.inputItem);
|
|
297
|
-
const
|
|
298
|
-
? `\nlogic:${input.logicFingerprint.trim()}`
|
|
299
|
-
: '';
|
|
300
|
-
const digest = sha256Hex(
|
|
301
|
-
`${normalizedNamespace}${fingerprint}\n${canonicalRow}`,
|
|
302
|
-
);
|
|
268
|
+
const digest = sha256Hex(`${normalizedNamespace}\n${canonicalRow}`);
|
|
303
269
|
return `${normalizedNamespace}:${digest}`;
|
|
304
270
|
}
|
|
305
271
|
|
|
306
272
|
export function deriveToolRequestIdentity(input: {
|
|
307
273
|
toolId: string;
|
|
308
274
|
requestInput: Record<string, unknown>;
|
|
309
|
-
effectiveAccountContext?: string | null;
|
|
310
|
-
toolContractRevision?: string | number | null;
|
|
311
|
-
reuseSafetyPolicy?: string | null;
|
|
312
275
|
}): string {
|
|
313
276
|
const toolId = input.toolId.trim();
|
|
314
277
|
if (!toolId) {
|
|
315
278
|
throw new Error('Tool request identity requires a non-empty tool id.');
|
|
316
279
|
}
|
|
317
|
-
const accountContext =
|
|
318
|
-
input.effectiveAccountContext?.trim() || 'default_account_context';
|
|
319
|
-
const contractRevision =
|
|
320
|
-
input.toolContractRevision == null
|
|
321
|
-
? 'default_tool_contract'
|
|
322
|
-
: String(input.toolContractRevision).trim() || 'default_tool_contract';
|
|
323
|
-
const reuseSafetyPolicy =
|
|
324
|
-
input.reuseSafetyPolicy?.trim() || 'default_reuse_policy';
|
|
325
280
|
const digest = sha256Hex(
|
|
326
281
|
stableStringify({
|
|
327
|
-
accountContext,
|
|
328
282
|
requestInput: input.requestInput,
|
|
329
|
-
reuseSafetyPolicy,
|
|
330
|
-
toolContractRevision: contractRevision,
|
|
331
283
|
toolId,
|
|
332
284
|
}),
|
|
333
285
|
);
|
|
@@ -344,14 +296,8 @@ export function deriveToolRequestIdentity(input: {
|
|
|
344
296
|
export function derivePlayRowIdentityFromKey(
|
|
345
297
|
key: string,
|
|
346
298
|
tableNamespace: string,
|
|
347
|
-
logicFingerprint?: string | null,
|
|
348
299
|
): string {
|
|
349
300
|
const normalizedNamespace = normalizeTableNamespace(tableNamespace);
|
|
350
|
-
const
|
|
351
|
-
? `\nlogic:${logicFingerprint.trim()}`
|
|
352
|
-
: '';
|
|
353
|
-
const digest = sha256Hex(`${normalizedNamespace}${fingerprint}\nkey:${key}`);
|
|
301
|
+
const digest = sha256Hex(`${normalizedNamespace}\nkey:${key}`);
|
|
354
302
|
return `${normalizedNamespace}:${digest}`;
|
|
355
303
|
}
|
|
356
|
-
|
|
357
|
-
export const normalizeMapKeyNamespace = normalizeTableNamespace;
|