@contentful/experience-design-system-generation 2.36.2-dev-build-7907fec3.0 → 2.36.2-dev-build-a6f80ae6.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.
Files changed (65) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/agent-invoker.d.ts +25 -0
  3. package/dist/src/{services/agent/local-cli-agent-invoker.js → agent-invoker.js} +2 -2
  4. package/dist/src/agent-names.d.ts +0 -1
  5. package/dist/src/agent-names.js +0 -7
  6. package/dist/src/agent-runner.d.ts +144 -0
  7. package/dist/src/agent-runner.js +564 -0
  8. package/dist/src/index.d.ts +7 -19
  9. package/dist/src/index.js +6 -18
  10. package/dist/src/prompt-builder.d.ts +81 -0
  11. package/dist/src/prompt-builder.js +344 -0
  12. package/package.json +2 -2
  13. package/dist/src/controller/create-generation-endpoint.d.ts +0 -3
  14. package/dist/src/controller/create-generation-endpoint.js +0 -15
  15. package/dist/src/controller/helpers/validate-generation-request.d.ts +0 -2
  16. package/dist/src/controller/helpers/validate-generation-request.js +0 -18
  17. package/dist/src/orchestrator/execute-generation-orchestrator.d.ts +0 -6
  18. package/dist/src/orchestrator/execute-generation-orchestrator.js +0 -12
  19. package/dist/src/services/agent/helpers/build-agent-args.d.ts +0 -6
  20. package/dist/src/services/agent/helpers/build-agent-args.js +0 -58
  21. package/dist/src/services/agent/helpers/check-agent-auth.d.ts +0 -3
  22. package/dist/src/services/agent/helpers/check-agent-auth.js +0 -19
  23. package/dist/src/services/agent/helpers/check-claude-auth.d.ts +0 -2
  24. package/dist/src/services/agent/helpers/check-claude-auth.js +0 -37
  25. package/dist/src/services/agent/helpers/describe-agent-failure.d.ts +0 -2
  26. package/dist/src/services/agent/helpers/describe-agent-failure.js +0 -5
  27. package/dist/src/services/agent/helpers/extract-sentinel-output.d.ts +0 -1
  28. package/dist/src/services/agent/helpers/extract-sentinel-output.js +0 -16
  29. package/dist/src/services/agent/helpers/resolve-agent-binary.d.ts +0 -2
  30. package/dist/src/services/agent/helpers/resolve-agent-binary.js +0 -8
  31. package/dist/src/services/agent/local-cli-agent-invoker.d.ts +0 -3
  32. package/dist/src/services/agent/run-agent.d.ts +0 -15
  33. package/dist/src/services/agent/run-agent.js +0 -70
  34. package/dist/src/services/parsing/helpers/read-tool-call-objects.d.ts +0 -4
  35. package/dist/src/services/parsing/helpers/read-tool-call-objects.js +0 -54
  36. package/dist/src/services/parsing/parse-map-token-tool-calls.d.ts +0 -2
  37. package/dist/src/services/parsing/parse-map-token-tool-calls.js +0 -39
  38. package/dist/src/services/parsing/parse-prop-tool-calls.d.ts +0 -2
  39. package/dist/src/services/parsing/parse-prop-tool-calls.js +0 -91
  40. package/dist/src/services/parsing/parse-select-tool-calls.d.ts +0 -2
  41. package/dist/src/services/parsing/parse-select-tool-calls.js +0 -27
  42. package/dist/src/services/parsing/parse-token-tool-calls.d.ts +0 -2
  43. package/dist/src/services/parsing/parse-token-tool-calls.js +0 -39
  44. package/dist/src/services/prompts/build-prompt.d.ts +0 -4
  45. package/dist/src/services/prompts/build-prompt.js +0 -8
  46. package/dist/src/services/prompts/helpers/build-preamble.d.ts +0 -2
  47. package/dist/src/services/prompts/helpers/build-preamble.js +0 -48
  48. package/dist/src/services/prompts/helpers/build-skill-preambles.d.ts +0 -4
  49. package/dist/src/services/prompts/helpers/build-skill-preambles.js +0 -126
  50. package/dist/src/services/prompts/helpers/format-source-refs.d.ts +0 -2
  51. package/dist/src/services/prompts/helpers/format-source-refs.js +0 -31
  52. package/dist/src/services/prompts/helpers/format-token-candidates.d.ts +0 -8
  53. package/dist/src/services/prompts/helpers/format-token-candidates.js +0 -80
  54. package/dist/src/services/prompts/helpers/resolve-skill-file.d.ts +0 -4
  55. package/dist/src/services/prompts/helpers/resolve-skill-file.js +0 -49
  56. package/dist/src/types/agent.d.ts +0 -10
  57. package/dist/src/types/agent.js +0 -1
  58. package/dist/src/types/contract.d.ts +0 -24
  59. package/dist/src/types/contract.js +0 -1
  60. package/dist/src/types/invoker.d.ts +0 -17
  61. package/dist/src/types/invoker.js +0 -1
  62. package/dist/src/types/prompt.d.ts +0 -53
  63. package/dist/src/types/prompt.js +0 -1
  64. package/dist/src/types/tool-calls.d.ts +0 -91
  65. 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
+ }
@@ -1,21 +1,9 @@
1
- export { AGENT_NAMES, AGENT_BINARIES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
1
+ export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
2
2
  export type { AgentName } from './agent-names.js';
3
- export type { AgentAuthStatus, AgentDebugEvent, AgentRunResult } from './types/agent.js';
4
- export type { ClassifyComponentCall, ClassifyPropCall, ClassifySlotCall, ExcludePropCall, MapTokenPropCall, ParsedMapTokenPropToolCalls, ParsedSelectToolCalls, ParsedTokenToolCalls, ParsedToolCalls, RejectComponentCall, SelectComponentCall, SelectToolCall, SetGroupCall, SetTokenCall, ToolCall, TokenToolCall, } from './types/tool-calls.js';
5
- export type { ComponentSourceRef, GeneratedCdf, Mode, PromptOptions, Skill } from './types/prompt.js';
6
- export { createGenerationEndpoint } from './controller/create-generation-endpoint.js';
7
- export type { GenerationEndpoint, GenerationEndpointOptions, GenerationEndpointRequest, GenerationEndpointResponse, } from './controller/create-generation-endpoint.js';
8
- export { executeGenerationOrchestrator } from './orchestrator/execute-generation-orchestrator.js';
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, AGENT_BINARIES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
3
- // Generation endpoint (primary entry point)
4
- export { createGenerationEndpoint } from './controller/create-generation-endpoint.js';
5
- // Orchestrator (lower-level — for callers that supply their own invoker)
6
- export { executeGenerationOrchestrator } from './orchestrator/execute-generation-orchestrator.js';
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 './services/prompts/build-prompt.js';
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';