@contentful/experience-design-system-generation 2.35.1-dev-build-45d6c918.0 → 2.36.0
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/package.json +1 -1
- package/dist/src/agent-invoker.d.ts +25 -0
- package/dist/src/{services/agent/local-cli-agent-invoker.js → agent-invoker.js} +2 -2
- package/dist/src/agent-names.d.ts +0 -1
- package/dist/src/agent-names.js +0 -7
- package/dist/src/agent-runner.d.ts +144 -0
- package/dist/src/agent-runner.js +564 -0
- package/dist/src/index.d.ts +7 -19
- package/dist/src/index.js +6 -18
- package/dist/src/prompt-builder.d.ts +81 -0
- package/dist/src/prompt-builder.js +344 -0
- package/package.json +2 -2
- package/dist/src/controller/create-generation-endpoint.d.ts +0 -3
- package/dist/src/controller/create-generation-endpoint.js +0 -15
- package/dist/src/controller/helpers/validate-generation-request.d.ts +0 -2
- package/dist/src/controller/helpers/validate-generation-request.js +0 -18
- package/dist/src/orchestrator/execute-generation-orchestrator.d.ts +0 -6
- package/dist/src/orchestrator/execute-generation-orchestrator.js +0 -12
- package/dist/src/services/agent/helpers/build-agent-args.d.ts +0 -6
- package/dist/src/services/agent/helpers/build-agent-args.js +0 -58
- package/dist/src/services/agent/helpers/check-agent-auth.d.ts +0 -3
- package/dist/src/services/agent/helpers/check-agent-auth.js +0 -19
- package/dist/src/services/agent/helpers/check-claude-auth.d.ts +0 -2
- package/dist/src/services/agent/helpers/check-claude-auth.js +0 -37
- package/dist/src/services/agent/helpers/describe-agent-failure.d.ts +0 -2
- package/dist/src/services/agent/helpers/describe-agent-failure.js +0 -5
- package/dist/src/services/agent/helpers/extract-sentinel-output.d.ts +0 -1
- package/dist/src/services/agent/helpers/extract-sentinel-output.js +0 -16
- package/dist/src/services/agent/helpers/resolve-agent-binary.d.ts +0 -2
- package/dist/src/services/agent/helpers/resolve-agent-binary.js +0 -8
- package/dist/src/services/agent/local-cli-agent-invoker.d.ts +0 -3
- package/dist/src/services/agent/run-agent.d.ts +0 -15
- package/dist/src/services/agent/run-agent.js +0 -70
- package/dist/src/services/parsing/helpers/read-tool-call-objects.d.ts +0 -4
- package/dist/src/services/parsing/helpers/read-tool-call-objects.js +0 -54
- package/dist/src/services/parsing/parse-map-token-tool-calls.d.ts +0 -2
- package/dist/src/services/parsing/parse-map-token-tool-calls.js +0 -39
- package/dist/src/services/parsing/parse-prop-tool-calls.d.ts +0 -2
- package/dist/src/services/parsing/parse-prop-tool-calls.js +0 -91
- package/dist/src/services/parsing/parse-select-tool-calls.d.ts +0 -2
- package/dist/src/services/parsing/parse-select-tool-calls.js +0 -27
- package/dist/src/services/parsing/parse-token-tool-calls.d.ts +0 -2
- package/dist/src/services/parsing/parse-token-tool-calls.js +0 -39
- package/dist/src/services/prompts/build-prompt.d.ts +0 -4
- package/dist/src/services/prompts/build-prompt.js +0 -8
- package/dist/src/services/prompts/helpers/build-preamble.d.ts +0 -2
- package/dist/src/services/prompts/helpers/build-preamble.js +0 -48
- package/dist/src/services/prompts/helpers/build-skill-preambles.d.ts +0 -4
- package/dist/src/services/prompts/helpers/build-skill-preambles.js +0 -126
- package/dist/src/services/prompts/helpers/format-source-refs.d.ts +0 -2
- package/dist/src/services/prompts/helpers/format-source-refs.js +0 -31
- package/dist/src/services/prompts/helpers/format-token-candidates.d.ts +0 -8
- package/dist/src/services/prompts/helpers/format-token-candidates.js +0 -80
- package/dist/src/services/prompts/helpers/resolve-skill-file.d.ts +0 -4
- package/dist/src/services/prompts/helpers/resolve-skill-file.js +0 -49
- package/dist/src/types/agent.d.ts +0 -10
- package/dist/src/types/agent.js +0 -1
- package/dist/src/types/contract.d.ts +0 -24
- package/dist/src/types/contract.js +0 -1
- package/dist/src/types/invoker.d.ts +0 -17
- package/dist/src/types/invoker.js +0 -1
- package/dist/src/types/prompt.d.ts +0 -53
- package/dist/src/types/prompt.js +0 -1
- package/dist/src/types/tool-calls.d.ts +0 -91
- package/dist/src/types/tool-calls.js +0 -1
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
|
|
3
|
+
const VALID_SELECT_TOOL_NAMES = new Set(['select_component', 'reject_component']);
|
|
4
|
+
function findJsonObjectEnd(line) {
|
|
5
|
+
let depth = 0;
|
|
6
|
+
let inString = false;
|
|
7
|
+
let escaped = false;
|
|
8
|
+
for (let i = 0; i < line.length; i++) {
|
|
9
|
+
const ch = line[i];
|
|
10
|
+
if (escaped) {
|
|
11
|
+
escaped = false;
|
|
12
|
+
}
|
|
13
|
+
else if (ch === '\\' && inString) {
|
|
14
|
+
escaped = true;
|
|
15
|
+
}
|
|
16
|
+
else if (ch === '"') {
|
|
17
|
+
inString = !inString;
|
|
18
|
+
}
|
|
19
|
+
else if (!inString && ch === '{') {
|
|
20
|
+
depth++;
|
|
21
|
+
}
|
|
22
|
+
else if (!inString && ch === '}' && --depth === 0) {
|
|
23
|
+
return i;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return -1;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reads an agent's stdout into the tool-call objects it carries. Lines that do
|
|
30
|
+
* not start with `{` are the agent's prose and are skipped silently, as before.
|
|
31
|
+
*/
|
|
32
|
+
function readToolCallObjects(stdout) {
|
|
33
|
+
const objects = [];
|
|
34
|
+
const warnings = [];
|
|
35
|
+
for (const raw of stdout.split('\n')) {
|
|
36
|
+
const line = raw.trim();
|
|
37
|
+
if (!line.startsWith('{'))
|
|
38
|
+
continue;
|
|
39
|
+
const end = findJsonObjectEnd(line);
|
|
40
|
+
if (end === -1) {
|
|
41
|
+
warnings.push(`unparseable line: ${line.slice(0, 120)}`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(line.slice(0, end + 1));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
warnings.push(`unparseable line: ${line.slice(0, 120)}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const trailing = line.slice(end + 1);
|
|
53
|
+
if (trailing.trim()) {
|
|
54
|
+
warnings.push(`ignored trailing content after JSON: ${trailing.trim().slice(0, 120)}`);
|
|
55
|
+
}
|
|
56
|
+
if (typeof parsed === 'object' && parsed !== null && 'tool' in parsed) {
|
|
57
|
+
objects.push(parsed);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { objects, warnings };
|
|
61
|
+
}
|
|
62
|
+
export function parseSelectToolCallLines(stdout) {
|
|
63
|
+
const calls = [];
|
|
64
|
+
const { objects, warnings } = readToolCallObjects(stdout);
|
|
65
|
+
for (const rec of objects) {
|
|
66
|
+
if (!VALID_SELECT_TOOL_NAMES.has(rec.tool))
|
|
67
|
+
continue;
|
|
68
|
+
if (typeof rec.name !== 'string' || !rec.name) {
|
|
69
|
+
warnings.push(`${String(rec.tool)} missing name — skipped`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const call = {
|
|
73
|
+
tool: rec.tool,
|
|
74
|
+
name: rec.name,
|
|
75
|
+
};
|
|
76
|
+
if (typeof rec.reason === 'string')
|
|
77
|
+
call.reason = rec.reason;
|
|
78
|
+
if (typeof rec.confidence === 'number' && rec.confidence >= 1 && rec.confidence <= 5) {
|
|
79
|
+
if (call.tool === 'select_component') {
|
|
80
|
+
call.confidence = rec.confidence;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
call.confidence = rec.confidence;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
calls.push(call);
|
|
87
|
+
}
|
|
88
|
+
return { calls, warnings };
|
|
89
|
+
}
|
|
90
|
+
const VALID_TOOL_NAMES = new Set(['classify_prop', 'exclude_prop', 'classify_component', 'classify_slot']);
|
|
91
|
+
const VALID_TOKEN_TOOL_NAMES = new Set(['set_token', 'set_group']);
|
|
92
|
+
const VALID_CDF_TYPES = new Set(['string', 'richtext', 'media', 'enum', 'token', 'boolean']);
|
|
93
|
+
const VALID_CATEGORIES = new Set(['content', 'design', 'state']);
|
|
94
|
+
export function parseToolCallLines(stdout) {
|
|
95
|
+
const calls = [];
|
|
96
|
+
const { objects, warnings } = readToolCallObjects(stdout);
|
|
97
|
+
for (const rec of objects) {
|
|
98
|
+
if (!VALID_TOOL_NAMES.has(rec.tool)) {
|
|
99
|
+
warnings.push(`unknown tool: ${String(rec.tool)}`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const tool = rec.tool;
|
|
103
|
+
if (tool === 'classify_prop') {
|
|
104
|
+
if (typeof rec.prop !== 'string' || !rec.prop) {
|
|
105
|
+
warnings.push('classify_prop missing prop name — skipped');
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (typeof rec.cdf_type !== 'string' || !VALID_CDF_TYPES.has(rec.cdf_type)) {
|
|
109
|
+
warnings.push(`classify_prop '${rec.prop}': invalid cdf_type '${String(rec.cdf_type)}' — skipped`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (typeof rec.cdf_category !== 'string' || !VALID_CATEGORIES.has(rec.cdf_category)) {
|
|
113
|
+
warnings.push(`classify_prop '${rec.prop}': invalid cdf_category '${String(rec.cdf_category)}' — skipped`);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const call = {
|
|
117
|
+
tool: 'classify_prop',
|
|
118
|
+
prop: rec.prop,
|
|
119
|
+
cdf_type: rec.cdf_type,
|
|
120
|
+
cdf_category: rec.cdf_category,
|
|
121
|
+
};
|
|
122
|
+
if (Array.isArray(rec.values) && rec.values.every((v) => typeof v === 'string')) {
|
|
123
|
+
call.values = rec.values;
|
|
124
|
+
}
|
|
125
|
+
if (typeof rec.token_kind === 'string')
|
|
126
|
+
call.token_kind = rec.token_kind;
|
|
127
|
+
if (typeof rec.required === 'boolean')
|
|
128
|
+
call.required = rec.required;
|
|
129
|
+
if (typeof rec.description === 'string')
|
|
130
|
+
call.description = rec.description;
|
|
131
|
+
if (typeof rec.default === 'string' || typeof rec.default === 'boolean')
|
|
132
|
+
call.default = rec.default;
|
|
133
|
+
if (typeof rec.reason === 'string')
|
|
134
|
+
call.reason = rec.reason;
|
|
135
|
+
calls.push(call);
|
|
136
|
+
}
|
|
137
|
+
else if (tool === 'exclude_prop') {
|
|
138
|
+
if (typeof rec.prop !== 'string' || !rec.prop) {
|
|
139
|
+
warnings.push('exclude_prop missing prop name — skipped');
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
calls.push({
|
|
143
|
+
tool: 'exclude_prop',
|
|
144
|
+
prop: rec.prop,
|
|
145
|
+
reason: typeof rec.reason === 'string' ? rec.reason : '',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else if (tool === 'classify_component') {
|
|
149
|
+
const call = { tool: 'classify_component' };
|
|
150
|
+
if (typeof rec.description === 'string')
|
|
151
|
+
call.description = rec.description;
|
|
152
|
+
if (typeof rec.rationale === 'object' && rec.rationale !== null) {
|
|
153
|
+
const r = rec.rationale;
|
|
154
|
+
const rationale = {};
|
|
155
|
+
if (typeof r.description === 'string')
|
|
156
|
+
rationale.description = r.description;
|
|
157
|
+
if (typeof r.props === 'string')
|
|
158
|
+
rationale.props = r.props;
|
|
159
|
+
if (typeof r.slots === 'string')
|
|
160
|
+
rationale.slots = r.slots;
|
|
161
|
+
if (Object.keys(rationale).length > 0)
|
|
162
|
+
call.rationale = rationale;
|
|
163
|
+
}
|
|
164
|
+
calls.push(call);
|
|
165
|
+
}
|
|
166
|
+
else if (tool === 'classify_slot') {
|
|
167
|
+
if (typeof rec.slot !== 'string' || !rec.slot) {
|
|
168
|
+
warnings.push('classify_slot missing slot name — skipped');
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const call = { tool: 'classify_slot', slot: rec.slot };
|
|
172
|
+
if (typeof rec.required === 'boolean')
|
|
173
|
+
call.required = rec.required;
|
|
174
|
+
if (Array.isArray(rec.allowed_components) && rec.allowed_components.every((v) => typeof v === 'string')) {
|
|
175
|
+
call.allowed_components = rec.allowed_components;
|
|
176
|
+
}
|
|
177
|
+
if (typeof rec.description === 'string')
|
|
178
|
+
call.description = rec.description;
|
|
179
|
+
if (typeof rec.rationale === 'string')
|
|
180
|
+
call.rationale = rec.rationale;
|
|
181
|
+
calls.push(call);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { calls, warnings };
|
|
185
|
+
}
|
|
186
|
+
export function parseTokenToolCallLines(stdout) {
|
|
187
|
+
const calls = [];
|
|
188
|
+
const { objects, warnings } = readToolCallObjects(stdout);
|
|
189
|
+
for (const rec of objects) {
|
|
190
|
+
if (!VALID_TOKEN_TOOL_NAMES.has(rec.tool))
|
|
191
|
+
continue; // not a token call — skip silently
|
|
192
|
+
if (rec.tool === 'set_token') {
|
|
193
|
+
if (typeof rec.path !== 'string' || !rec.path) {
|
|
194
|
+
warnings.push('set_token missing path — skipped');
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (typeof rec.type !== 'string' || !rec.type) {
|
|
198
|
+
warnings.push(`set_token '${rec.path}': missing type — skipped`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (!('value' in rec)) {
|
|
202
|
+
warnings.push(`set_token '${rec.path}': missing value — skipped`);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const call = { tool: 'set_token', path: rec.path, type: rec.type, value: rec.value };
|
|
206
|
+
if (typeof rec.description === 'string')
|
|
207
|
+
call.description = rec.description;
|
|
208
|
+
calls.push(call);
|
|
209
|
+
}
|
|
210
|
+
else if (rec.tool === 'set_group') {
|
|
211
|
+
if (typeof rec.path !== 'string' || !rec.path) {
|
|
212
|
+
warnings.push('set_group missing path — skipped');
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const call = { tool: 'set_group', path: rec.path };
|
|
216
|
+
if (typeof rec.description === 'string')
|
|
217
|
+
call.description = rec.description;
|
|
218
|
+
calls.push(call);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return { calls, warnings };
|
|
222
|
+
}
|
|
223
|
+
function isStringArray(value) {
|
|
224
|
+
return Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
225
|
+
}
|
|
226
|
+
export function parseMapTokenPropToolCallLines(stdout) {
|
|
227
|
+
const calls = [];
|
|
228
|
+
const warnings = [];
|
|
229
|
+
for (const raw of stdout.split('\n')) {
|
|
230
|
+
const line = raw.trim();
|
|
231
|
+
if (!line.startsWith('{'))
|
|
232
|
+
continue;
|
|
233
|
+
let obj;
|
|
234
|
+
try {
|
|
235
|
+
obj = JSON.parse(line);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
warnings.push(`unparseable line: ${line.slice(0, 120)}`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (typeof obj !== 'object' || obj === null || !('tool' in obj))
|
|
242
|
+
continue;
|
|
243
|
+
const rec = obj;
|
|
244
|
+
if (rec.tool !== 'map_token_prop')
|
|
245
|
+
continue; // not a map-tokens call — skip silently
|
|
246
|
+
if (typeof rec.component !== 'string' || !rec.component) {
|
|
247
|
+
warnings.push('map_token_prop missing component — skipped');
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (typeof rec.prop !== 'string' || !rec.prop) {
|
|
251
|
+
warnings.push(`map_token_prop '${rec.component}': missing prop — skipped`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (!isStringArray(rec.token_allowed) || rec.token_allowed.length === 0) {
|
|
255
|
+
warnings.push(`map_token_prop '${rec.component}.${String(rec.prop)}': missing or empty token_allowed — skipped`);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const call = {
|
|
259
|
+
tool: 'map_token_prop',
|
|
260
|
+
component: rec.component,
|
|
261
|
+
prop: rec.prop,
|
|
262
|
+
token_allowed: rec.token_allowed,
|
|
263
|
+
};
|
|
264
|
+
calls.push(call);
|
|
265
|
+
}
|
|
266
|
+
return { calls, warnings };
|
|
267
|
+
}
|
|
268
|
+
// --- Agent invocation ---
|
|
269
|
+
const AGENT_BINARIES = {
|
|
270
|
+
claude: 'claude',
|
|
271
|
+
codex: 'codex',
|
|
272
|
+
opencode: 'opencode',
|
|
273
|
+
cursor: 'cursor-agent',
|
|
274
|
+
copilot: 'copilot',
|
|
275
|
+
};
|
|
276
|
+
export function resolveBinary(agent) {
|
|
277
|
+
const envKey = `EDS_AGENT_BINARY_${agent.toUpperCase()}`;
|
|
278
|
+
const override = process.env[envKey];
|
|
279
|
+
if (override && override.trim())
|
|
280
|
+
return override.trim();
|
|
281
|
+
return AGENT_BINARIES[agent];
|
|
282
|
+
}
|
|
283
|
+
/** Per-agent env vars that switch model calls to AWS Bedrock. */
|
|
284
|
+
const BEDROCK_ENV_BY_AGENT = {
|
|
285
|
+
claude: { CLAUDE_CODE_USE_BEDROCK: '1' },
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Agents with a working Bedrock routing mechanism, of any shape (env var,
|
|
289
|
+
* argv config override, or a provider-prefixed model string) — not just the
|
|
290
|
+
* env-var agents in BEDROCK_ENV_BY_AGENT. cursor is excluded: its own Bedrock
|
|
291
|
+
* support is currently broken for non-interactive/CI use.
|
|
292
|
+
*/
|
|
293
|
+
const BEDROCK_CAPABLE_AGENTS = new Set(['claude', 'codex', 'opencode']);
|
|
294
|
+
export function agentSupportsBedrock(agent) {
|
|
295
|
+
return BEDROCK_CAPABLE_AGENTS.has(agent);
|
|
296
|
+
}
|
|
297
|
+
const DEFAULT_CODEX_BEDROCK_REGION = 'us-east-1';
|
|
298
|
+
const DEFAULT_OPENCODE_MODEL = 'claude-haiku-4-5';
|
|
299
|
+
/**
|
|
300
|
+
* Default models per agent — lightweight/fast picks to control cost when no
|
|
301
|
+
* explicit model is configured. cursor uses `gpt-mini` (verified alias from
|
|
302
|
+
* GetUsableModels; haiku is not available in cursor's model catalog).
|
|
303
|
+
*
|
|
304
|
+
* codex is deliberately absent: with no entry here it gets no `--model` at
|
|
305
|
+
* all, so the installed Codex CLI picks whatever its account supports. On
|
|
306
|
+
* Bedrock it still needs an explicit id, since the model id there carries an
|
|
307
|
+
* `openai.` prefix required by the Bedrock provider and absent from the
|
|
308
|
+
* direct api.openai.com id — see DEFAULT_CODEX_BEDROCK_MODEL.
|
|
309
|
+
*/
|
|
310
|
+
const DEFAULT_MODELS = {
|
|
311
|
+
claude: 'haiku',
|
|
312
|
+
opencode: DEFAULT_OPENCODE_MODEL,
|
|
313
|
+
cursor: 'gpt-mini', // cursor alias for gpt-5.4-mini-medium; haiku not in cursor's catalog
|
|
314
|
+
copilot: 'Auto', // the only model guaranteed on every Copilot plan (Free/Pro/Business/Enterprise); Pro+ users override via EDS_AGENT_MODEL_COPILOT
|
|
315
|
+
};
|
|
316
|
+
const DEFAULT_CODEX_BEDROCK_MODEL = 'openai.gpt-5.6-luna';
|
|
317
|
+
/**
|
|
318
|
+
* Resolve the model for an agent. Explicit flag/creds value wins, then a
|
|
319
|
+
* per-agent `EDS_AGENT_MODEL_<AGENT>` env override (mirrors the
|
|
320
|
+
* `EDS_AGENT_BINARY_<AGENT>` pattern), otherwise the lightweight default for
|
|
321
|
+
* that agent. `bedrock` only affects the fallback default, and only for
|
|
322
|
+
* agents whose model id changes shape under Bedrock (codex, opencode) — an
|
|
323
|
+
* explicit value or env override is never rewritten.
|
|
324
|
+
*/
|
|
325
|
+
export function resolveAgentModel(agent, explicit, bedrock = false) {
|
|
326
|
+
if (explicit && explicit.trim())
|
|
327
|
+
return explicit.trim();
|
|
328
|
+
const override = process.env[`EDS_AGENT_MODEL_${agent.toUpperCase()}`];
|
|
329
|
+
if (override && override.trim())
|
|
330
|
+
return override.trim();
|
|
331
|
+
if (bedrock && agent === 'codex')
|
|
332
|
+
return DEFAULT_CODEX_BEDROCK_MODEL;
|
|
333
|
+
if (bedrock && agent === 'opencode')
|
|
334
|
+
return withBedrockProviderPrefix(DEFAULT_OPENCODE_MODEL);
|
|
335
|
+
return DEFAULT_MODELS[agent];
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* opencode selects providers via a `provider/model` string rather than a
|
|
339
|
+
* flag or env var, so routing it through Bedrock means rewriting the model
|
|
340
|
+
* string itself. Left alone if it already carries a provider prefix (an
|
|
341
|
+
* explicit --model / EDS_AGENT_MODEL_OPENCODE value naming its own
|
|
342
|
+
* provider), since resolveAgentModel only applies this to the bare default.
|
|
343
|
+
*/
|
|
344
|
+
function withBedrockProviderPrefix(model) {
|
|
345
|
+
return model.includes('/') ? model : `amazon-bedrock/${model}`;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* codex has no Bedrock env var; it requires `-c` config overrides
|
|
349
|
+
* (`model_provider` + the Bedrock provider's region) passed as argv, per the
|
|
350
|
+
* org's verified codex-bedrock-security-review setup. Region comes from the
|
|
351
|
+
* environment's own AWS region var, falling back to us-east-1 — the region
|
|
352
|
+
* gpt-5.6-luna's Bedrock endpoint requires — when neither is set.
|
|
353
|
+
*/
|
|
354
|
+
function codexBedrockConfigArgs() {
|
|
355
|
+
const region = process.env.AWS_REGION?.trim() || process.env.AWS_DEFAULT_REGION?.trim() || DEFAULT_CODEX_BEDROCK_REGION;
|
|
356
|
+
return ['-c', 'model_provider=amazon-bedrock', '-c', `model_providers.amazon-bedrock.region=${region}`];
|
|
357
|
+
}
|
|
358
|
+
export function buildArgs(agent, prompt, model, promptViaStdin = false, bedrock = false) {
|
|
359
|
+
// codex with no configured model resolves to undefined — omit --model
|
|
360
|
+
// entirely so the CLI picks its own account-compatible default.
|
|
361
|
+
const resolvedModel = resolveAgentModel(agent, model, bedrock);
|
|
362
|
+
const modelArg = resolvedModel ? ['--model', resolvedModel] : [];
|
|
363
|
+
// When the prompt is delivered on stdin, omit it from argv — a large prompt
|
|
364
|
+
// as a command-line argument overflows ARG_MAX (spawn E2BIG). All four CLIs
|
|
365
|
+
// read the prompt from stdin when it isn't passed positionally.
|
|
366
|
+
const promptArg = promptViaStdin ? [] : [prompt];
|
|
367
|
+
switch (agent) {
|
|
368
|
+
case 'claude':
|
|
369
|
+
return ['--print', ...modelArg, ...promptArg];
|
|
370
|
+
case 'codex': {
|
|
371
|
+
const bedrockArgs = bedrock ? codexBedrockConfigArgs() : [];
|
|
372
|
+
// --dangerously-bypass-approvals-and-sandbox required for non-interactive use
|
|
373
|
+
return ['exec', ...bedrockArgs, ...modelArg, '--dangerously-bypass-approvals-and-sandbox', ...promptArg];
|
|
374
|
+
}
|
|
375
|
+
case 'opencode':
|
|
376
|
+
return ['run', ...modelArg, ...promptArg];
|
|
377
|
+
case 'cursor':
|
|
378
|
+
// cursor-agent uses --print for non-interactive stdout output
|
|
379
|
+
return ['--print', ...modelArg, ...promptArg];
|
|
380
|
+
case 'copilot': {
|
|
381
|
+
// copilot's -p takes the prompt as its value — it MUST come immediately
|
|
382
|
+
// after -p or the CLI rejects with "Invalid command format".
|
|
383
|
+
// --allow-all-tools mirrors codex's sandbox bypass for non-interactive
|
|
384
|
+
// use.
|
|
385
|
+
//
|
|
386
|
+
// Model handling is special: copilot's UI shows "Auto" as the default
|
|
387
|
+
// pick, but the CLI rejects --model Auto ("not available"). Auto is a
|
|
388
|
+
// UI-only label — internally, omitting --model IS Auto. So we only
|
|
389
|
+
// pass --model when the user set an explicit override (via flag or
|
|
390
|
+
// EDS_AGENT_MODEL_COPILOT); the sentinel string 'Auto' means "omit".
|
|
391
|
+
//
|
|
392
|
+
// Stdin fallback is unsupported: copilot -p requires an inline prompt
|
|
393
|
+
// value, so promptViaStdin will fail here — callers must pass the
|
|
394
|
+
// prompt inline for copilot.
|
|
395
|
+
const copilotModel = resolveAgentModel('copilot', model);
|
|
396
|
+
const copilotModelArg = !copilotModel || copilotModel === 'Auto' ? [] : ['--model', copilotModel];
|
|
397
|
+
return ['-p', ...promptArg, ...copilotModelArg, '--allow-all-tools'];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
export async function runAgent(options) {
|
|
402
|
+
const { agent, prompt, timeoutMs, model, onOutput, promptViaStdin, onDebugEvent } = options;
|
|
403
|
+
// Fall back to the process-wide EDS_BEDROCK signal (set once by the CLI's
|
|
404
|
+
// top-level --bedrock resolution and inherited by every spawned subprocess)
|
|
405
|
+
// when a call site doesn't pass `bedrock` explicitly — closes the gap for
|
|
406
|
+
// call sites that forget to thread the flag through by hand.
|
|
407
|
+
const bedrock = options.bedrock ?? process.env.EDS_BEDROCK === '1';
|
|
408
|
+
const binary = resolveBinary(agent);
|
|
409
|
+
const useStdin = !!promptViaStdin;
|
|
410
|
+
const args = buildArgs(agent, prompt, model, useStdin, bedrock);
|
|
411
|
+
const startedAt = Date.now();
|
|
412
|
+
onDebugEvent?.('run.start', {
|
|
413
|
+
agent,
|
|
414
|
+
binary,
|
|
415
|
+
model,
|
|
416
|
+
bedrock: !!bedrock,
|
|
417
|
+
timeoutMs,
|
|
418
|
+
promptLen: prompt.length,
|
|
419
|
+
promptHead: prompt.slice(0, 500),
|
|
420
|
+
});
|
|
421
|
+
return new Promise((resolve) => {
|
|
422
|
+
const bedrockEnv = bedrock ? BEDROCK_ENV_BY_AGENT[agent] : undefined;
|
|
423
|
+
const child = spawn(binary, args, {
|
|
424
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
425
|
+
...(bedrockEnv ? { env: { ...process.env, ...bedrockEnv } } : {}),
|
|
426
|
+
});
|
|
427
|
+
if (useStdin && child.stdin) {
|
|
428
|
+
// Guard against EPIPE: the child may close stdin before we finish
|
|
429
|
+
// writing (fast exit, or it stops reading). Swallow the write error —
|
|
430
|
+
// the child's own exit code/stderr is the source of truth.
|
|
431
|
+
child.stdin.on('error', () => { });
|
|
432
|
+
child.stdin.write(prompt, () => {
|
|
433
|
+
child.stdin?.end();
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
child.stdin?.end();
|
|
438
|
+
}
|
|
439
|
+
let stdout = '';
|
|
440
|
+
let stderr = '';
|
|
441
|
+
let timedOut = false;
|
|
442
|
+
const timer = setTimeout(() => {
|
|
443
|
+
timedOut = true;
|
|
444
|
+
child.kill('SIGTERM');
|
|
445
|
+
}, timeoutMs);
|
|
446
|
+
child.stdout?.on('data', (chunk) => {
|
|
447
|
+
const text = chunk.toString();
|
|
448
|
+
stdout += text;
|
|
449
|
+
onOutput?.(text);
|
|
450
|
+
});
|
|
451
|
+
child.stderr?.on('data', (chunk) => {
|
|
452
|
+
stderr += chunk.toString();
|
|
453
|
+
});
|
|
454
|
+
child.on('close', (code, signal) => {
|
|
455
|
+
clearTimeout(timer);
|
|
456
|
+
const result = {
|
|
457
|
+
exitCode: signal ? 1 : (code ?? 1),
|
|
458
|
+
stdout,
|
|
459
|
+
stderr,
|
|
460
|
+
timedOut,
|
|
461
|
+
};
|
|
462
|
+
onDebugEvent?.('run.end', {
|
|
463
|
+
agent,
|
|
464
|
+
model,
|
|
465
|
+
durationMs: Date.now() - startedAt,
|
|
466
|
+
exitCode: result.exitCode,
|
|
467
|
+
signal,
|
|
468
|
+
timedOut,
|
|
469
|
+
stdoutLen: stdout.length,
|
|
470
|
+
stderrLen: stderr.length,
|
|
471
|
+
stderrTail: stderr.slice(-1000),
|
|
472
|
+
});
|
|
473
|
+
resolve(result);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
export async function checkAgentAuth(agent) {
|
|
478
|
+
const binary = resolveBinary(agent);
|
|
479
|
+
// Verify the selected agent's binary exists first — for EVERY agent, not
|
|
480
|
+
// just claude. When `binary` is an absolute path (e.g. set via
|
|
481
|
+
// EDS_AGENT_BINARY_<AGENT>=/opt/custom/bin), `which` on some shells doesn't
|
|
482
|
+
// resolve it — check the filesystem directly for absolute paths, and fall
|
|
483
|
+
// back to `which` for bare names on $PATH.
|
|
484
|
+
const binaryExists = await new Promise((resolve) => {
|
|
485
|
+
if (binary.startsWith('/')) {
|
|
486
|
+
import('node:fs/promises').then((fs) => fs.access(binary).then(() => resolve(true), () => resolve(false)));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const child = spawn(process.platform === 'win32' ? 'where' : 'which', [binary], { stdio: 'ignore' });
|
|
490
|
+
child.on('close', (code) => resolve(code === 0));
|
|
491
|
+
});
|
|
492
|
+
if (!binaryExists)
|
|
493
|
+
return 'not-found';
|
|
494
|
+
// Only Claude exposes `auth status --json`. Non-Claude agents are considered
|
|
495
|
+
// authenticated once their binary is present — never gate them on claude
|
|
496
|
+
// (e.g. Codex-via-Bedrock users would otherwise be blocked by a claude check).
|
|
497
|
+
if (agent !== 'claude')
|
|
498
|
+
return 'ok';
|
|
499
|
+
// Use `claude auth status` — fast, no API call, works regardless of which
|
|
500
|
+
// auth provider (direct, Bedrock, Vertex) or whether AWS_PROFILE is set.
|
|
501
|
+
return new Promise((resolve) => {
|
|
502
|
+
const child = spawn(binary, ['auth', 'status', '--json'], {
|
|
503
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
504
|
+
});
|
|
505
|
+
let stdout = '';
|
|
506
|
+
let done = false;
|
|
507
|
+
const timer = setTimeout(() => {
|
|
508
|
+
if (!done) {
|
|
509
|
+
done = true;
|
|
510
|
+
child.kill('SIGTERM');
|
|
511
|
+
resolve('unauthenticated');
|
|
512
|
+
}
|
|
513
|
+
}, 5000);
|
|
514
|
+
child.stdout?.on('data', (chunk) => {
|
|
515
|
+
stdout += chunk.toString();
|
|
516
|
+
});
|
|
517
|
+
child.on('close', (code) => {
|
|
518
|
+
if (done)
|
|
519
|
+
return;
|
|
520
|
+
done = true;
|
|
521
|
+
clearTimeout(timer);
|
|
522
|
+
if (code !== 0) {
|
|
523
|
+
resolve('unauthenticated');
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
const status = JSON.parse(stdout);
|
|
528
|
+
resolve(status.loggedIn ? 'ok' : 'unauthenticated');
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
resolve('unauthenticated');
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Build a diagnostic string from a failed agent run, surfacing the agent's own
|
|
538
|
+
* stderr (or stdout, when stderr is empty) so callers never emit a context-free
|
|
539
|
+
* "agent failed". Callers only invoke this on a failed run: either a non-zero
|
|
540
|
+
* exit, or a zero exit that produced no tool calls. A non-zero exit is reported
|
|
541
|
+
* as such; a zero exit is therefore the "produced no tool calls" case.
|
|
542
|
+
*/
|
|
543
|
+
export function describeAgentFailure(result, maxDetail = 800) {
|
|
544
|
+
const base = result.exitCode !== 0 ? `agent exited with code ${result.exitCode}` : 'agent produced no tool calls';
|
|
545
|
+
const detail = (result.stderr.trim() || result.stdout.trim()).slice(-maxDetail).trim();
|
|
546
|
+
return detail ? `${base} — ${detail}` : base;
|
|
547
|
+
}
|
|
548
|
+
export function extractSentinelOutput(stdout) {
|
|
549
|
+
const START = '<<<EDS_OUTPUT_START>>>';
|
|
550
|
+
const END = '<<<EDS_OUTPUT_END>>>';
|
|
551
|
+
const startIdx = stdout.indexOf(START);
|
|
552
|
+
const endIdx = stdout.indexOf(END);
|
|
553
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx)
|
|
554
|
+
return null;
|
|
555
|
+
// Check for multiple blocks
|
|
556
|
+
const secondStart = stdout.indexOf(START, startIdx + START.length);
|
|
557
|
+
if (secondStart !== -1 && secondStart < endIdx)
|
|
558
|
+
return 'multiple';
|
|
559
|
+
const afterStart = stdout.indexOf(END, startIdx);
|
|
560
|
+
const secondEnd = stdout.indexOf(END, afterStart + END.length);
|
|
561
|
+
if (secondEnd !== -1)
|
|
562
|
+
return 'multiple';
|
|
563
|
+
return stdout.slice(startIdx + START.length, endIdx).trim();
|
|
564
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
export { AGENT_NAMES,
|
|
1
|
+
export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
|
|
2
2
|
export type { AgentName } from './agent-names.js';
|
|
3
|
-
export
|
|
4
|
-
export type { ClassifyComponentCall, ClassifyPropCall, ClassifySlotCall, ExcludePropCall, MapTokenPropCall, ParsedMapTokenPropToolCalls, ParsedSelectToolCalls, ParsedTokenToolCalls, ParsedToolCalls, RejectComponentCall, SelectComponentCall, SelectToolCall, SetGroupCall, SetTokenCall, ToolCall, TokenToolCall, } from './
|
|
5
|
-
export
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
9
|
-
export type { GenerationOrchestratorRequest } from './orchestrator/execute-generation-orchestrator.js';
|
|
10
|
-
export { agentSupportsBedrock, buildAgentArgs as buildArgs, resolveAgentModel, } from './services/agent/helpers/build-agent-args.js';
|
|
11
|
-
export { resolveAgentBinary as resolveBinary } from './services/agent/helpers/resolve-agent-binary.js';
|
|
12
|
-
export { checkAgentAuth } from './services/agent/helpers/check-agent-auth.js';
|
|
13
|
-
export { runAgent, describeAgentFailure, extractSentinelOutput } from './services/agent/run-agent.js';
|
|
14
|
-
export { parsePropToolCallLines as parseToolCallLines } from './services/parsing/parse-prop-tool-calls.js';
|
|
15
|
-
export { parseSelectToolCallLines } from './services/parsing/parse-select-tool-calls.js';
|
|
16
|
-
export { parseTokenToolCallLines } from './services/parsing/parse-token-tool-calls.js';
|
|
17
|
-
export { parseMapTokenPropToolCallLines } from './services/parsing/parse-map-token-tool-calls.js';
|
|
18
|
-
export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './services/prompts/build-prompt.js';
|
|
3
|
+
export { agentSupportsBedrock, buildArgs, checkAgentAuth, describeAgentFailure, extractSentinelOutput, parseMapTokenPropToolCallLines, parseSelectToolCallLines, parseTokenToolCallLines, parseToolCallLines, resolveAgentModel, resolveBinary, runAgent, } from './agent-runner.js';
|
|
4
|
+
export type { AgentAuthStatus, AgentDebugEvent, AgentRunResult, ClassifyComponentCall, ClassifyPropCall, ClassifySlotCall, ExcludePropCall, MapTokenPropCall, ParsedMapTokenPropToolCalls, ParsedSelectToolCalls, ParsedTokenToolCalls, ParsedToolCalls, RejectComponentCall, SelectComponentCall, SelectToolCall, SetGroupCall, SetTokenCall, ToolCall, TokenToolCall, } from './agent-runner.js';
|
|
5
|
+
export { createLocalCliAgentInvoker } from './agent-invoker.js';
|
|
6
|
+
export type { AgentInvoker, CreateLocalCliAgentInvokerOptions, InvokeAgentOptions } from './agent-invoker.js';
|
|
7
|
+
export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './prompt-builder.js';
|
|
8
|
+
export type { ComponentSourceRef, GeneratedCdf, Mode, PromptOptions, Skill } from './prompt-builder.js';
|
|
19
9
|
export { formatGenerateProgressLine } from './progress.js';
|
|
20
|
-
export { createLocalCliAgentInvoker } from './services/agent/local-cli-agent-invoker.js';
|
|
21
|
-
export type { AgentInvoker, InvokeAgentOptions, LocalCliAgentInvokerOptions as CreateLocalCliAgentInvokerOptions, } from './types/invoker.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
// Agent identity
|
|
2
|
-
export { AGENT_NAMES,
|
|
3
|
-
//
|
|
4
|
-
export {
|
|
5
|
-
//
|
|
6
|
-
export {
|
|
7
|
-
// Agent execution (low-level)
|
|
8
|
-
export { agentSupportsBedrock, buildAgentArgs as buildArgs, resolveAgentModel, } from './services/agent/helpers/build-agent-args.js';
|
|
9
|
-
export { resolveAgentBinary as resolveBinary } from './services/agent/helpers/resolve-agent-binary.js';
|
|
10
|
-
export { checkAgentAuth } from './services/agent/helpers/check-agent-auth.js';
|
|
11
|
-
export { runAgent, describeAgentFailure, extractSentinelOutput } from './services/agent/run-agent.js';
|
|
12
|
-
// Tool call parsing
|
|
13
|
-
export { parsePropToolCallLines as parseToolCallLines } from './services/parsing/parse-prop-tool-calls.js';
|
|
14
|
-
export { parseSelectToolCallLines } from './services/parsing/parse-select-tool-calls.js';
|
|
15
|
-
export { parseTokenToolCallLines } from './services/parsing/parse-token-tool-calls.js';
|
|
16
|
-
export { parseMapTokenPropToolCallLines } from './services/parsing/parse-map-token-tool-calls.js';
|
|
2
|
+
export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
|
|
3
|
+
// Agent invocation (low-level)
|
|
4
|
+
export { agentSupportsBedrock, buildArgs, checkAgentAuth, describeAgentFailure, extractSentinelOutput, parseMapTokenPropToolCallLines, parseSelectToolCallLines, parseTokenToolCallLines, parseToolCallLines, resolveAgentModel, resolveBinary, runAgent, } from './agent-runner.js';
|
|
5
|
+
// Agent invocation (interface)
|
|
6
|
+
export { createLocalCliAgentInvoker } from './agent-invoker.js';
|
|
17
7
|
// Prompt building
|
|
18
|
-
export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './
|
|
8
|
+
export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './prompt-builder.js';
|
|
19
9
|
// Progress reporting
|
|
20
10
|
export { formatGenerateProgressLine } from './progress.js';
|
|
21
|
-
// Backward-compat exports — CLI callers still use these; migrate to createGenerationEndpoint
|
|
22
|
-
export { createLocalCliAgentInvoker } from './services/agent/local-cli-agent-invoker.js';
|