@tagma/sdk 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +74 -6
  2. package/dist/engine.d.ts.map +1 -1
  3. package/dist/engine.js +194 -21
  4. package/dist/engine.js.map +1 -1
  5. package/dist/pipeline-runner.d.ts.map +1 -1
  6. package/dist/pipeline-runner.js +3 -0
  7. package/dist/pipeline-runner.js.map +1 -1
  8. package/dist/ports.d.ts +118 -0
  9. package/dist/ports.d.ts.map +1 -0
  10. package/dist/ports.js +365 -0
  11. package/dist/ports.js.map +1 -0
  12. package/dist/prompt-doc.d.ts +35 -1
  13. package/dist/prompt-doc.d.ts.map +1 -1
  14. package/dist/prompt-doc.js +110 -0
  15. package/dist/prompt-doc.js.map +1 -1
  16. package/dist/runner.d.ts +17 -0
  17. package/dist/runner.d.ts.map +1 -1
  18. package/dist/runner.js +171 -8
  19. package/dist/runner.js.map +1 -1
  20. package/dist/schema.d.ts.map +1 -1
  21. package/dist/schema.js +8 -0
  22. package/dist/schema.js.map +1 -1
  23. package/dist/sdk.d.ts +3 -1
  24. package/dist/sdk.d.ts.map +1 -1
  25. package/dist/sdk.js +5 -1
  26. package/dist/sdk.js.map +1 -1
  27. package/dist/validate-raw.d.ts.map +1 -1
  28. package/dist/validate-raw.js +141 -0
  29. package/dist/validate-raw.js.map +1 -1
  30. package/package.json +2 -7
  31. package/src/dag.test.ts +56 -0
  32. package/src/engine-ports.test.ts +404 -0
  33. package/src/engine.ts +231 -24
  34. package/src/pipeline-runner.ts +3 -0
  35. package/src/ports.test.ts +301 -0
  36. package/src/ports.ts +442 -0
  37. package/src/prompt-doc.test.ts +174 -0
  38. package/src/prompt-doc.ts +121 -1
  39. package/src/runner.test.ts +142 -0
  40. package/src/runner.ts +198 -8
  41. package/src/schema-ports.test.ts +236 -0
  42. package/src/schema.ts +8 -0
  43. package/src/sdk.ts +14 -0
  44. package/src/validate-raw-ports.test.ts +198 -0
  45. package/src/validate-raw.ts +155 -1
  46. package/dist/plugin-registry.test.d.ts +0 -2
  47. package/dist/plugin-registry.test.d.ts.map +0 -1
  48. package/dist/plugin-registry.test.js +0 -188
  49. package/dist/plugin-registry.test.js.map +0 -1
  50. package/dist/schema.test.d.ts +0 -2
  51. package/dist/schema.test.d.ts.map +0 -1
  52. package/dist/schema.test.js +0 -94
  53. package/dist/schema.test.js.map +0 -1
  54. package/dist/task-ref.test.d.ts +0 -2
  55. package/dist/task-ref.test.d.ts.map +0 -1
  56. package/dist/task-ref.test.js +0 -364
  57. package/dist/task-ref.test.js.map +0 -1
