@tagma/sdk 0.6.5 → 0.6.7

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 (43) hide show
  1. package/README.md +66 -1
  2. package/package.json +2 -2
  3. package/src/engine-ports.test.ts +1 -1
  4. package/dist/dag.test.d.ts +0 -2
  5. package/dist/dag.test.d.ts.map +0 -1
  6. package/dist/dag.test.js +0 -42
  7. package/dist/dag.test.js.map +0 -1
  8. package/dist/engine-ports.test.d.ts +0 -2
  9. package/dist/engine-ports.test.d.ts.map +0 -1
  10. package/dist/engine-ports.test.js +0 -378
  11. package/dist/engine-ports.test.js.map +0 -1
  12. package/dist/plugin-registry.test.d.ts +0 -2
  13. package/dist/plugin-registry.test.d.ts.map +0 -1
  14. package/dist/plugin-registry.test.js +0 -188
  15. package/dist/plugin-registry.test.js.map +0 -1
  16. package/dist/ports.test.d.ts +0 -2
  17. package/dist/ports.test.d.ts.map +0 -1
  18. package/dist/ports.test.js +0 -262
  19. package/dist/ports.test.js.map +0 -1
  20. package/dist/prompt-doc.test.d.ts +0 -2
  21. package/dist/prompt-doc.test.d.ts.map +0 -1
  22. package/dist/prompt-doc.test.js +0 -145
  23. package/dist/prompt-doc.test.js.map +0 -1
  24. package/dist/runner.test.d.ts +0 -2
  25. package/dist/runner.test.d.ts.map +0 -1
  26. package/dist/runner.test.js +0 -119
  27. package/dist/runner.test.js.map +0 -1
  28. package/dist/schema-ports.test.d.ts +0 -2
  29. package/dist/schema-ports.test.d.ts.map +0 -1
  30. package/dist/schema-ports.test.js +0 -219
  31. package/dist/schema-ports.test.js.map +0 -1
  32. package/dist/schema.test.d.ts +0 -2
  33. package/dist/schema.test.d.ts.map +0 -1
  34. package/dist/schema.test.js +0 -94
  35. package/dist/schema.test.js.map +0 -1
  36. package/dist/task-ref.test.d.ts +0 -2
  37. package/dist/task-ref.test.d.ts.map +0 -1
  38. package/dist/task-ref.test.js +0 -364
  39. package/dist/task-ref.test.js.map +0 -1
  40. package/dist/validate-raw-ports.test.d.ts +0 -2
  41. package/dist/validate-raw-ports.test.d.ts.map +0 -1
  42. package/dist/validate-raw-ports.test.js +0 -157
  43. package/dist/validate-raw-ports.test.js.map +0 -1
