@tagma/sdk 0.7.0 → 0.7.1
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/core/dataflow.d.ts.map +1 -1
- package/dist/core/dataflow.js +45 -9
- package/dist/core/dataflow.js.map +1 -1
- package/dist/core/task-executor.d.ts.map +1 -1
- package/dist/core/task-executor.js +35 -51
- package/dist/core/task-executor.js.map +1 -1
- package/dist/ports.d.ts +4 -0
- package/dist/ports.d.ts.map +1 -1
- package/dist/ports.js +27 -4
- package/dist/ports.js.map +1 -1
- package/dist/validate-raw.d.ts +4 -4
- package/dist/validate-raw.js +91 -132
- package/dist/validate-raw.js.map +1 -1
- package/package.json +2 -2
- package/src/core/dataflow.test.ts +8 -9
- package/src/core/dataflow.ts +57 -14
- package/src/core/task-executor.ts +61 -95
- package/src/engine-ports-mixed.test.ts +68 -411
- package/src/engine-ports.test.ts +37 -341
- package/src/pipeline-runner.test.ts +5 -9
- package/src/ports.test.ts +80 -0
- package/src/ports.ts +36 -4
- package/src/schema-ports.test.ts +41 -214
- package/src/validate-raw-ports.test.ts +80 -393
- package/src/validate-raw.ts +93 -137
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tagma/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Local AI task orchestration engine — core SDK for tagma pipelines",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"release:publish": "bun scripts/release.ts --publish"
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
|
-
"@tagma/types": "0.4.
|
|
95
|
+
"@tagma/types": "0.4.9",
|
|
96
96
|
"js-yaml": "^4.1.0",
|
|
97
97
|
"chokidar": "^4.0.0"
|
|
98
98
|
}
|
|
@@ -36,7 +36,7 @@ function result(stdout: string, normalizedOutput: string | null = null): TaskRes
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
describe('inferEffectivePorts', () => {
|
|
39
|
-
test('returns
|
|
39
|
+
test('returns typed outputs for command tasks', () => {
|
|
40
40
|
const config: PipelineConfig = {
|
|
41
41
|
name: 'p',
|
|
42
42
|
tracks: [
|
|
@@ -48,7 +48,7 @@ describe('inferEffectivePorts', () => {
|
|
|
48
48
|
id: 'cmd',
|
|
49
49
|
name: 'Cmd',
|
|
50
50
|
command: 'echo',
|
|
51
|
-
|
|
51
|
+
outputs: { city: { type: 'string' } },
|
|
52
52
|
},
|
|
53
53
|
],
|
|
54
54
|
},
|
|
@@ -75,7 +75,7 @@ describe('inferEffectivePorts', () => {
|
|
|
75
75
|
id: 'up',
|
|
76
76
|
name: 'Up',
|
|
77
77
|
command: 'echo',
|
|
78
|
-
|
|
78
|
+
outputs: { city: { type: 'string' } },
|
|
79
79
|
},
|
|
80
80
|
{ id: 'prompt', name: 'Prompt', prompt: 'hi', depends_on: ['up'] },
|
|
81
81
|
],
|
|
@@ -103,13 +103,13 @@ describe('inferEffectivePorts', () => {
|
|
|
103
103
|
id: 'a',
|
|
104
104
|
name: 'A',
|
|
105
105
|
command: 'echo',
|
|
106
|
-
|
|
106
|
+
outputs: { city: { type: 'string' } },
|
|
107
107
|
},
|
|
108
108
|
{
|
|
109
109
|
id: 'b',
|
|
110
110
|
name: 'B',
|
|
111
111
|
command: 'echo',
|
|
112
|
-
|
|
112
|
+
outputs: { city: { type: 'string' } },
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
id: 'prompt',
|
|
@@ -131,7 +131,7 @@ describe('inferEffectivePorts', () => {
|
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
describe('extractSuccessfulOutputs', () => {
|
|
134
|
-
test('
|
|
134
|
+
test('extracts typed binding outputs', () => {
|
|
135
135
|
const config: PipelineConfig = {
|
|
136
136
|
name: 'p',
|
|
137
137
|
tracks: [
|
|
@@ -143,8 +143,7 @@ describe('extractSuccessfulOutputs', () => {
|
|
|
143
143
|
id: 'cmd',
|
|
144
144
|
name: 'Cmd',
|
|
145
145
|
command: 'echo',
|
|
146
|
-
outputs: { raw: { from: 'stdout' } },
|
|
147
|
-
ports: { outputs: [{ name: 'city', type: 'string' }] },
|
|
146
|
+
outputs: { city: { type: 'string' }, raw: { from: 'stdout' } },
|
|
148
147
|
},
|
|
149
148
|
],
|
|
150
149
|
},
|
|
@@ -154,7 +153,7 @@ describe('extractSuccessfulOutputs', () => {
|
|
|
154
153
|
const node = ctx.dag.nodes.get('t.cmd')!;
|
|
155
154
|
const extracted = extractSuccessfulOutputs({
|
|
156
155
|
task: node.task,
|
|
157
|
-
effectivePorts:
|
|
156
|
+
effectivePorts: undefined,
|
|
158
157
|
result: result('{"city":"Paris"}'),
|
|
159
158
|
});
|
|
160
159
|
expect(extracted.outputs).toEqual({
|
package/src/core/dataflow.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
PortDef,
|
|
3
|
+
TaskConfig,
|
|
4
|
+
TaskInputBindings,
|
|
5
|
+
TaskOutputBindings,
|
|
6
|
+
TaskPorts,
|
|
7
|
+
TaskResult,
|
|
8
|
+
} from '../types';
|
|
2
9
|
import {
|
|
3
10
|
extractTaskBindingOutputs,
|
|
4
11
|
extractTaskOutputs,
|
|
@@ -18,6 +25,40 @@ function isCommandTaskConfig(
|
|
|
18
25
|
return task.command !== undefined && task.prompt === undefined;
|
|
19
26
|
}
|
|
20
27
|
|
|
28
|
+
function inputBindingsToPorts(bindings: TaskInputBindings | undefined): PortDef[] | undefined {
|
|
29
|
+
if (!bindings || Object.keys(bindings).length === 0) return undefined;
|
|
30
|
+
return Object.entries(bindings).map(([name, binding]) => ({
|
|
31
|
+
name,
|
|
32
|
+
type: binding.type ?? 'json',
|
|
33
|
+
...(binding.description ? { description: binding.description } : {}),
|
|
34
|
+
...(binding.required !== undefined ? { required: binding.required } : {}),
|
|
35
|
+
...(binding.default !== undefined ? { default: binding.default } : {}),
|
|
36
|
+
...(binding.enum ? { enum: [...binding.enum] } : {}),
|
|
37
|
+
...(binding.from ? { from: binding.from } : {}),
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function outputBindingsToPorts(bindings: TaskOutputBindings | undefined): PortDef[] | undefined {
|
|
42
|
+
if (!bindings || Object.keys(bindings).length === 0) return undefined;
|
|
43
|
+
return Object.entries(bindings).map(([name, binding]) => ({
|
|
44
|
+
name,
|
|
45
|
+
type: binding.type ?? 'json',
|
|
46
|
+
...(binding.description ? { description: binding.description } : {}),
|
|
47
|
+
...(binding.default !== undefined ? { default: binding.default } : {}),
|
|
48
|
+
...(binding.enum ? { enum: [...binding.enum] } : {}),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function taskBindingsAsPorts(task: TaskConfig): TaskPorts | undefined {
|
|
53
|
+
const inputs = inputBindingsToPorts(task.inputs);
|
|
54
|
+
const outputs = outputBindingsToPorts(task.outputs);
|
|
55
|
+
if (!inputs && !outputs) return undefined;
|
|
56
|
+
return {
|
|
57
|
+
...(inputs ? { inputs } : {}),
|
|
58
|
+
...(outputs ? { outputs } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
21
62
|
export type EffectivePortsResult =
|
|
22
63
|
| {
|
|
23
64
|
readonly kind: 'ready';
|
|
@@ -38,7 +79,7 @@ export function inferEffectivePorts(
|
|
|
38
79
|
const isPromptTask = isPromptTaskConfig(task);
|
|
39
80
|
|
|
40
81
|
if (!isPromptTask) {
|
|
41
|
-
return { kind: 'ready', isPromptTask: false, effectivePorts: task
|
|
82
|
+
return { kind: 'ready', isPromptTask: false, effectivePorts: taskBindingsAsPorts(task) };
|
|
42
83
|
}
|
|
43
84
|
|
|
44
85
|
const inference = inferPromptPorts({
|
|
@@ -47,7 +88,7 @@ export function inferEffectivePorts(
|
|
|
47
88
|
const isUpstreamCommand = upstream ? isCommandTaskConfig(upstream.task) : false;
|
|
48
89
|
return {
|
|
49
90
|
taskId: upstreamId,
|
|
50
|
-
outputs: isUpstreamCommand ? upstream?.task.
|
|
91
|
+
outputs: isUpstreamCommand ? outputBindingsToPorts(upstream?.task.outputs) : undefined,
|
|
51
92
|
};
|
|
52
93
|
}),
|
|
53
94
|
downstreams: (ctx.directDownstreams.get(taskId) ?? []).map((downstreamId) => {
|
|
@@ -55,7 +96,7 @@ export function inferEffectivePorts(
|
|
|
55
96
|
const isDownstreamCommand = downstream ? isCommandTaskConfig(downstream.task) : false;
|
|
56
97
|
return {
|
|
57
98
|
taskId: downstreamId,
|
|
58
|
-
inputs: isDownstreamCommand ? downstream?.task.
|
|
99
|
+
inputs: isDownstreamCommand ? inputBindingsToPorts(downstream?.task.inputs) : undefined,
|
|
59
100
|
};
|
|
60
101
|
}),
|
|
61
102
|
});
|
|
@@ -98,21 +139,23 @@ export function extractSuccessfulOutputs(
|
|
|
98
139
|
extractedOutputs = bindingExtraction.outputs;
|
|
99
140
|
}
|
|
100
141
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
extractedOutputs =
|
|
108
|
-
|
|
109
|
-
|
|
142
|
+
if ((!task.outputs || Object.keys(task.outputs).length === 0) && effectivePorts?.outputs?.length) {
|
|
143
|
+
const portExtraction = extractTaskOutputs(
|
|
144
|
+
effectivePorts,
|
|
145
|
+
result.stdout,
|
|
146
|
+
result.normalizedOutput,
|
|
147
|
+
);
|
|
148
|
+
extractedOutputs = portExtraction.outputs;
|
|
149
|
+
return {
|
|
150
|
+
outputs: extractedOutputs,
|
|
151
|
+
bindingDiagnostic: bindingExtraction.diagnostic,
|
|
152
|
+
portDiagnostic: portExtraction.diagnostic,
|
|
110
153
|
};
|
|
111
154
|
}
|
|
112
155
|
|
|
113
156
|
return {
|
|
114
157
|
outputs: extractedOutputs,
|
|
115
158
|
bindingDiagnostic: bindingExtraction.diagnostic,
|
|
116
|
-
portDiagnostic:
|
|
159
|
+
portDiagnostic: null,
|
|
117
160
|
};
|
|
118
161
|
}
|
|
@@ -21,11 +21,7 @@ import {
|
|
|
21
21
|
renderInputsBlock,
|
|
22
22
|
renderOutputSchemaBlock,
|
|
23
23
|
} from '../prompt-doc';
|
|
24
|
-
import {
|
|
25
|
-
resolveTaskBindingInputs,
|
|
26
|
-
resolveTaskInputs,
|
|
27
|
-
substituteInputs,
|
|
28
|
-
} from '../ports';
|
|
24
|
+
import { resolveTaskBindingInputs, resolveTaskInputs, substituteInputs } from '../ports';
|
|
29
25
|
import { executeHook, buildTaskContext } from '../hooks';
|
|
30
26
|
import { clip, tailLines, type Logger } from '../logger';
|
|
31
27
|
import type { ApprovalGateway } from '../approval';
|
|
@@ -35,15 +31,17 @@ import { TriggerBlockedError, TriggerTimeoutError } from './trigger-errors';
|
|
|
35
31
|
|
|
36
32
|
const MAX_NORMALIZED_BYTES = 1_000_000;
|
|
37
33
|
|
|
38
|
-
function isPromptTaskConfig(
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
function isPromptTaskConfig(task: {
|
|
35
|
+
readonly prompt?: string;
|
|
36
|
+
readonly command?: string;
|
|
37
|
+
}): task is { readonly prompt: string; readonly command?: undefined } {
|
|
41
38
|
return task.prompt !== undefined && task.command === undefined;
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
function isCommandTaskConfig(
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
function isCommandTaskConfig(task: {
|
|
42
|
+
readonly command?: string;
|
|
43
|
+
readonly prompt?: string;
|
|
44
|
+
}): task is { readonly command: string; readonly prompt?: undefined } {
|
|
47
45
|
return task.command !== undefined && task.prompt === undefined;
|
|
48
46
|
}
|
|
49
47
|
|
|
@@ -196,7 +194,12 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
196
194
|
const hookResult = await executeHook(
|
|
197
195
|
config.hooks,
|
|
198
196
|
'task_start',
|
|
199
|
-
buildTaskContext(
|
|
197
|
+
buildTaskContext(
|
|
198
|
+
'task_start',
|
|
199
|
+
pipelineInfo,
|
|
200
|
+
ctx.trackInfoOf(taskId),
|
|
201
|
+
ctx.buildTaskInfoObj(taskId),
|
|
202
|
+
),
|
|
200
203
|
workDir,
|
|
201
204
|
ctx.abortController.signal,
|
|
202
205
|
);
|
|
@@ -309,50 +312,48 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
309
312
|
);
|
|
310
313
|
}
|
|
311
314
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (inputResolution.kind === 'blocked') {
|
|
319
|
-
log.error(
|
|
320
|
-
`[task:${taskId}]`,
|
|
321
|
-
`blocked — cannot resolve port inputs:\n${inputResolution.reason}`,
|
|
315
|
+
let inferredPromptInputs: Readonly<Record<string, unknown>> = {};
|
|
316
|
+
if (isPromptTask && effectivePorts?.inputs && effectivePorts.inputs.length > 0) {
|
|
317
|
+
const inputResolution = resolveTaskInputs(
|
|
318
|
+
{ ...task, ports: effectivePorts },
|
|
319
|
+
ctx.outputValuesMap,
|
|
320
|
+
node.dependsOn,
|
|
322
321
|
);
|
|
323
|
-
|
|
324
|
-
exitCode: -1,
|
|
325
|
-
stdout: '',
|
|
326
|
-
stderr: `[engine] port input resolution failed:\n${inputResolution.reason}`,
|
|
327
|
-
stdoutPath: null,
|
|
328
|
-
stderrPath: null,
|
|
329
|
-
durationMs: 0,
|
|
330
|
-
sessionId: null,
|
|
331
|
-
normalizedOutput: null,
|
|
332
|
-
failureKind: 'spawn_error',
|
|
333
|
-
outputs: null,
|
|
334
|
-
};
|
|
335
|
-
state.finishedAt = nowISO();
|
|
336
|
-
ctx.setTaskStatus(taskId, 'blocked');
|
|
337
|
-
try {
|
|
338
|
-
await ctx.fireHook(taskId, 'task_failure');
|
|
339
|
-
} catch (hookErr) {
|
|
322
|
+
if (inputResolution.kind === 'blocked') {
|
|
340
323
|
log.error(
|
|
341
324
|
`[task:${taskId}]`,
|
|
342
|
-
`
|
|
325
|
+
`blocked — cannot resolve inferred prompt inputs:\n${inputResolution.reason}`,
|
|
343
326
|
);
|
|
327
|
+
state.result = {
|
|
328
|
+
exitCode: -1,
|
|
329
|
+
stdout: '',
|
|
330
|
+
stderr: `[engine] inferred prompt input resolution failed:\n${inputResolution.reason}`,
|
|
331
|
+
stdoutPath: null,
|
|
332
|
+
stderrPath: null,
|
|
333
|
+
durationMs: 0,
|
|
334
|
+
sessionId: null,
|
|
335
|
+
normalizedOutput: null,
|
|
336
|
+
failureKind: 'spawn_error',
|
|
337
|
+
outputs: null,
|
|
338
|
+
};
|
|
339
|
+
state.finishedAt = nowISO();
|
|
340
|
+
ctx.setTaskStatus(taskId, 'blocked');
|
|
341
|
+
try {
|
|
342
|
+
await ctx.fireHook(taskId, 'task_failure');
|
|
343
|
+
} catch (hookErr) {
|
|
344
|
+
log.error(
|
|
345
|
+
`[task:${taskId}]`,
|
|
346
|
+
`hook execution failed: ${hookErr instanceof Error ? hookErr.message : String(hookErr)}`,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (ctx.getOnFailure(taskId) === 'stop_all') ctx.applyStopAll();
|
|
350
|
+
return;
|
|
344
351
|
}
|
|
345
|
-
|
|
346
|
-
return;
|
|
352
|
+
inferredPromptInputs = inputResolution.inputs;
|
|
347
353
|
}
|
|
348
|
-
|
|
354
|
+
|
|
355
|
+
const resolvedInputs = { ...inferredPromptInputs, ...bindingResolution.inputs };
|
|
349
356
|
ctx.resolvedInputsMap.set(taskId, resolvedInputs);
|
|
350
|
-
if (inputResolution.missingOptional.length > 0) {
|
|
351
|
-
log.debug(
|
|
352
|
-
`[task:${taskId}]`,
|
|
353
|
-
`optional inputs unresolved (empty in placeholders): ${inputResolution.missingOptional.join(', ')}`,
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
357
|
if (effectivePorts?.inputs && effectivePorts.inputs.length > 0) {
|
|
357
358
|
log.debug(
|
|
358
359
|
`[task:${taskId}]`,
|
|
@@ -413,10 +414,7 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
413
414
|
// errors, so the only way to land here with an unresolved is an
|
|
414
415
|
// optional input that had no upstream producer and no default,
|
|
415
416
|
// which we surface in the log.
|
|
416
|
-
const { text: expandedCommand, unresolved } = substituteInputs(
|
|
417
|
-
task.command,
|
|
418
|
-
resolvedInputs,
|
|
419
|
-
);
|
|
417
|
+
const { text: expandedCommand, unresolved } = substituteInputs(task.command, resolvedInputs);
|
|
420
418
|
if (unresolved.length > 0) {
|
|
421
419
|
log.debug(
|
|
422
420
|
`[task:${taskId}]`,
|
|
@@ -433,10 +431,7 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
433
431
|
// Substitute placeholders in the user-authored prompt before
|
|
434
432
|
// wrapping into a PromptDocument so middlewares see the
|
|
435
433
|
// already-resolved task text.
|
|
436
|
-
const { text: expandedPrompt, unresolved } = substituteInputs(
|
|
437
|
-
task.prompt!,
|
|
438
|
-
resolvedInputs,
|
|
439
|
-
);
|
|
434
|
+
const { text: expandedPrompt, unresolved } = substituteInputs(task.prompt!, resolvedInputs);
|
|
440
435
|
if (unresolved.length > 0) {
|
|
441
436
|
log.debug(
|
|
442
437
|
`[task:${taskId}]`,
|
|
@@ -460,10 +455,7 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
460
455
|
}
|
|
461
456
|
const mws = task.middlewares !== undefined ? task.middlewares : track.middlewares;
|
|
462
457
|
if (mws && mws.length > 0) {
|
|
463
|
-
log.debug(
|
|
464
|
-
`[task:${taskId}]`,
|
|
465
|
-
`middleware chain: ${mws.map((m) => m.type).join(' → ')}`,
|
|
466
|
-
);
|
|
458
|
+
log.debug(`[task:${taskId}]`, `middleware chain: ${mws.map((m) => m.type).join(' → ')}`);
|
|
467
459
|
const mwCtx: MiddlewareContext = {
|
|
468
460
|
task,
|
|
469
461
|
track,
|
|
@@ -480,11 +472,7 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
480
472
|
// middleware's output becomes the new task body) but never
|
|
481
473
|
// silently drops content.
|
|
482
474
|
if (typeof mwPlugin.enhanceDoc === 'function') {
|
|
483
|
-
const next = await mwPlugin.enhanceDoc(
|
|
484
|
-
doc,
|
|
485
|
-
mwConfig as Record<string, unknown>,
|
|
486
|
-
mwCtx,
|
|
487
|
-
);
|
|
475
|
+
const next = await mwPlugin.enhanceDoc(doc, mwConfig as Record<string, unknown>, mwCtx);
|
|
488
476
|
if (
|
|
489
477
|
!next ||
|
|
490
478
|
typeof next !== 'object' ||
|
|
@@ -558,13 +546,6 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
558
546
|
...task,
|
|
559
547
|
prompt,
|
|
560
548
|
continue_from: node.resolvedContinueFrom,
|
|
561
|
-
// Hand the driver the EFFECTIVE port schema rather than the
|
|
562
|
-
// raw task.ports. For Prompt tasks this is the one inferred
|
|
563
|
-
// from neighbor Commands; Command tasks are unchanged.
|
|
564
|
-
// Drivers that introspect ports (e.g. to annotate a system
|
|
565
|
-
// prompt with the I/O contract) otherwise saw `undefined`
|
|
566
|
-
// for every prompt and had no way to know the contract.
|
|
567
|
-
ports: effectivePorts,
|
|
568
549
|
};
|
|
569
550
|
const driverCtx: DriverContext = {
|
|
570
551
|
sessionMap: ctx.sessionMap,
|
|
@@ -575,12 +556,8 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
575
556
|
// contexts and task). Drivers that read task.prompt see the
|
|
576
557
|
// default serialization and need no changes.
|
|
577
558
|
promptDoc: doc,
|
|
578
|
-
//
|
|
579
|
-
// already coerced
|
|
580
|
-
// need to re-substitute placeholders inside a custom envelope
|
|
581
|
-
// can read this and call `substituteInputs`; most drivers can
|
|
582
|
-
// ignore it because the engine has already expanded
|
|
583
|
-
// `{{inputs.X}}` into `task.prompt` upstream.
|
|
559
|
+
// Resolved input values keyed by input name. Typed bindings have
|
|
560
|
+
// already been coerced when a binding declares `type`.
|
|
584
561
|
inputs: resolvedInputs,
|
|
585
562
|
};
|
|
586
563
|
const spec = await driver.buildCommand(enrichedTask, track, driverCtx);
|
|
@@ -588,10 +565,7 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
588
565
|
log.debug(`[task:${taskId}]`, `spawn args: ${JSON.stringify(spec.args)}`);
|
|
589
566
|
if (spec.cwd) log.debug(`[task:${taskId}]`, `spawn cwd: ${spec.cwd}`);
|
|
590
567
|
if (spec.env)
|
|
591
|
-
log.debug(
|
|
592
|
-
`[task:${taskId}]`,
|
|
593
|
-
`spawn env overrides: ${Object.keys(spec.env).join(', ')}`,
|
|
594
|
-
);
|
|
568
|
+
log.debug(`[task:${taskId}]`, `spawn env overrides: ${Object.keys(spec.env).join(', ')}`);
|
|
595
569
|
if (spec.stdin) log.debug(`[task:${taskId}]`, `spawn stdin: ${spec.stdin.length} chars`);
|
|
596
570
|
result = await runSpawn(spec, driver, runOpts);
|
|
597
571
|
}
|
|
@@ -652,13 +626,11 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
652
626
|
);
|
|
653
627
|
if (outputExtraction.bindingDiagnostic) {
|
|
654
628
|
log.debug(`[task:${taskId}]`, outputExtraction.bindingDiagnostic);
|
|
629
|
+
const note = `\n[engine] ${outputExtraction.bindingDiagnostic}`;
|
|
630
|
+
result = { ...result, stderr: result.stderr + note };
|
|
655
631
|
}
|
|
656
632
|
}
|
|
657
633
|
|
|
658
|
-
// Prompt tasks use inferred ports (from direct-downstream Command
|
|
659
|
-
// inputs); Command tasks use their declared ports. Either way,
|
|
660
|
-
// `extractTaskOutputs` is a no-op when there are no declared
|
|
661
|
-
// outputs to pull, so pre-ports tasks pay nothing for this call.
|
|
662
634
|
if (effectivePorts?.outputs && effectivePorts.outputs.length > 0) {
|
|
663
635
|
log.debug(
|
|
664
636
|
`[task:${taskId}]`,
|
|
@@ -745,16 +717,10 @@ export async function executeTask(options: ExecuteTaskOptions): Promise<void> {
|
|
|
745
717
|
log.debug(`[task:${taskId}]`, `wrote stderr: ${result.stderrPath}`);
|
|
746
718
|
}
|
|
747
719
|
if (result.stdout) {
|
|
748
|
-
log.quiet(
|
|
749
|
-
`--- stdout (${taskId}) ---\n${clip(result.stdout)}\n--- end stdout ---`,
|
|
750
|
-
taskId,
|
|
751
|
-
);
|
|
720
|
+
log.quiet(`--- stdout (${taskId}) ---\n${clip(result.stdout)}\n--- end stdout ---`, taskId);
|
|
752
721
|
}
|
|
753
722
|
if (result.stderr) {
|
|
754
|
-
log.quiet(
|
|
755
|
-
`--- stderr (${taskId}) ---\n${clip(result.stderr)}\n--- end stderr ---`,
|
|
756
|
-
taskId,
|
|
757
|
-
);
|
|
723
|
+
log.quiet(`--- stderr (${taskId}) ---\n${clip(result.stderr)}\n--- end stderr ---`, taskId);
|
|
758
724
|
}
|
|
759
725
|
if (task.completion) {
|
|
760
726
|
log.debug(
|