package/dist/ports.js ADDED
@@ -0,0 +1,365 @@
1
+ // ═══ Task ports: substitute / resolve / extract ═══
2
+ //
3
+ // One module, three concerns, all keyed on `task.ports`:
4
+ //
5
+ // 1. `substituteInputs(text, inputs)` — expand `{{inputs.<name>}}` in
6
+ // user-authored strings (command lines, prompts). Strict syntax, no
7
+ // arbitrary expressions — the placeholder is a thin pasteboard, not
8
+ // a templating engine. Unknown / undefined references render as empty
9
+ // string with a diagnostic that the caller can surface.
10
+ //
11
+ // 2. `resolveTaskInputs(task, upstreamOutputs, dependsOn)` — gather the
12
+ // values a task will consume from its direct upstreams. Matches by
13
+ // port name (or by explicit `from:`), applies defaults, coerces to
14
+ // the declared type, and classifies the result as ready / missing
15
+ // required / ambiguous. The engine calls this before a task starts
16
+ // and uses the classification to decide whether to block.
17
+ //
18
+ // 3. `extractTaskOutputs(ports, stdout, normalizedOutput)` — after a
19
+ // task succeeds, pull the declared output values from the task's
20
+ // output stream. Default strategy: find the last non-empty line that
21
+ // parses as a JSON object, and read each declared output name from
22
+ // it. Prefer `normalizedOutput` for AI tasks, fall back to raw
23
+ // stdout — command tasks only ever have stdout.
24
+ //
25
+ // Everything here is pure / deterministic so it can be reused by the CLI,
26
+ // the editor (for preview/simulation), and the engine without side effects.
27
+ // ─── Template substitution ────────────────────────────────────────────
28
+ /**
29
+ * Matches `{{inputs.<identifier>}}` with optional whitespace inside the
30
+ * braces. The identifier is restricted to the same character set we use
31
+ * for task IDs (letter/underscore, then letters/digits/underscores) so
32
+ * accidental use of `{{inputs.foo.bar}}` fails loudly rather than
33
+ * silently producing garbage.
34
+ */
35
+ const PLACEHOLDER_RE = /\{\{\s*inputs\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
36
+ /**
37
+ * Scan `text` for every `{{inputs.<name>}}` placeholder and return the
38
+ * set of referenced input names. Useful at validation time: the editor
39
+ * can cross-check that each placeholder has a corresponding declared
40
+ * port and flag typos before a run ever starts.
41
+ */
42
+ export function extractInputReferences(text) {
43
+ const names = new Set();
44
+ for (const match of text.matchAll(PLACEHOLDER_RE)) {
45
+ names.add(match[1]);
46
+ }
47
+ return [...names];
48
+ }
49
+ /**
50
+ * Replace `{{inputs.<name>}}` placeholders in `text` with values from
51
+ * `inputs`. Coercion:
52
+ * - string → as-is
53
+ * - number / boolean → `String(value)`
54
+ * - null / undefined → empty string (name is also reported as unresolved)
55
+ * - anything else (object, array, json port) → `JSON.stringify(value)`
56
+ *
57
+ * Values are substituted *verbatim* — quoting is the user's
58
+ * responsibility in the authored text. For command lines that interpolate
59
+ * user-provided strings, authors should wrap the placeholder in quotes:
60
+ *
61
+ * weather.sh --city "{{inputs.city}}"
62
+ *
63
+ * That's a documented contract rather than a silent shell-escape, because
64
+ * silent escaping would hide the difference between `--city Shanghai` and
65
+ * `--flag $(echo pwned)` — both valid command fragments, one a bug, one a
66
+ * feature. Users know which they want; the engine doesn't.
67
+ */
68
+ export function substituteInputs(text, inputs) {
69
+ const unresolved = new Set();
70
+ const out = text.replace(PLACEHOLDER_RE, (_full, name) => {
71
+ if (!(name in inputs)) {
72
+ unresolved.add(name);
73
+ return '';
74
+ }
75
+ const value = inputs[name];
76
+ if (value === null || value === undefined) {
77
+ unresolved.add(name);
78
+ return '';
79
+ }
80
+ if (typeof value === 'string')
81
+ return value;
82
+ if (typeof value === 'number' || typeof value === 'boolean')
83
+ return String(value);
84
+ try {
85
+ return JSON.stringify(value);
86
+ }
87
+ catch {
88
+ // Circular / unserializable — render a placeholder rather than
89
+ // throwing, and mark it unresolved so the caller can warn.
90
+ unresolved.add(name);
91
+ return '';
92
+ }
93
+ });
94
+ return { text: out, unresolved: [...unresolved] };
95
+ }
96
+ /**
97
+ * Resolve the input values for `task` from the outputs its direct
98
+ * upstreams produced.
99
+ *
100
+ * `upstreamOutputs` is keyed by fully-qualified task id and maps to the
101
+ * outputs that task published (its `TaskResult.outputs`). `dependsOn` is
102
+ * the already-qualified dependency list (from `DagNode.dependsOn`). When
103
+ * an upstream has no outputs entry (e.g. it didn't declare any or it
104
+ * failed), its entry may be missing — we just skip it during matching.
105
+ *
106
+ * Matching rules:
107
+ * - If the input port has `from: "taskId.portName"` → look up that
108
+ * specific upstream / port. Missing = unsatisfied.
109
+ * - If it has `from: "portName"` (bare) → treat as explicit port name
110
+ * but allow any upstream to provide it (useful when the user wants
111
+ * to match by name but still be explicit about the intent).
112
+ * - If no `from` → scan every upstream's outputs for a key matching
113
+ * the input name. Zero hits = unsatisfied; 2+ hits across different
114
+ * upstreams = ambiguous.
115
+ *
116
+ * The function never throws on config errors — every failure mode maps
117
+ * to a field of the `blocked` result so the engine can log a unified
118
+ * message and mark the task blocked.
119
+ */
120
+ export function resolveTaskInputs(task, upstreamOutputs, dependsOn) {
121
+ const inputsDecl = task.ports?.inputs;
122
+ if (!inputsDecl || inputsDecl.length === 0) {
123
+ return { kind: 'ready', inputs: {}, missingOptional: [] };
124
+ }
125
+ const inputs = {};
126
+ const missingRequired = [];
127
+ const missingOptional = [];
128
+ const ambiguous = [];
129
+ const typeErrors = [];
130
+ for (const port of inputsDecl) {
131
+ const found = findUpstreamValue(port, upstreamOutputs, dependsOn);
132
+ if (found.kind === 'ambiguous') {
133
+ ambiguous.push({ port: port.name, producers: found.producers });
134
+ continue;
135
+ }
136
+ let value;
137
+ let present = false;
138
+ if (found.kind === 'hit') {
139
+ value = found.value;
140
+ present = true;
141
+ }
142
+ else if (port.default !== undefined) {
143
+ value = port.default;
144
+ present = true;
145
+ }
146
+ if (!present) {
147
+ if (port.required === true) {
148
+ missingRequired.push(port.name);
149
+ }
150
+ else {
151
+ missingOptional.push(port.name);
152
+ }
153
+ continue;
154
+ }
155
+ const coerced = coerceValue(port, value);
156
+ if (coerced.kind === 'error') {
157
+ typeErrors.push({ port: port.name, reason: coerced.reason });
158
+ continue;
159
+ }
160
+ inputs[port.name] = coerced.value;
161
+ }
162
+ if (missingRequired.length > 0 || ambiguous.length > 0 || typeErrors.length > 0) {
163
+ const lines = [];
164
+ if (missingRequired.length > 0) {
165
+ lines.push(`missing required input(s): ${missingRequired.join(', ')}`);
166
+ }
167
+ if (ambiguous.length > 0) {
168
+ for (const amb of ambiguous) {
169
+ lines.push(`input "${amb.port}" is produced by multiple upstreams ` +
170
+ `(${amb.producers.join(', ')}) — disambiguate with "from: taskId.${amb.port}"`);
171
+ }
172
+ }
173
+ if (typeErrors.length > 0) {
174
+ for (const te of typeErrors) {
175
+ lines.push(`input "${te.port}": ${te.reason}`);
176
+ }
177
+ }
178
+ return {
179
+ kind: 'blocked',
180
+ missingRequired,
181
+ ambiguous,
182
+ typeErrors,
183
+ reason: lines.join('\n'),
184
+ };
185
+ }
186
+ return { kind: 'ready', inputs, missingOptional };
187
+ }
188
+ function findUpstreamValue(port, upstreamOutputs, dependsOn) {
189
+ // Explicit fully-qualified binding: "taskId.portName"
190
+ if (port.from && port.from.includes('.')) {
191
+ const dot = port.from.lastIndexOf('.');
192
+ const upstreamId = port.from.slice(0, dot);
193
+ const portName = port.from.slice(dot + 1);
194
+ const upstream = upstreamOutputs.get(upstreamId);
195
+ if (upstream && portName in upstream) {
196
+ return { kind: 'hit', producer: upstreamId, value: upstream[portName] };
197
+ }
198
+ return { kind: 'miss' };
199
+ }
200
+ // Name match (either explicit `from: "portName"` or defaulted to port.name)
201
+ const key = port.from ?? port.name;
202
+ const hits = [];
203
+ for (const upstreamId of dependsOn) {
204
+ const upstream = upstreamOutputs.get(upstreamId);
205
+ if (upstream && key in upstream) {
206
+ hits.push({ producer: upstreamId, value: upstream[key] });
207
+ }
208
+ }
209
+ if (hits.length === 0)
210
+ return { kind: 'miss' };
211
+ if (hits.length === 1)
212
+ return { kind: 'hit', producer: hits[0].producer, value: hits[0].value };
213
+ return { kind: 'ambiguous', producers: hits.map((h) => h.producer) };
214
+ }
215
+ function coerceValue(port, raw) {
216
+ switch (port.type) {
217
+ case 'string': {
218
+ if (typeof raw === 'string')
219
+ return { kind: 'ok', value: raw };
220
+ if (typeof raw === 'number' || typeof raw === 'boolean') {
221
+ return { kind: 'ok', value: String(raw) };
222
+ }
223
+ return { kind: 'error', reason: `expected string, got ${describe(raw)}` };
224
+ }
225
+ case 'number': {
226
+ if (typeof raw === 'number' && Number.isFinite(raw))
227
+ return { kind: 'ok', value: raw };
228
+ if (typeof raw === 'string' && raw.trim() !== '') {
229
+ const n = Number(raw);
230
+ if (Number.isFinite(n))
231
+ return { kind: 'ok', value: n };
232
+ }
233
+ return { kind: 'error', reason: `expected number, got ${describe(raw)}` };
234
+ }
235
+ case 'boolean': {
236
+ if (typeof raw === 'boolean')
237
+ return { kind: 'ok', value: raw };
238
+ if (raw === 'true' || raw === 'false')
239
+ return { kind: 'ok', value: raw === 'true' };
240
+ return { kind: 'error', reason: `expected boolean, got ${describe(raw)}` };
241
+ }
242
+ case 'enum': {
243
+ const allowed = port.enum ?? [];
244
+ if (allowed.length === 0) {
245
+ return { kind: 'error', reason: 'enum port declared without "enum" values' };
246
+ }
247
+ const asStr = typeof raw === 'string' ? raw : String(raw);
248
+ if (!allowed.includes(asStr)) {
249
+ return {
250
+ kind: 'error',
251
+ reason: `value ${JSON.stringify(raw)} not in enum [${allowed.map((v) => JSON.stringify(v)).join(', ')}]`,
252
+ };
253
+ }
254
+ return { kind: 'ok', value: asStr };
255
+ }
256
+ case 'json':
257
+ // 'json' accepts anything that survives JSON round-trip. We don't
258
+ // validate deeply — users opt into `json` precisely because they
259
+ // want a free-form payload.
260
+ return { kind: 'ok', value: raw };
261
+ default: {
262
+ // Exhaustiveness — TypeScript won't let us reach here unless a
263
+ // new PortType is added without updating this switch. The return
264
+ // satisfies the type checker; in practice the default branch is
265
+ // dead code.
266
+ const _exhaustive = port.type;
267
+ void _exhaustive;
268
+ return { kind: 'error', reason: `unknown port type "${String(port.type)}"` };
269
+ }
270
+ }
271
+ }
272
+ function describe(v) {
273
+ if (v === null)
274
+ return 'null';
275
+ if (Array.isArray(v))
276
+ return 'array';
277
+ return typeof v;
278
+ }
279
+ /**
280
+ * Extract declared outputs from a terminated task's output streams.
281
+ *
282
+ * Strategy (v1 — intentionally dumb but predictable):
283
+ * 1. Prefer `normalizedOutput` when provided (AI drivers populate this
284
+ * with the canonical assistant message; it's much cleaner than raw
285
+ * stdout, which often has JSONL event dumps). Fall back to stdout
286
+ * otherwise.
287
+ * 2. Scan from the end for the first non-empty line. If it parses as a
288
+ * JSON object, use that as the source record.
289
+ * 3. If (2) fails, try parsing the entire source as JSON (supports
290
+ * commands that pretty-print with line breaks).
291
+ * 4. For each declared output port, read the matching key and coerce
292
+ * to the declared type. Coercion failures produce a diagnostic and
293
+ * the port is absent from `outputs` (treated as missing downstream).
294
+ *
295
+ * When no declared outputs are present this returns an empty `outputs`
296
+ * map and null diagnostic — the engine interprets that as "task has no
297
+ * port contract".
298
+ */
299
+ export function extractTaskOutputs(ports, stdout, normalizedOutput) {
300
+ const decl = ports?.outputs;
301
+ if (!decl || decl.length === 0) {
302
+ return { outputs: {}, diagnostic: null };
303
+ }
304
+ const source = (normalizedOutput ?? '').length > 0 ? normalizedOutput : stdout;
305
+ const record = parseJsonTail(source);
306
+ if (record === null) {
307
+ return {
308
+ outputs: {},
309
+ diagnostic: 'outputs: could not find a final-line JSON object in task output — declared outputs are unresolved',
310
+ };
311
+ }
312
+ const outputs = {};
313
+ const warnings = [];
314
+ for (const port of decl) {
315
+ if (!(port.name in record)) {
316
+ warnings.push(`missing key "${port.name}"`);
317
+ continue;
318
+ }
319
+ const coerced = coerceValue(port, record[port.name]);
320
+ if (coerced.kind === 'error') {
321
+ warnings.push(`"${port.name}": ${coerced.reason}`);
322
+ continue;
323
+ }
324
+ outputs[port.name] = coerced.value;
325
+ }
326
+ const diagnostic = warnings.length > 0 ? `outputs: ${warnings.join('; ')}` : null;
327
+ return { outputs, diagnostic };
328
+ }
329
+ /**
330
+ * Find the last non-empty line that parses as a JSON object. Returns
331
+ * null when no such line exists. Also tries the whole source as a
332
+ * fallback — covers pretty-printed JSON that spans multiple lines.
333
+ */
334
+ function parseJsonTail(source) {
335
+ const lines = source.split(/\r?\n/);
336
+ for (let i = lines.length - 1; i >= 0; i--) {
337
+ const line = lines[i].trim();
338
+ if (!line)
339
+ continue;
340
+ const parsed = safeParseJson(line);
341
+ if (parsed !== null)
342
+ return parsed;
343
+ // First non-empty line from the tail — if it didn't parse, fall through
344
+ // to the whole-source attempt below rather than scanning further up
345
+ // (otherwise a prior human-readable line would be silently picked up
346
+ // if it happened to contain `{...}` fragments).
347
+ break;
348
+ }
349
+ return safeParseJson(source.trim());
350
+ }
351
+ function safeParseJson(candidate) {
352
+ if (!candidate.startsWith('{'))
353
+ return null;
354
+ try {
355
+ const parsed = JSON.parse(candidate);
356
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
357
+ return parsed;
358
+ }
359
+ }
360
+ catch {
361
+ /* not JSON */
362
+ }
363
+ return null;
364
+ }
365
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { PromptDocument, PromptContextBlock } from './types';
1
+ import type { PortDef, PromptContextBlock, PromptDocument } from './types';
2
2
  /**
3
3
  * Build a fresh `PromptDocument` from a raw task string.
4
4
  * Middlewares receive this from the engine and push context blocks onto
@@ -33,4 +33,38 @@ export declare function serializePromptDocument(doc: PromptDocument): string;
33
33
  * appended to `contexts`, preserving immutability of `doc`.
34
34
  */
35
35
  export declare function appendContext(doc: PromptDocument, block: PromptContextBlock): PromptDocument;
36
+ /**
37
+ * Helper: return a new document with the given block PREPENDED. The
38
+ * engine uses this to place port-related context blocks (`[Inputs]`,
39
+ * `[Output Format]`) at the top of the document so middlewares that
40
+ * assemble retrieval context against the task's inputs see them.
41
+ */
42
+ export declare function prependContext(doc: PromptDocument, block: PromptContextBlock): PromptDocument;
43
+ /**
44
+ * Build an `[Inputs]` context block from a map of resolved port inputs.
45
+ * Each input is rendered on its own line as `name: <value>` with an
46
+ * optional trailing `# <description>` comment so the model has both the
47
+ * value and the reason it matters.
48
+ *
49
+ * The block is *only* useful for AI tasks; command tasks consume inputs
50
+ * through `{{inputs.X}}` substitution in their command line and do not
51
+ * need this context.
52
+ *
53
+ * Returns null when there are no inputs to render — callers can forward
54
+ * that nullish value to `prependContext` via an `if (block)` check so
55
+ * empty-input tasks don't grow a noise block in their prompt.
56
+ */
57
+ export declare function renderInputsBlock(inputsDecl: readonly PortDef[] | undefined, values: Readonly<Record<string, unknown>>): PromptContextBlock | null;
58
+ /**
59
+ * Build an `[Output Format]` context block from a task's declared output
60
+ * ports. The block instructs the model to emit a final-line JSON object
61
+ * matching the declared schema so `extractTaskOutputs` can pick it up
62
+ * without fragile heuristics. Returns null when the task declares no
63
+ * outputs.
64
+ *
65
+ * The instruction is deliberately short and explicit — a terse "emit
66
+ * this object as JSON on the final line" beats a long schema dump
67
+ * because shorter prompts compose better with downstream middlewares.
68
+ */
69
+ export declare function renderOutputSchemaBlock(outputsDecl: readonly PortDef[] | undefined): PromptContextBlock | null;
36
70
  //# sourceMappingURL=prompt-doc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-doc.d.ts","sourceRoot":"","sources":["../src/prompt-doc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAErE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAInE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,kBAAkB,GACxB,cAAc,CAEhB"}
1
+ {"version":3,"file":"prompt-doc.d.ts","sourceRoot":"","sources":["../src/prompt-doc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAErE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAInE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,kBAAkB,GACxB,cAAc,CAEhB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,kBAAkB,GACxB,cAAc,CAEhB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,EAC1C,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,kBAAkB,GAAG,IAAI,CAY3B;AAaD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAC1C,kBAAkB,GAAG,IAAI,CAsB3B"}
@@ -41,4 +41,114 @@ export function serializePromptDocument(doc) {
41
41
  export function appendContext(doc, block) {
42
42
  return { contexts: [...doc.contexts, block], task: doc.task };
43
43
  }
44
+ /**
45
+ * Helper: return a new document with the given block PREPENDED. The
46
+ * engine uses this to place port-related context blocks (`[Inputs]`,
47
+ * `[Output Format]`) at the top of the document so middlewares that
48
+ * assemble retrieval context against the task's inputs see them.
49
+ */
50
+ export function prependContext(doc, block) {
51
+ return { contexts: [block, ...doc.contexts], task: doc.task };
52
+ }
53
+ /**
54
+ * Build an `[Inputs]` context block from a map of resolved port inputs.
55
+ * Each input is rendered on its own line as `name: <value>` with an
56
+ * optional trailing `# <description>` comment so the model has both the
57
+ * value and the reason it matters.
58
+ *
59
+ * The block is *only* useful for AI tasks; command tasks consume inputs
60
+ * through `{{inputs.X}}` substitution in their command line and do not
61
+ * need this context.
62
+ *
63
+ * Returns null when there are no inputs to render — callers can forward
64
+ * that nullish value to `prependContext` via an `if (block)` check so
65
+ * empty-input tasks don't grow a noise block in their prompt.
66
+ */
67
+ export function renderInputsBlock(inputsDecl, values) {
68
+ if (!inputsDecl || inputsDecl.length === 0)
69
+ return null;
70
+ const lines = [];
71
+ for (const port of inputsDecl) {
72
+ if (!(port.name in values))
73
+ continue;
74
+ const raw = values[port.name];
75
+ const rendered = renderInputValue(raw);
76
+ const descr = port.description?.trim();
77
+ lines.push(descr ? `${port.name}: ${rendered} # ${descr}` : `${port.name}: ${rendered}`);
78
+ }
79
+ if (lines.length === 0)
80
+ return null;
81
+ return { label: 'Inputs', content: lines.join('\n') };
82
+ }
83
+ function renderInputValue(value) {
84
+ if (value === null || value === undefined)
85
+ return '';
86
+ if (typeof value === 'string')
87
+ return JSON.stringify(value);
88
+ if (typeof value === 'number' || typeof value === 'boolean')
89
+ return String(value);
90
+ try {
91
+ return JSON.stringify(value);
92
+ }
93
+ catch {
94
+ return String(value);
95
+ }
96
+ }
97
+ /**
98
+ * Build an `[Output Format]` context block from a task's declared output
99
+ * ports. The block instructs the model to emit a final-line JSON object
100
+ * matching the declared schema so `extractTaskOutputs` can pick it up
101
+ * without fragile heuristics. Returns null when the task declares no
102
+ * outputs.
103
+ *
104
+ * The instruction is deliberately short and explicit — a terse "emit
105
+ * this object as JSON on the final line" beats a long schema dump
106
+ * because shorter prompts compose better with downstream middlewares.
107
+ */
108
+ export function renderOutputSchemaBlock(outputsDecl) {
109
+ if (!outputsDecl || outputsDecl.length === 0)
110
+ return null;
111
+ const lines = [];
112
+ lines.push('After your response, emit a single JSON object on the FINAL line with these keys:');
113
+ for (const port of outputsDecl) {
114
+ const descr = port.description?.trim();
115
+ const enumHint = port.type === 'enum' && port.enum?.length
116
+ ? ` (one of: ${port.enum.map((v) => JSON.stringify(v)).join(', ')})`
117
+ : '';
118
+ lines.push(descr
119
+ ? ` - ${port.name} (${port.type}${enumHint}): ${descr}`
120
+ : ` - ${port.name} (${port.type}${enumHint})`);
121
+ }
122
+ const example = buildExampleObject(outputsDecl);
123
+ lines.push('');
124
+ lines.push(`Example final line: ${JSON.stringify(example)}`);
125
+ return { label: 'Output Format', content: lines.join('\n') };
126
+ }
127
+ function buildExampleObject(outputsDecl) {
128
+ const example = {};
129
+ for (const port of outputsDecl) {
130
+ if (port.default !== undefined) {
131
+ example[port.name] = port.default;
132
+ continue;
133
+ }
134
+ switch (port.type) {
135
+ case 'string':
136
+ example[port.name] = '...';
137
+ break;
138
+ case 'number':
139
+ example[port.name] = 0;
140
+ break;
141
+ case 'boolean':
142
+ example[port.name] = false;
143
+ break;
144
+ case 'enum':
145
+ example[port.name] = port.enum?.[0] ?? '...';
146
+ break;
147
+ case 'json':
148
+ default:
149
+ example[port.name] = null;
150
+ }
151
+ }
152
+ return example;
153
+ }
44
154
  //# sourceMappingURL=prompt-doc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-doc.js","sourceRoot":"","sources":["../src/prompt-doc.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAmB;IACzD,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC;IAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACrE,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAmB,EACnB,KAAyB;IAEzB,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"prompt-doc.js","sourceRoot":"","sources":["../src/prompt-doc.ts"],"names":[],"mappings":"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"}
package/dist/runner.d.ts CHANGED
@@ -2,6 +2,23 @@ import type { SpawnSpec, DriverPlugin, TaskResult } from './types';
2
2
  export interface RunOptions {
3
3
  readonly timeoutMs?: number;
4
4
  readonly signal?: AbortSignal;
5
+ /**
6
+ * If set, stream the child's stdout to this file path as it arrives. The
7
+ * returned `TaskResult.stdout` is still a bounded in-memory tail
8
+ * (`maxStdoutTailBytes`) — callers that need the full output should read
9
+ * from the returned `stdoutPath`. Parent directories are created as needed.
10
+ */
11
+ readonly stdoutPath?: string;
12
+ /** Symmetric to `stdoutPath` for stderr. */
13
+ readonly stderrPath?: string;
14
+ /**
15
+ * Cap on bytes retained in memory for the returned `TaskResult.stdout`
16
+ * string. Defaults to `DEFAULT_STDOUT_TAIL_BYTES`. Bytes beyond this cap
17
+ * from the HEAD of the stream are dropped from the in-memory string; the
18
+ * on-disk file (if `stdoutPath` is set) is still the full output.
19
+ */
20
+ readonly maxStdoutTailBytes?: number;
21
+ readonly maxStderrTailBytes?: number;
5
22
  }
6
23
  /**
7
24
  * R2: Validate a SpawnSpec returned by a third-party driver. Returns null on
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAiCnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAoJD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoClF;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CAiNrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CAMrB"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA4CnE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AA4RD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoClF;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,YAAY,GAAG,IAAI,EAC3B,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CA0OrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,UAAU,CAAC,CAMrB"}