@@ -1,188 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { PluginRegistry, defaultRegistry } from './registry';
3
- import { bootstrapBuiltins } from './bootstrap';
4
- import { runPipeline } from './engine';
5
- import { mkdtempSync, rmSync } from 'node:fs';
6
- import { tmpdir } from 'node:os';
7
- import { join } from 'node:path';
8
- function makeDriver(name, marker) {
9
- return {
10
- name,
11
- capabilities: { sessionResume: false, systemPrompt: false, outputFormat: false },
12
- async buildCommand() {
13
- marker.push(`buildCommand:${name}`);
14
- return { args: ['echo', 'noop'] };
15
- },
16
- };
17
- }
18
- function makeTrigger(name, marker) {
19
- return {
20
- name,
21
- async watch() {
22
- marker.push(`watch:${name}`);
23
- },
24
- };
25
- }
26
- describe('PluginRegistry — instance isolation', () => {
27
- test('two registries do not share drivers registered under the same type', () => {
28
- const regA = new PluginRegistry();
29
- const regB = new PluginRegistry();
30
- const markerA = [];
31
- const markerB = [];
32
- regA.registerPlugin('drivers', 'mock', makeDriver('mockA', markerA));
33
- regB.registerPlugin('drivers', 'mock', makeDriver('mockB', markerB));
34
- expect(regA.getHandler('drivers', 'mock').name).toBe('mockA');
35
- expect(regB.getHandler('drivers', 'mock').name).toBe('mockB');
36
- expect(regA.hasHandler('drivers', 'mock')).toBe(true);
37
- expect(regB.hasHandler('drivers', 'mock')).toBe(true);
38
- expect(regA.hasHandler('triggers', 'mock')).toBe(false);
39
- });
40
- test('unregistering in one registry does not affect the other', () => {
41
- const regA = new PluginRegistry();
42
- const regB = new PluginRegistry();
43
- regA.registerPlugin('drivers', 'mock', makeDriver('mockA', []));
44
- regB.registerPlugin('drivers', 'mock', makeDriver('mockB', []));
45
- expect(regA.unregisterPlugin('drivers', 'mock')).toBe(true);
46
- expect(regA.hasHandler('drivers', 'mock')).toBe(false);
47
- expect(regB.hasHandler('drivers', 'mock')).toBe(true);
48
- });
49
- test('listRegistered is scoped per instance', () => {
50
- const regA = new PluginRegistry();
51
- const regB = new PluginRegistry();
52
- regA.registerPlugin('triggers', 'a-only', makeTrigger('a-only', []));
53
- regB.registerPlugin('triggers', 'b-only', makeTrigger('b-only', []));
54
- expect(regA.listRegistered('triggers')).toEqual(['a-only']);
55
- expect(regB.listRegistered('triggers')).toEqual(['b-only']);
56
- });
57
- test('registering the same instance twice returns unchanged', () => {
58
- const reg = new PluginRegistry();
59
- const driver = makeDriver('same', []);
60
- expect(reg.registerPlugin('drivers', 'mock', driver)).toBe('registered');
61
- expect(reg.registerPlugin('drivers', 'mock', driver)).toBe('unchanged');
62
- });
63
- test('replacing with a different handler returns replaced', () => {
64
- const reg = new PluginRegistry();
65
- expect(reg.registerPlugin('drivers', 'mock', makeDriver('one', []))).toBe('registered');
66
- expect(reg.registerPlugin('drivers', 'mock', makeDriver('two', []))).toBe('replaced');
67
- expect(reg.getHandler('drivers', 'mock').name).toBe('two');
68
- });
69
- test('bootstrapBuiltins(target) populates a specific instance without touching the default', () => {
70
- const fresh = new PluginRegistry();
71
- expect(fresh.hasHandler('drivers', 'opencode')).toBe(false);
72
- bootstrapBuiltins(fresh);
73
- expect(fresh.hasHandler('drivers', 'opencode')).toBe(true);
74
- expect(fresh.hasHandler('triggers', 'file')).toBe(true);
75
- expect(fresh.hasHandler('triggers', 'manual')).toBe(true);
76
- expect(fresh.hasHandler('completions', 'exit_code')).toBe(true);
77
- expect(fresh.hasHandler('middlewares', 'static_context')).toBe(true);
78
- // Default registry's state is independent of `fresh` — if the default
79
- // happens to have opencode (because another test bootstrapped it), that
80
- // is fine; the guarantee is that `fresh.unregister` does not leak.
81
- fresh.unregisterPlugin('drivers', 'opencode');
82
- expect(fresh.hasHandler('drivers', 'opencode')).toBe(false);
83
- });
84
- });
85
- describe('PluginRegistry — validation', () => {
86
- test('rejects unknown category', () => {
87
- const reg = new PluginRegistry();
88
- expect(() => reg.registerPlugin('nope', 'x', makeDriver('x', []))).toThrow(/Unknown plugin category/);
89
- });
90
- test('rejects driver missing buildCommand', () => {
91
- const reg = new PluginRegistry();
92
- expect(() => reg.registerPlugin('drivers', 'broken',
93
- // deliberately bad: no buildCommand
94
- { name: 'broken', capabilities: { sessionResume: false, systemPrompt: false, outputFormat: false } })).toThrow(/must export buildCommand/);
95
- });
96
- test('rejects handler with missing name', () => {
97
- const reg = new PluginRegistry();
98
- expect(() => reg.registerPlugin('drivers', 'x',
99
- // deliberately bad: no name
100
- { capabilities: { sessionResume: false, systemPrompt: false, outputFormat: false }, buildCommand: async () => ({ args: [] }) })).toThrow(/non-empty "name"/);
101
- });
102
- });
103
- describe('runPipeline — options.registry isolation', () => {
104
- test('concurrent runs with different registries see their own drivers', async () => {
105
- const regA = new PluginRegistry();
106
- const regB = new PluginRegistry();
107
- const seenA = [];
108
- const seenB = [];
109
- bootstrapBuiltins(regA);
110
- bootstrapBuiltins(regB);
111
- regA.registerPlugin('drivers', 'mock', makeDriver('mockA', seenA));
112
- regB.registerPlugin('drivers', 'mock', makeDriver('mockB', seenB));
113
- // Command-only pipeline exercises the preflight path (which uses the
114
- // registry) plus the run-loop path without requiring a real driver
115
- // invocation. We verify isolation by asserting that preflight with a
116
- // registry missing `mock` rejects, while the matching registry accepts.
117
- const config = {
118
- name: 'isolation-test',
119
- tracks: [
120
- {
121
- id: 't',
122
- name: 'T',
123
- tasks: [{ id: 'only', name: 'only', command: 'echo hi' }],
124
- },
125
- ],
126
- };
127
- const tmpA = mkdtempSync(join(tmpdir(), 'tagma-regA-'));
128
- const tmpB = mkdtempSync(join(tmpdir(), 'tagma-regB-'));
129
- try {
130
- const [resA, resB] = await Promise.all([
131
- runPipeline(config, tmpA, { registry: regA, skipPluginLoading: true }),
132
- runPipeline(config, tmpB, { registry: regB, skipPluginLoading: true }),
133
- ]);
134
- expect(resA.success).toBe(true);
135
- expect(resB.success).toBe(true);
136
- expect(resA.runId).not.toBe(resB.runId);
137
- }
138
- finally {
139
- rmSync(tmpA, { recursive: true, force: true });
140
- rmSync(tmpB, { recursive: true, force: true });
141
- }
142
- });
143
- test('preflight fails when referenced driver is missing from the passed registry', async () => {
144
- const regNoOpencode = new PluginRegistry();
145
- // Deliberately do NOT bootstrap builtins — opencode is not registered.
146
- const config = {
147
- name: 'preflight-miss',
148
- tracks: [
149
- {
150
- id: 't',
151
- name: 'T',
152
- tasks: [{ id: 'x', name: 'x', prompt: 'hello' }],
153
- },
154
- ],
155
- };
156
- const tmp = mkdtempSync(join(tmpdir(), 'tagma-miss-'));
157
- try {
158
- await expect(runPipeline(config, tmp, { registry: regNoOpencode, skipPluginLoading: true })).rejects.toThrow(/driver "opencode" not registered/);
159
- }
160
- finally {
161
- rmSync(tmp, { recursive: true, force: true });
162
- }
163
- });
164
- test('omitting options.registry falls back to defaultRegistry', async () => {
165
- // bootstrapBuiltins into default happens in most host callers; do it
166
- // explicitly here so the test is independent of module-load order.
167
- bootstrapBuiltins(defaultRegistry);
168
- const config = {
169
- name: 'default-fallback',
170
- tracks: [
171
- {
172
- id: 't',
173
- name: 'T',
174
- tasks: [{ id: 'only', name: 'only', command: 'echo hi' }],
175
- },
176
- ],
177
- };
178
- const tmp = mkdtempSync(join(tmpdir(), 'tagma-default-'));
179
- try {
180
- const res = await runPipeline(config, tmp, { skipPluginLoading: true });
181
- expect(res.success).toBe(true);
182
- }
183
- finally {
184
- rmSync(tmp, { recursive: true, force: true });
185
- }
186
- });
187
- });
188
- //# sourceMappingURL=plugin-registry.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin-registry.test.js","sourceRoot":"","sources":["../src/plugin-registry.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=ports.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ports.test.d.ts","sourceRoot":"","sources":["../src/ports.test.ts"],"names":[],"mappings":""}
@@ -1,262 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { extractInputReferences, extractTaskOutputs, resolveTaskInputs, substituteInputs, } from './ports';
3
- const PERMS = { read: true, write: false, execute: false };
4
- function task(overrides) {
5
- return {
6
- name: overrides.id,
7
- permissions: PERMS,
8
- ...overrides,
9
- };
10
- }
11
- // ─── substituteInputs ────────────────────────────────────────────────
12
- describe('substituteInputs', () => {
13
- test('replaces single placeholder with string value', () => {
14
- const { text, unresolved } = substituteInputs('hello {{inputs.name}}', { name: 'world' });
15
- expect(text).toBe('hello world');
16
- expect(unresolved).toEqual([]);
17
- });
18
- test('allows optional whitespace inside braces', () => {
19
- const { text } = substituteInputs('{{ inputs.name }} / {{inputs.name}}', { name: 'x' });
20
- expect(text).toBe('x / x');
21
- });
22
- test('stringifies number / boolean values verbatim', () => {
23
- const { text } = substituteInputs('n={{inputs.n}} b={{inputs.b}}', { n: 42, b: true });
24
- expect(text).toBe('n=42 b=true');
25
- });
26
- test('JSON-stringifies object values', () => {
27
- const { text } = substituteInputs('payload={{inputs.p}}', {
28
- p: { a: 1, b: 'x' },
29
- });
30
- expect(text).toBe('payload={"a":1,"b":"x"}');
31
- });
32
- test('renders unknown placeholder empty and reports it', () => {
33
- const { text, unresolved } = substituteInputs('hello {{inputs.missing}}', {});
34
- expect(text).toBe('hello ');
35
- expect(unresolved).toEqual(['missing']);
36
- });
37
- test('renders null / undefined as empty and reports', () => {
38
- const { text, unresolved } = substituteInputs('a={{inputs.a}} b={{inputs.b}}', {
39
- a: null,
40
- b: undefined,
41
- });
42
- expect(text).toBe('a= b=');
43
- expect([...unresolved].sort()).toEqual(['a', 'b']);
44
- });
45
- test('leaves malformed placeholders alone', () => {
46
- const { text } = substituteInputs('{{inputs.a.b}} {{inputs.}}', { a: 'x' });
47
- expect(text).toBe('{{inputs.a.b}} {{inputs.}}');
48
- });
49
- test('handles circular objects without throwing', () => {
50
- const obj = { self: null };
51
- obj.self = obj;
52
- const { text, unresolved } = substituteInputs('{{inputs.x}}', { x: obj });
53
- expect(text).toBe('');
54
- expect(unresolved).toEqual(['x']);
55
- });
56
- });
57
- describe('extractInputReferences', () => {
58
- test('returns unique referenced names', () => {
59
- const refs = extractInputReferences('get {{inputs.city}} for id={{inputs.id}} and {{inputs.city}} again');
60
- expect(refs.sort()).toEqual(['city', 'id']);
61
- });
62
- test('returns empty for text without placeholders', () => {
63
- expect(extractInputReferences('no placeholders here')).toEqual([]);
64
- });
65
- });
66
- // ─── resolveTaskInputs ────────────────────────────────────────────────
67
- const cityPort = { name: 'city', type: 'string', required: true };
68
- const idPort = { name: 'id', type: 'number', required: true };
69
- describe('resolveTaskInputs', () => {
70
- test('no declared inputs → ready with empty map', () => {
71
- const t = task({ id: 'downstream', command: 'echo' });
72
- const res = resolveTaskInputs(t, new Map(), []);
73
- expect(res).toEqual({ kind: 'ready', inputs: {}, missingOptional: [] });
74
- });
75
- test('matches inputs by name across upstream outputs', () => {
76
- const t = task({
77
- id: 'downstream',
78
- command: 'echo',
79
- ports: { inputs: [cityPort, idPort] },
80
- });
81
- const upstream = new Map([
82
- ['t.prompt', { city: 'Shanghai' }],
83
- ['t.other', { id: 42 }],
84
- ]);
85
- const res = resolveTaskInputs(t, upstream, ['t.prompt', 't.other']);
86
- expect(res.kind).toBe('ready');
87
- if (res.kind !== 'ready')
88
- return;
89
- expect(res.inputs).toEqual({ city: 'Shanghai', id: 42 });
90
- });
91
- test('required missing blocks with a readable reason', () => {
92
- const t = task({
93
- id: 'downstream',
94
- command: 'echo',
95
- ports: { inputs: [cityPort, idPort] },
96
- });
97
- const res = resolveTaskInputs(t, new Map(), ['t.x']);
98
- expect(res.kind).toBe('blocked');
99
- if (res.kind !== 'blocked')
100
- return;
101
- expect([...res.missingRequired].sort()).toEqual(['city', 'id']);
102
- expect(res.reason).toMatch(/city.*id|id.*city/);
103
- });
104
- test('optional missing yields ready but reports missingOptional', () => {
105
- const optional = { name: 'note', type: 'string' };
106
- const t = task({
107
- id: 'downstream',
108
- command: 'echo',
109
- ports: { inputs: [optional] },
110
- });
111
- const res = resolveTaskInputs(t, new Map(), []);
112
- expect(res.kind).toBe('ready');
113
- if (res.kind !== 'ready')
114
- return;
115
- expect(res.inputs).toEqual({});
116
- expect(res.missingOptional).toEqual(['note']);
117
- });
118
- test('applies default for missing optional', () => {
119
- const optional = { name: 'note', type: 'string', default: 'n/a' };
120
- const t = task({
121
- id: 'd',
122
- command: 'echo',
123
- ports: { inputs: [optional] },
124
- });
125
- const res = resolveTaskInputs(t, new Map(), []);
126
- expect(res.kind).toBe('ready');
127
- if (res.kind !== 'ready')
128
- return;
129
- expect(res.inputs).toEqual({ note: 'n/a' });
130
- });
131
- test('ambiguous multi-upstream match blocks unless disambiguated', () => {
132
- const t = task({
133
- id: 'd',
134
- command: 'echo',
135
- ports: { inputs: [cityPort] },
136
- });
137
- const upstream = new Map([
138
- ['t.a', { city: 'Shanghai' }],
139
- ['t.b', { city: 'Beijing' }],
140
- ]);
141
- const res = resolveTaskInputs(t, upstream, ['t.a', 't.b']);
142
- expect(res.kind).toBe('blocked');
143
- if (res.kind !== 'blocked')
144
- return;
145
- expect(res.ambiguous.length).toBe(1);
146
- expect(res.ambiguous[0].port).toBe('city');
147
- expect([...res.ambiguous[0].producers].sort()).toEqual(['t.a', 't.b']);
148
- });
149
- test('explicit fully-qualified "from" wins over name-match ambiguity', () => {
150
- const explicit = {
151
- name: 'city',
152
- type: 'string',
153
- required: true,
154
- from: 't.b.city',
155
- };
156
- const t = task({
157
- id: 'd',
158
- command: 'echo',
159
- ports: { inputs: [explicit] },
160
- });
161
- const upstream = new Map([
162
- ['t.a', { city: 'Shanghai' }],
163
- ['t.b', { city: 'Beijing' }],
164
- ]);
165
- const res = resolveTaskInputs(t, upstream, ['t.a', 't.b']);
166
- expect(res.kind).toBe('ready');
167
- if (res.kind !== 'ready')
168
- return;
169
- expect(res.inputs).toEqual({ city: 'Beijing' });
170
- });
171
- test('coerces numeric strings to number type', () => {
172
- const t = task({
173
- id: 'd',
174
- command: 'echo',
175
- ports: { inputs: [idPort] },
176
- });
177
- const upstream = new Map([['t.a', { id: '42' }]]);
178
- const res = resolveTaskInputs(t, upstream, ['t.a']);
179
- expect(res.kind).toBe('ready');
180
- if (res.kind !== 'ready')
181
- return;
182
- expect(res.inputs.id).toBe(42);
183
- });
184
- test('flags type-coercion failures as blocked', () => {
185
- const t = task({
186
- id: 'd',
187
- command: 'echo',
188
- ports: { inputs: [idPort] },
189
- });
190
- const upstream = new Map([['t.a', { id: 'nope' }]]);
191
- const res = resolveTaskInputs(t, upstream, ['t.a']);
192
- expect(res.kind).toBe('blocked');
193
- if (res.kind !== 'blocked')
194
- return;
195
- expect(res.typeErrors.length).toBe(1);
196
- expect(res.typeErrors[0].port).toBe('id');
197
- });
198
- test('enforces enum membership', () => {
199
- const colorPort = {
200
- name: 'color',
201
- type: 'enum',
202
- enum: ['red', 'green'],
203
- required: true,
204
- };
205
- const t = task({
206
- id: 'd',
207
- command: 'echo',
208
- ports: { inputs: [colorPort] },
209
- });
210
- const upstream = new Map([['t.a', { color: 'blue' }]]);
211
- const res = resolveTaskInputs(t, upstream, ['t.a']);
212
- expect(res.kind).toBe('blocked');
213
- if (res.kind !== 'blocked')
214
- return;
215
- expect(res.typeErrors[0].port).toBe('color');
216
- });
217
- });
218
- // ─── extractTaskOutputs ──────────────────────────────────────────────
219
- describe('extractTaskOutputs', () => {
220
- const outputs = [
221
- { name: 'city', type: 'string' },
222
- { name: 'temp', type: 'number' },
223
- ];
224
- test('no declared outputs → empty map, null diagnostic', () => {
225
- const r = extractTaskOutputs(undefined, 'anything', null);
226
- expect(r.outputs).toEqual({});
227
- expect(r.diagnostic).toBeNull();
228
- });
229
- test('parses last-line JSON object as source record', () => {
230
- const stdout = 'some log\nmore log\n{"city":"Shanghai","temp":23}\n';
231
- const r = extractTaskOutputs({ outputs }, stdout, null);
232
- expect(r.outputs).toEqual({ city: 'Shanghai', temp: 23 });
233
- expect(r.diagnostic).toBeNull();
234
- });
235
- test('falls back to whole-source JSON when last line is a closing brace', () => {
236
- const stdout = '{\n "city": "Shanghai",\n "temp": 23\n}\n';
237
- const r = extractTaskOutputs({ outputs }, stdout, null);
238
- expect(r.outputs).toEqual({ city: 'Shanghai', temp: 23 });
239
- });
240
- test('prefers normalizedOutput over stdout when provided', () => {
241
- const stdout = '{"city":"Wrong","temp":0}';
242
- const normalized = '{"city":"Shanghai","temp":23}';
243
- const r = extractTaskOutputs({ outputs }, stdout, normalized);
244
- expect(r.outputs).toEqual({ city: 'Shanghai', temp: 23 });
245
- });
246
- test('reports missing keys as diagnostic, keeps resolved keys', () => {
247
- const r = extractTaskOutputs({ outputs }, '{"city":"Shanghai"}', null);
248
- expect(r.outputs).toEqual({ city: 'Shanghai' });
249
- expect(r.diagnostic).toContain('missing key "temp"');
250
- });
251
- test('reports coercion failure and skips bad port', () => {
252
- const r = extractTaskOutputs({ outputs }, '{"city":"Shanghai","temp":"not-a-number"}', null);
253
- expect(r.outputs).toEqual({ city: 'Shanghai' });
254
- expect(r.diagnostic).toContain('"temp"');
255
- });
256
- test('reports diagnostic when no JSON can be parsed', () => {
257
- const r = extractTaskOutputs({ outputs }, 'plain text output\nnothing json\n', null);
258
- expect(r.outputs).toEqual({});
259
- expect(r.diagnostic).toContain('could not find a final-line JSON object');
260
- });
261
- });
262
- //# sourceMappingURL=ports.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ports.test.js","sourceRoot":"","sources":["../src/ports.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=prompt-doc.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prompt-doc.test.d.ts","sourceRoot":"","sources":["../src/prompt-doc.test.ts"],"names":[],"mappings":""}
@@ -1,145 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { appendContext, prependContext, promptDocumentFromString, renderInputsBlock, renderOutputSchemaBlock, serializePromptDocument, } from './prompt-doc';
3
- // ─── renderInputsBlock ────────────────────────────────────────────────
4
- describe('renderInputsBlock', () => {
5
- test('returns null when no inputs declared', () => {
6
- expect(renderInputsBlock(undefined, {})).toBeNull();
7
- expect(renderInputsBlock([], { any: 'x' })).toBeNull();
8
- });
9
- test('returns null when declared inputs have no resolved values', () => {
10
- const ports = [{ name: 'city', type: 'string' }];
11
- // values missing entirely — block is noise, omit it
12
- expect(renderInputsBlock(ports, {})).toBeNull();
13
- });
14
- test('renders name: value per declared input', () => {
15
- const ports = [
16
- { name: 'city', type: 'string' },
17
- { name: 'id', type: 'number' },
18
- ];
19
- const block = renderInputsBlock(ports, { city: 'Shanghai', id: 42 });
20
- expect(block).not.toBeNull();
21
- expect(block.label).toBe('Inputs');
22
- expect(block.content).toBe('city: "Shanghai"\nid: 42');
23
- });
24
- test('appends # description comment when provided', () => {
25
- const ports = [
26
- { name: 'city', type: 'string', description: 'Target city' },
27
- ];
28
- const block = renderInputsBlock(ports, { city: 'Shanghai' });
29
- expect(block.content).toBe('city: "Shanghai" # Target city');
30
- });
31
- test('preserves declaration order, not input-map iteration order', () => {
32
- const ports = [
33
- { name: 'b', type: 'string' },
34
- { name: 'a', type: 'string' },
35
- ];
36
- // Values object has 'a' first, 'b' second — block should still emit 'b' first.
37
- const block = renderInputsBlock(ports, { a: 'x', b: 'y' });
38
- expect(block.content).toBe('b: "y"\na: "x"');
39
- });
40
- test('skips ports whose values were not resolved', () => {
41
- const ports = [
42
- { name: 'a', type: 'string' },
43
- { name: 'b', type: 'string' },
44
- ];
45
- const block = renderInputsBlock(ports, { a: 'x' });
46
- expect(block.content).toBe('a: "x"');
47
- });
48
- test('JSON-encodes non-primitive values', () => {
49
- const ports = [{ name: 'payload', type: 'json' }];
50
- const block = renderInputsBlock(ports, { payload: { a: 1, b: [2, 3] } });
51
- expect(block.content).toBe('payload: {"a":1,"b":[2,3]}');
52
- });
53
- test('booleans render verbatim, not quoted', () => {
54
- const ports = [{ name: 'flag', type: 'boolean' }];
55
- const block = renderInputsBlock(ports, { flag: true });
56
- expect(block.content).toBe('flag: true');
57
- });
58
- });
59
- // ─── renderOutputSchemaBlock ──────────────────────────────────────────
60
- describe('renderOutputSchemaBlock', () => {
61
- test('returns null when no outputs declared', () => {
62
- expect(renderOutputSchemaBlock(undefined)).toBeNull();
63
- expect(renderOutputSchemaBlock([])).toBeNull();
64
- });
65
- test('instructs the model to emit final-line JSON', () => {
66
- const ports = [{ name: 'city', type: 'string' }];
67
- const block = renderOutputSchemaBlock(ports);
68
- expect(block.label).toBe('Output Format');
69
- expect(block.content).toMatch(/final line/i);
70
- });
71
- test('lists each port with its type', () => {
72
- const ports = [
73
- { name: 'city', type: 'string', description: 'Target city' },
74
- { name: 'temp', type: 'number' },
75
- ];
76
- const block = renderOutputSchemaBlock(ports);
77
- expect(block.content).toContain('- city (string): Target city');
78
- expect(block.content).toContain('- temp (number)');
79
- });
80
- test('includes enum values in the type hint', () => {
81
- const ports = [
82
- { name: 'color', type: 'enum', enum: ['red', 'green', 'blue'] },
83
- ];
84
- const block = renderOutputSchemaBlock(ports);
85
- expect(block.content).toContain('color (enum (one of: "red", "green", "blue"))');
86
- });
87
- test('example object uses declared defaults when present', () => {
88
- const ports = [
89
- { name: 'score', type: 'number', default: 0.5 },
90
- { name: 'note', type: 'string', default: 'n/a' },
91
- ];
92
- const block = renderOutputSchemaBlock(ports);
93
- // The example line is `Example final line: {"score":0.5,"note":"n/a"}`.
94
- expect(block.content).toContain('"score":0.5');
95
- expect(block.content).toContain('"note":"n/a"');
96
- });
97
- test('example uses type-appropriate placeholders when no default', () => {
98
- const ports = [
99
- { name: 's', type: 'string' },
100
- { name: 'n', type: 'number' },
101
- { name: 'b', type: 'boolean' },
102
- { name: 'j', type: 'json' },
103
- ];
104
- const block = renderOutputSchemaBlock(ports);
105
- expect(block.content).toContain('"s":"..."');
106
- expect(block.content).toContain('"n":0');
107
- expect(block.content).toContain('"b":false');
108
- expect(block.content).toContain('"j":null');
109
- });
110
- test('example uses first enum value when present', () => {
111
- const ports = [
112
- { name: 'tier', type: 'enum', enum: ['low', 'high'] },
113
- ];
114
- const block = renderOutputSchemaBlock(ports);
115
- expect(block.content).toContain('"tier":"low"');
116
- });
117
- });
118
- // ─── prependContext / appendContext ──────────────────────────────────
119
- describe('prependContext / appendContext', () => {
120
- const block = { label: 'X', content: 'x' };
121
- test('prependContext puts block at front without mutating input', () => {
122
- const doc = { contexts: [{ label: 'Y', content: 'y' }], task: 't' };
123
- const next = prependContext(doc, block);
124
- expect(next.contexts.map((c) => c.label)).toEqual(['X', 'Y']);
125
- // Original untouched — immutability is part of the contract for
126
- // middleware safety (the engine compares doc identity to detect
127
- // changes in some paths).
128
- expect(doc.contexts).toHaveLength(1);
129
- expect(doc.contexts[0].label).toBe('Y');
130
- expect(next.task).toBe('t');
131
- });
132
- test('appendContext puts block at end without mutating input', () => {
133
- const doc = { contexts: [{ label: 'Y', content: 'y' }], task: 't' };
134
- const next = appendContext(doc, block);
135
- expect(next.contexts.map((c) => c.label)).toEqual(['Y', 'X']);
136
- expect(doc.contexts).toHaveLength(1);
137
- });
138
- test('prepend + serialize produces [X] block above the task', () => {
139
- const doc = promptDocumentFromString('do the thing');
140
- const next = prependContext(doc, { label: 'Inputs', content: 'city: "Shanghai"' });
141
- const text = serializePromptDocument(next);
142
- expect(text).toBe('[Inputs]\ncity: "Shanghai"\n\ndo the thing');
143
- });
144
- });
145
- //# sourceMappingURL=prompt-doc.test.js.map