@relayflows/sdk 2.0.10 → 2.0.12
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/adapters/base.d.ts +6 -0
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +1 -0
- package/dist/adapters/claude.js.map +1 -1
- package/dist/agent-relay-receipt.d.ts +24 -0
- package/dist/agent-relay-receipt.d.ts.map +1 -0
- package/dist/agent-relay-receipt.js +66 -0
- package/dist/agent-relay-receipt.js.map +1 -0
- package/dist/agent-relay-state.d.ts +18 -0
- package/dist/agent-relay-state.d.ts.map +1 -0
- package/dist/agent-relay-state.js +58 -0
- package/dist/agent-relay-state.js.map +1 -0
- package/dist/agent-relay-transport.d.ts +17 -70
- package/dist/agent-relay-transport.d.ts.map +1 -1
- package/dist/agent-relay-transport.js +177 -112
- package/dist/agent-relay-transport.js.map +1 -1
- package/dist/authored-admission.d.ts +5 -0
- package/dist/authored-admission.d.ts.map +1 -0
- package/dist/authored-admission.js +14 -0
- package/dist/authored-admission.js.map +1 -0
- package/dist/authored-budget.d.ts +1 -1
- package/dist/authored-budget.d.ts.map +1 -1
- package/dist/authored-budget.js +3 -3
- package/dist/authored-budget.js.map +1 -1
- package/dist/authored-flow-error.d.ts +1 -1
- package/dist/authored-flow-error.d.ts.map +1 -1
- package/dist/authored-flow-error.js.map +1 -1
- package/dist/authored-flow-executor.d.ts +10 -0
- package/dist/authored-flow-executor.d.ts.map +1 -1
- package/dist/authored-flow-executor.js +10 -7
- package/dist/authored-flow-executor.js.map +1 -1
- package/dist/authored-flow-loader.d.ts +20 -0
- package/dist/authored-flow-loader.d.ts.map +1 -1
- package/dist/authored-flow-loader.js +86 -11
- package/dist/authored-flow-loader.js.map +1 -1
- package/dist/authored-helper-effect.d.ts +1 -1
- package/dist/authored-helper-effect.d.ts.map +1 -1
- package/dist/authored-helper-effect.js +5 -3
- package/dist/authored-helper-effect.js.map +1 -1
- package/dist/authored-mcp.d.ts +1 -1
- package/dist/authored-mcp.d.ts.map +1 -1
- package/dist/authored-mcp.js +7 -4
- package/dist/authored-mcp.js.map +1 -1
- package/dist/authored-node-entry.d.ts +2 -0
- package/dist/authored-node-entry.d.ts.map +1 -0
- package/dist/authored-node-entry.js +111 -0
- package/dist/authored-node-entry.js.map +1 -0
- package/dist/authored-node-runner.d.ts +11 -0
- package/dist/authored-node-runner.d.ts.map +1 -0
- package/dist/authored-node-runner.js +246 -0
- package/dist/authored-node-runner.js.map +1 -0
- package/dist/authored-plugin-effect.d.ts +1 -1
- package/dist/authored-plugin-effect.d.ts.map +1 -1
- package/dist/authored-plugin-effect.js +7 -4
- package/dist/authored-plugin-effect.js.map +1 -1
- package/dist/authored-root.d.ts +38 -0
- package/dist/authored-root.d.ts.map +1 -0
- package/dist/authored-root.js +329 -0
- package/dist/authored-root.js.map +1 -0
- package/dist/authored-runtime-capability.d.ts +6 -0
- package/dist/authored-runtime-capability.d.ts.map +1 -0
- package/dist/authored-runtime-capability.js +40 -0
- package/dist/authored-runtime-capability.js.map +1 -0
- package/dist/authored-source-authority.d.ts +4 -0
- package/dist/authored-source-authority.d.ts.map +1 -0
- package/dist/authored-source-authority.js +61 -0
- package/dist/authored-source-authority.js.map +1 -0
- package/dist/authored-worker-step.d.ts +2 -2
- package/dist/authored-worker-step.d.ts.map +1 -1
- package/dist/authored-worker-step.js +11 -6
- package/dist/authored-worker-step.js.map +1 -1
- package/dist/budget-preflight.d.ts +17 -3
- package/dist/budget-preflight.d.ts.map +1 -1
- package/dist/budget-preflight.js +31 -24
- package/dist/budget-preflight.js.map +1 -1
- package/dist/cli/check.js +3 -1
- package/dist/cli/check.js.map +1 -1
- package/dist/cli/direct-run.d.ts.map +1 -1
- package/dist/cli/direct-run.js +18 -14
- package/dist/cli/direct-run.js.map +1 -1
- package/dist/cli/run.d.ts +2 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +56 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/cli-adapter.d.ts +16 -0
- package/dist/cli-adapter.d.ts.map +1 -1
- package/dist/cli-adapter.js +17 -0
- package/dist/cli-adapter.js.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/cloud-run.d.ts +11 -1
- package/dist/cloud-run.d.ts.map +1 -1
- package/dist/cloud-run.js +65 -19
- package/dist/cloud-run.js.map +1 -1
- package/dist/failure-kinds.d.ts +7 -3
- package/dist/failure-kinds.d.ts.map +1 -1
- package/dist/failure-kinds.js +5 -1
- package/dist/failure-kinds.js.map +1 -1
- package/dist/journal-client.d.ts +1 -1
- package/dist/journal-client.d.ts.map +1 -1
- package/dist/journal-client.js +6 -2
- package/dist/journal-client.js.map +1 -1
- package/dist/llm-worker.d.ts.map +1 -1
- package/dist/llm-worker.js +4 -2
- package/dist/llm-worker.js.map +1 -1
- package/dist/local-agent.d.ts +1 -1
- package/dist/local-agent.d.ts.map +1 -1
- package/dist/local-agent.js +2 -2
- package/dist/local-agent.js.map +1 -1
- package/dist/model-pricing.d.ts +3 -4
- package/dist/model-pricing.d.ts.map +1 -1
- package/dist/model-pricing.js +4 -4
- package/dist/model-pricing.js.map +1 -1
- package/dist/preflight.d.ts +4 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +36 -18
- package/dist/preflight.js.map +1 -1
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/spec.d.ts +5 -4
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/worker-cli.d.ts +5 -2
- package/dist/worker-cli.d.ts.map +1 -1
- package/dist/worker-cli.js +43 -46
- package/dist/worker-cli.js.map +1 -1
- package/dist/worker-spend.d.ts +4 -3
- package/dist/worker-spend.d.ts.map +1 -1
- package/dist/worker-spend.js +13 -4
- package/dist/worker-spend.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +12 -4
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/base.ts +7 -0
- package/src/adapters/claude.ts +1 -0
- package/src/agent-relay-receipt.ts +120 -0
- package/src/agent-relay-state.ts +73 -0
- package/src/agent-relay-transport.ts +242 -156
- package/src/authored-admission.ts +17 -0
- package/src/authored-budget.ts +4 -3
- package/src/authored-flow-error.ts +0 -1
- package/src/authored-flow-executor.ts +27 -7
- package/src/authored-flow-loader.ts +96 -13
- package/src/authored-helper-effect.ts +5 -3
- package/src/authored-mcp.ts +6 -4
- package/src/authored-node-entry.ts +90 -0
- package/src/authored-node-runner.ts +219 -0
- package/src/authored-plugin-effect.ts +6 -3
- package/src/authored-root.ts +396 -0
- package/src/authored-runtime-capability.ts +40 -0
- package/src/authored-source-authority.ts +62 -0
- package/src/authored-worker-step.ts +15 -4
- package/src/budget-preflight.ts +41 -25
- package/src/cli/check.ts +3 -1
- package/src/cli/direct-run.ts +24 -16
- package/src/cli/run.ts +58 -1
- package/src/cli-adapter.ts +27 -0
- package/src/cli.ts +5 -5
- package/src/cloud-run.ts +74 -21
- package/src/failure-kinds.ts +5 -1
- package/src/journal-client.ts +6 -2
- package/src/llm-worker.ts +4 -2
- package/src/local-agent.ts +7 -2
- package/src/model-pricing.ts +4 -4
- package/src/preflight.ts +40 -18
- package/src/protocol.ts +2 -0
- package/src/spec.ts +5 -4
- package/src/worker-cli.ts +50 -54
- package/src/worker-spend.ts +15 -4
- package/src/worker.ts +12 -4
package/src/preflight.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { TriggerSource } from '@relayflows/surface';
|
|
|
8
8
|
import { acceptsAnyOutput, inspectStepGate, type StepGateInspection } from './gate-contract.js';
|
|
9
9
|
import { compileSpec, CompileError } from './compile.js';
|
|
10
10
|
import { helperCall } from './yaml-helpers.js';
|
|
11
|
+
import { resolveCliModelSelection, type CliModelSource } from './cli-adapter.js';
|
|
11
12
|
import { isNamedGate, NAMED_GATE_FAILURE_KINDS, type NamedGateFailureKind } from './named-gates.js';
|
|
12
13
|
import { compileScopes, type ScopeInput, type MountRegistry } from './scope-compiler.js';
|
|
13
14
|
import { readMountRegistry } from './mount-registry.js';
|
|
@@ -22,8 +23,10 @@ export interface CliResolution {
|
|
|
22
23
|
stepId: string;
|
|
23
24
|
cli: string;
|
|
24
25
|
source: CliResolutionSource;
|
|
25
|
-
/**
|
|
26
|
+
/** Effective model probed with the CLI. */
|
|
26
27
|
model?: string;
|
|
28
|
+
/** Exact source selected by step > named agent > adapter default priority. */
|
|
29
|
+
modelSource?: CliModelSource;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export interface CliProbeResult {
|
|
@@ -208,18 +211,17 @@ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResul
|
|
|
208
211
|
};
|
|
209
212
|
}
|
|
210
213
|
const diagnostics: PreflightDiagnostic[] = [];
|
|
214
|
+
const cliResolutionDiagnostics: PreflightDiagnostic[] = [];
|
|
211
215
|
const resolutions: CliResolution[] = [];
|
|
212
216
|
const resolutionByStep = new Map<string, CliResolution>();
|
|
213
217
|
const cliProbeResults = new Map<string, CliProbeOutcome>();
|
|
214
218
|
|
|
215
|
-
diagnostics.push(...unknownModelDiagnostics(compiled, options));
|
|
216
219
|
diagnostics.push(...scopeDiagnostics(compiled, options));
|
|
217
220
|
for (const server of new Set(options.mcpServers ?? [])) {
|
|
218
221
|
if (options.mcp !== undefined && Object.hasOwn(options.mcp, server)) continue;
|
|
219
222
|
diagnostics.push({ severity: 'refusal', kind: 'mcp_undeclared_server', server,
|
|
220
223
|
message: `MCP server "${server}" is not declared in the nearest flows.json mcp map.` });
|
|
221
224
|
}
|
|
222
|
-
diagnostics.push(...budgetDiagnostics(compiled));
|
|
223
225
|
// Resolve the complete flow before touching any environment fact. A later
|
|
224
226
|
// statically unresolved CLI makes the whole submission impossible, so no
|
|
225
227
|
// earlier command, provider/model, or trigger probe may run first.
|
|
@@ -228,7 +230,7 @@ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResul
|
|
|
228
230
|
if (step.type === 'agent' && helperCall(step) !== undefined) continue;
|
|
229
231
|
const resolution = resolveCli(step, compiled, options.projectCli);
|
|
230
232
|
if (resolution === undefined) {
|
|
231
|
-
|
|
233
|
+
cliResolutionDiagnostics.push({
|
|
232
234
|
severity: 'refusal',
|
|
233
235
|
kind: 'cli_unresolved',
|
|
234
236
|
stepId: step.id,
|
|
@@ -239,7 +241,19 @@ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResul
|
|
|
239
241
|
resolutionByStep.set(step.id, resolution);
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
|
-
|
|
244
|
+
diagnostics.push(...cliResolutionDiagnostics);
|
|
245
|
+
if (cliResolutionDiagnostics.length > 0) {
|
|
246
|
+
return { ok: false, gates: compiled.steps.map(inspectStepGate), resolutions, diagnostics };
|
|
247
|
+
}
|
|
248
|
+
diagnostics.push(...unknownModelDiagnostics(compiled, options, resolutionByStep));
|
|
249
|
+
diagnostics.push(...budgetDiagnostics(
|
|
250
|
+
compiled,
|
|
251
|
+
new Map(resolutions.map(resolution => [resolution.stepId, {
|
|
252
|
+
cli: resolution.cli,
|
|
253
|
+
...(resolution.model === undefined ? {} : { model: resolution.model }),
|
|
254
|
+
}])),
|
|
255
|
+
));
|
|
256
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === 'refusal')) {
|
|
243
257
|
return { ok: false, gates: compiled.steps.map(inspectStepGate), resolutions, diagnostics };
|
|
244
258
|
}
|
|
245
259
|
|
|
@@ -311,6 +325,7 @@ function scopeDiagnostics(flow: FlowSpec, options: PreflightOptions): PreflightR
|
|
|
311
325
|
function unknownModelDiagnostics(
|
|
312
326
|
flow: FlowSpec,
|
|
313
327
|
options: PreflightOptions,
|
|
328
|
+
resolutionByStep: ReadonlyMap<string, CliResolution> = new Map(),
|
|
314
329
|
): PreflightRefusal[] {
|
|
315
330
|
const diagnostics: PreflightRefusal[] = [];
|
|
316
331
|
// model_unknown is a governance check: it exists to enforce a project's
|
|
@@ -340,16 +355,21 @@ function unknownModelDiagnostics(
|
|
|
340
355
|
}
|
|
341
356
|
|
|
342
357
|
for (const step of flow.steps) {
|
|
343
|
-
if (step.type === 'deterministic'
|
|
344
|
-
|
|
345
|
-
|
|
358
|
+
if (step.type === 'deterministic') continue;
|
|
359
|
+
const resolution = resolutionByStep.get(step.id);
|
|
360
|
+
// Selected named declarations were checked once above, including unused
|
|
361
|
+
// declarations. Other sources are step declarations or adapter defaults.
|
|
362
|
+
if (resolution?.modelSource === 'named') continue;
|
|
363
|
+
const model = resolution?.model ?? step.model;
|
|
364
|
+
if (model === undefined || isKnownModel(model, options.models)) continue;
|
|
365
|
+
if (!enforceRegistry) continue;
|
|
346
366
|
diagnostics.push({
|
|
347
367
|
severity: 'refusal',
|
|
348
368
|
kind: 'model_unknown',
|
|
349
369
|
stepId: step.id,
|
|
350
370
|
...(resolution === undefined ? {} : { cli: resolution.cli }),
|
|
351
|
-
model
|
|
352
|
-
message: unknownModelMessage(step.id,
|
|
371
|
+
model,
|
|
372
|
+
message: unknownModelMessage(step.id, model, resolution?.cli, options.modelRegistryPath),
|
|
353
373
|
});
|
|
354
374
|
}
|
|
355
375
|
|
|
@@ -402,14 +422,16 @@ function resolveCli(
|
|
|
402
422
|
const named = step.type === 'agent' && step.agent !== undefined
|
|
403
423
|
? flow.agents?.[step.agent]
|
|
404
424
|
: undefined;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (
|
|
412
|
-
if (
|
|
425
|
+
const resolved = (cli: string, source: CliResolutionSource): CliResolution => {
|
|
426
|
+
const effectiveModel = resolveCliModelSelection(cli, { step: step.model, named: named?.model });
|
|
427
|
+
return { stepId: step.id, cli, source,
|
|
428
|
+
...(effectiveModel.model === undefined ? {} : { model: effectiveModel.model }),
|
|
429
|
+
...(effectiveModel.source === undefined ? {} : { modelSource: effectiveModel.source }) };
|
|
430
|
+
};
|
|
431
|
+
if (step.cli !== undefined) return resolved(step.cli, 'step');
|
|
432
|
+
if (named !== undefined) return resolved(named.cli, 'named');
|
|
433
|
+
if (flow.cli !== undefined) return resolved(flow.cli, 'flow');
|
|
434
|
+
if (projectCli !== undefined) return resolved(projectCli, 'project');
|
|
413
435
|
return undefined;
|
|
414
436
|
}
|
|
415
437
|
|
package/src/protocol.ts
CHANGED
|
@@ -77,6 +77,8 @@ export interface StepSpend {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export interface RunStartParams {
|
|
80
|
+
/** Caller-owned retry identity. Reuse with a different spec is refused. */
|
|
81
|
+
admission_key?: string;
|
|
80
82
|
reuse_from_run_id?: string;
|
|
81
83
|
/**
|
|
82
84
|
* The kernel spec dialect — the ONE boundary shape `RunSpec::parse`
|
package/src/spec.ts
CHANGED
|
@@ -238,10 +238,11 @@ export interface AgentStepSpec extends BaseStepSpec {
|
|
|
238
238
|
/** Inert preflight declaration; overrides the flow/project CLI default. */
|
|
239
239
|
cli?: string;
|
|
240
240
|
/**
|
|
241
|
-
* Model the declared CLI must use.
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
241
|
+
* Model the declared CLI must use. A step declaration wins over its selected
|
|
242
|
+
* named agent and any registered adapter default. Raw Claude/Codex adapters
|
|
243
|
+
* receive the effective model as a real flag; an identified Relayflows
|
|
244
|
+
* wrapper receives an explicitly declared model in its same-process request.
|
|
245
|
+
* The effective choice is journaled instead of being ambient host state.
|
|
245
246
|
*/
|
|
246
247
|
model?: string;
|
|
247
248
|
surfaces?: AgentSurfaces;
|
package/src/worker-cli.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
agentExecution,
|
|
7
7
|
llmExecution,
|
|
8
8
|
cliAdapterKind,
|
|
9
|
+
resolveCliModel,
|
|
9
10
|
type CliInvocation,
|
|
10
11
|
type CliAdapterKind,
|
|
11
12
|
} from './cli-adapter.js';
|
|
@@ -15,8 +16,7 @@ import {
|
|
|
15
16
|
} from './wrapper-session.js';
|
|
16
17
|
import { wrapperEnvironment } from './wrapper-runtime.js';
|
|
17
18
|
import {
|
|
18
|
-
|
|
19
|
-
deriveAgentName,
|
|
19
|
+
runAgentRelayTask,
|
|
20
20
|
AgentRelayTransportError,
|
|
21
21
|
type AgentTransport,
|
|
22
22
|
} from './agent-relay-transport.js';
|
|
@@ -32,6 +32,7 @@ export const WAKE_CONTEXT_ENV = 'RELAYFLOW_WAKE_CONTEXT';
|
|
|
32
32
|
export const MODEL_ENV = 'RELAYFLOW_MODEL';
|
|
33
33
|
|
|
34
34
|
export interface WorkerCliResult {
|
|
35
|
+
relay_task?: import('./agent-relay-receipt.js').RelayTaskReceipt;
|
|
35
36
|
tokens_input?: number;
|
|
36
37
|
tokens_output?: number;
|
|
37
38
|
exit_code: number | null;
|
|
@@ -40,12 +41,14 @@ export interface WorkerCliResult {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
* stable, DM-addressable agent name (flows#385).
|
|
44
|
+
* Journal identity and durable dispatch storage used by the Relay task transport.
|
|
45
45
|
*/
|
|
46
46
|
export interface AgentRelayContext {
|
|
47
47
|
runId: string;
|
|
48
48
|
stepId: string;
|
|
49
|
+
idempotencyKey: string;
|
|
50
|
+
dataDir?: string;
|
|
51
|
+
resultSchema?: unknown;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export async function runAgentCli(
|
|
@@ -66,27 +69,28 @@ export async function runAgentCli(
|
|
|
66
69
|
throw new Error('Lease-bound agent execution requires macOS or Linux process-group cancellation; Windows is unsupported.');
|
|
67
70
|
}
|
|
68
71
|
const kind = cliAdapterKind(cli);
|
|
72
|
+
const effectiveModel = resolveCliModel(cli, model);
|
|
73
|
+
|
|
74
|
+
if (mode === 'agent' && transport === 'relay') {
|
|
75
|
+
return runViaAgentRelay(kind, instruction, wakeContext, effectiveModel, relayContext, cwd, signal);
|
|
76
|
+
}
|
|
69
77
|
|
|
70
78
|
if (kind === 'relayflows-wrapper-v1') {
|
|
71
79
|
return requirePricedUsage(decodeWrapperResult(await runWrapperSession(
|
|
72
80
|
cli,
|
|
73
81
|
instruction,
|
|
74
82
|
wakeContext,
|
|
75
|
-
|
|
83
|
+
effectiveModel,
|
|
76
84
|
wrapperEnvironment(process.env),
|
|
77
85
|
wrapperLimits,
|
|
78
86
|
signal,
|
|
79
|
-
)),
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (mode === 'agent' && transport === 'relay') {
|
|
83
|
-
return runViaAgentRelay(kind, instruction, model, relayContext, cwd);
|
|
87
|
+
)), effectiveModel);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
87
91
|
delete env[WAKE_CONTEXT_ENV];
|
|
88
92
|
delete env[MODEL_ENV];
|
|
89
|
-
const invocation = mode === 'llm' ? llmExecution(kind, instruction,
|
|
93
|
+
const invocation = mode === 'llm' ? llmExecution(kind, instruction, effectiveModel) : agentExecution(kind, instruction, effectiveModel);
|
|
90
94
|
|
|
91
95
|
if (wakeContext !== undefined) {
|
|
92
96
|
try {
|
|
@@ -104,57 +108,49 @@ export async function runAgentCli(
|
|
|
104
108
|
// Structured provider output carries the authoritative token counts.
|
|
105
109
|
const args = [...invocation.args];
|
|
106
110
|
args.splice(args.length - 1, 0, ...(kind === 'claude' ? ['--output-format', 'json'] : ['--json']));
|
|
107
|
-
return requirePricedUsage(decodeProviderResult(await spawnInvocation(cli, { ...invocation, args }, env, signal, sidechannel, cwd), kind),
|
|
111
|
+
return requirePricedUsage(decodeProviderResult(await spawnInvocation(cli, { ...invocation, args }, env, signal, sidechannel, cwd), kind), effectiveModel);
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
/**
|
|
111
|
-
* Relay-transport path (flows#385). Fires an agent-relay spawn HTTP request
|
|
112
|
-
* that registers the CLI as a first-class workspace participant DMs can
|
|
113
|
-
* steer, then reports the spawn outcome as a WorkerCliResult. Streaming
|
|
114
|
-
* completion tracking is a follow-up — this initial slice proves the
|
|
115
|
-
* transport wire and returns quickly with the registered name in stdout.
|
|
116
|
-
*/
|
|
114
|
+
/** Wait under the same worker lease for an authoritative task receipt. */
|
|
117
115
|
async function runViaAgentRelay(
|
|
118
|
-
kind: CliAdapterKind,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
relayContext: AgentRelayContext | undefined,
|
|
122
|
-
worker_cwd: string | undefined,
|
|
116
|
+
kind: CliAdapterKind, instruction: string, wakeContext: unknown,
|
|
117
|
+
model: string | undefined, context: AgentRelayContext | undefined,
|
|
118
|
+
worker_cwd: string | undefined, signal: AbortSignal | undefined,
|
|
123
119
|
): Promise<WorkerCliResult> {
|
|
124
|
-
if (kind === 'relayflows-wrapper-v1') {
|
|
125
|
-
return {
|
|
126
|
-
exit_code: null,
|
|
127
|
-
stdout_tail: '',
|
|
128
|
-
stderr_tail: 'agent-relay transport does not support the relayflows-wrapper-v1 same-process session.',
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
if (relayContext === undefined) {
|
|
132
|
-
return {
|
|
133
|
-
exit_code: null,
|
|
134
|
-
stdout_tail: '',
|
|
135
|
-
stderr_tail: 'agent-relay transport requires a relayContext (runId, stepId); the caller did not thread it through.',
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
const name = deriveAgentName(relayContext.runId, relayContext.stepId);
|
|
139
120
|
try {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
task
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
121
|
+
if (kind === 'relayflows-wrapper-v1') throw new Error('Relay task transport does not support same-process wrappers.');
|
|
122
|
+
if (!context?.dataDir) throw new Error('Relay task transport requires a durable data directory and journal dispatch identity.');
|
|
123
|
+
const task = instruction + (wakeContext === undefined ? '' : `\n\nWake context (journaled):\n${JSON.stringify(wakeContext)}`)
|
|
124
|
+
+ '\n\nReport the final task output with the injected agent_result tool and final=true. Wait for its successful durable acknowledgment before exiting.';
|
|
125
|
+
const received = await runAgentRelayTask({
|
|
126
|
+
cli: kind, task, model, worker_cwd, result_schema: context.resultSchema,
|
|
127
|
+
runId: context.runId, stepId: context.stepId, idempotencyKey: context.idempotencyKey,
|
|
128
|
+
dataDir: context.dataDir,
|
|
129
|
+
}, { signal });
|
|
130
|
+
const receipt = { ...received, error: received.error === null ? null : redactRelayError(received.error) };
|
|
131
|
+
const accounting = receipt.task_execution.accounting;
|
|
132
|
+
const result: WorkerCliResult = {
|
|
133
|
+
relay_task: receipt, exit_code: receipt.status === 'completed' ? 0 : 1,
|
|
134
|
+
stdout_tail: receipt.status === 'completed' ? JSON.stringify(receipt.output) : '',
|
|
135
|
+
stderr_tail: receipt.status === 'failed' ? `Relay task failed: ${receipt.error}` : '',
|
|
136
|
+
...(accounting?.tokens_input === undefined ? {} : { tokens_input: accounting.tokens_input }),
|
|
137
|
+
...(accounting?.tokens_output === undefined ? {} : { tokens_output: accounting.tokens_output }),
|
|
151
138
|
};
|
|
139
|
+
return requirePricedUsage(result, model);
|
|
152
140
|
} catch (error) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
:
|
|
156
|
-
return { exit_code: null, stdout_tail: '', stderr_tail: detail };
|
|
141
|
+
signal?.throwIfAborted();
|
|
142
|
+
const detail = error instanceof AgentRelayTransportError ? error.message
|
|
143
|
+
: error instanceof Error ? error.message : 'Relay task transport failed';
|
|
144
|
+
return { exit_code: null, stdout_tail: '', stderr_tail: redactRelayError(detail) };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function redactRelayError(message: string): string {
|
|
149
|
+
for (const key of ['RELAY_AGENT_TOKEN', 'RELAY_API_KEY']) {
|
|
150
|
+
const secret = process.env[key];
|
|
151
|
+
if (secret) message = message.replaceAll(secret, '[redacted]');
|
|
157
152
|
}
|
|
153
|
+
return message.replace(/\b(?:at|rk|nt|ot|br|arr)_(?:live_)?[A-Za-z0-9_-]+/g, '[redacted]');
|
|
158
154
|
}
|
|
159
155
|
|
|
160
156
|
async function spawnInvocation(
|
package/src/worker-spend.ts
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { pricedUsage } from './model-pricing.js';
|
|
2
2
|
import type { WorkerCliResult } from './worker-cli.js';
|
|
3
3
|
|
|
4
|
+
/** Zero-dollar usage for an unpriced step, only when the CLI reported tokens. */
|
|
5
|
+
function unmeteredUsage(input: number | undefined, output: number | undefined) {
|
|
6
|
+
if (input === undefined || output === undefined) return undefined;
|
|
7
|
+
return { tokens_in: input, tokens_out: output, dollars: '0.000000' };
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
/**
|
|
5
11
|
* Attach token/dollar usage to a worker's CLI result. Invalid token counts
|
|
6
12
|
* (non-integer or negative) are the one remaining failure mode — those are
|
|
7
13
|
* journaled as `worker_error` with the usage projected from clamped counts.
|
|
8
14
|
*
|
|
9
|
-
* Unpriced models are NOT a failure here:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
15
|
+
* Unpriced models are NOT a failure here: the step journals zero dollars, so it
|
|
16
|
+
* never trips a dollar budget, but its reported tokens still count toward any
|
|
17
|
+
* token budget. Preflight (see `budgetDiagnostics`) warns that such a step is
|
|
18
|
+
* unmetered for dollars.
|
|
12
19
|
*/
|
|
13
20
|
export function workerSpend(result: WorkerCliResult, model?: string) {
|
|
14
|
-
try {
|
|
21
|
+
try {
|
|
22
|
+
const usage = pricedUsage(model, result.tokens_input, result.tokens_output)
|
|
23
|
+
?? unmeteredUsage(result.tokens_input, result.tokens_output);
|
|
24
|
+
return { result, usage };
|
|
25
|
+
}
|
|
15
26
|
catch (error) {
|
|
16
27
|
return {
|
|
17
28
|
result: { ...result, exit_code: null, stderr_tail: error instanceof Error ? error.message : 'Invalid model usage' },
|
package/src/worker.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { JournalClient } from './journal-client.js';
|
|
|
5
5
|
import type { Pins, StepDispatchEvent } from './protocol.js';
|
|
6
6
|
import type { KernelAgentStep } from './spec.js';
|
|
7
7
|
import { runAgentCli } from './worker-cli.js';
|
|
8
|
+
import { resolveCliModel } from './cli-adapter.js';
|
|
8
9
|
import { withWorkerLease } from './worker-lease.js';
|
|
9
10
|
import { workerInstruction } from './worker-input.js';
|
|
10
11
|
import { helperCall } from './yaml-helpers.js';
|
|
@@ -105,16 +106,18 @@ export class AgentWorker extends EventEmitter {
|
|
|
105
106
|
return;
|
|
106
107
|
}
|
|
107
108
|
let humanIntervention = false;
|
|
109
|
+
const effectiveModel = typeof spec.cli === 'string' ? resolveCliModel(spec.cli, spec.model) : spec.model;
|
|
108
110
|
const completed: WorkerCliResult = await withWorkerLease(this.client, dispatch, signal =>
|
|
109
111
|
typeof spec.cli === 'string' && typeof spec.instruction === 'string'
|
|
110
|
-
? runAgentCli(spec.cli, workerInstruction(spec.instruction, dispatch), dispatch.wake_context,
|
|
112
|
+
? runAgentCli(spec.cli, workerInstruction(spec.instruction, dispatch), dispatch.wake_context, effectiveModel, undefined, signal, 'agent', this.options.dataDir === undefined ? undefined : {
|
|
111
113
|
dataDir: this.options.dataDir, runId: dispatch.run_id, stepId: dispatch.step_id,
|
|
112
114
|
onReady: this.options.onPtyReady, onDrive: () => { humanIntervention = true; },
|
|
113
115
|
}, typeof spec.cwd === 'string' ? spec.cwd : undefined,
|
|
114
116
|
spec.transport === 'relay' ? 'relay' : 'direct',
|
|
115
|
-
{ runId: dispatch.run_id, stepId: dispatch.step_id
|
|
117
|
+
{ runId: dispatch.run_id, stepId: dispatch.step_id, idempotencyKey: dispatch.idempotency_key,
|
|
118
|
+
dataDir: this.options.dataDir, resultSchema: spec.verification?.json_schema })
|
|
116
119
|
: Promise.resolve({ exit_code: null, stdout_tail: '', stderr_tail: 'agent step has no declared CLI' }));
|
|
117
|
-
const { result, usage } = workerSpend(completed,
|
|
120
|
+
const { result, usage } = workerSpend(completed, effectiveModel);
|
|
118
121
|
const completionReason = result.exit_code === 0 ? 'success' : 'worker_error';
|
|
119
122
|
|
|
120
123
|
// Output shape: if the CLI's stdout parses as JSON, promote THAT
|
|
@@ -130,7 +133,8 @@ export class AgentWorker extends EventEmitter {
|
|
|
130
133
|
// emitting an error JSON with exit 0. `completionReason` is
|
|
131
134
|
// derived from exit code, so a CLI that exits 0 while emitting
|
|
132
135
|
// `{"error":...}` will report success with an error payload.
|
|
133
|
-
const output =
|
|
136
|
+
const output = result.relay_task?.status === 'completed' && result.exit_code === 0
|
|
137
|
+
? result.relay_task.output : parseJsonOutput(result.stdout_tail) ?? result;
|
|
134
138
|
|
|
135
139
|
await this.client.stepComplete(
|
|
136
140
|
dispatch.run_id,
|
|
@@ -140,6 +144,10 @@ export class AgentWorker extends EventEmitter {
|
|
|
140
144
|
completionReason,
|
|
141
145
|
{
|
|
142
146
|
output,
|
|
147
|
+
...(result.relay_task === undefined ? {} : { trajectory_tail: { relay_task: {
|
|
148
|
+
invocation_id: result.relay_task.invocation_id, status: result.relay_task.status,
|
|
149
|
+
task_execution: result.relay_task.task_execution, error: result.relay_task.error,
|
|
150
|
+
} } }),
|
|
143
151
|
...(humanIntervention ? { human_intervention: true } : {}),
|
|
144
152
|
...(usage !== undefined ? { usage } : {}),
|
|
145
153
|
started_pins: dispatch.pins,
|