@trailstep/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -3
- package/dist/index.d.ts +85 -1
- package/dist/index.js +396 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -192,17 +192,21 @@ function parseTargetArray(path, value, diagnostics) {
|
|
|
192
192
|
`${targetPath}.thinking must be one of low, medium, high, xhigh, or max when present.`
|
|
193
193
|
);
|
|
194
194
|
}
|
|
195
|
+
const model = typeof target.model === "string" ? optionalNonEmptyString(target.model) : void 0;
|
|
195
196
|
const args = parseOptionalStringArray(`${targetPath}.args`, target.args, diagnostics);
|
|
196
197
|
return [
|
|
197
198
|
{
|
|
198
199
|
provider: target.provider,
|
|
199
|
-
...
|
|
200
|
+
...model === void 0 ? {} : { model },
|
|
200
201
|
...typeof target.thinking === "string" && THINKING_LEVELS.has(target.thinking) ? { thinking: target.thinking } : {},
|
|
201
202
|
...args === void 0 ? {} : { args }
|
|
202
203
|
}
|
|
203
204
|
];
|
|
204
205
|
});
|
|
205
206
|
}
|
|
207
|
+
function optionalNonEmptyString(value) {
|
|
208
|
+
return value.trim().length === 0 ? void 0 : value;
|
|
209
|
+
}
|
|
206
210
|
|
|
207
211
|
// src/agent-targeting/parse-trailstep-config/parse-agent-mappings.ts
|
|
208
212
|
function parseAgentMappings(path, value, diagnostics) {
|
|
@@ -218,6 +222,7 @@ function parseAgentMappings(path, value, diagnostics) {
|
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
// src/agent-targeting/parse-trailstep-config/parse-custom-providers.ts
|
|
225
|
+
var THINKING_LEVELS2 = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
221
226
|
function parseCustomProviders(value, diagnostics) {
|
|
222
227
|
const customProviders = {};
|
|
223
228
|
if (!isRecord(value)) {
|
|
@@ -243,6 +248,16 @@ function parseCustomProviders(value, diagnostics) {
|
|
|
243
248
|
providerConfig.interactiveArgs,
|
|
244
249
|
diagnostics
|
|
245
250
|
);
|
|
251
|
+
const model = parseCustomProviderModelSupport(
|
|
252
|
+
`customProviders.${name}.model`,
|
|
253
|
+
providerConfig.model,
|
|
254
|
+
diagnostics
|
|
255
|
+
);
|
|
256
|
+
const thinking = parseCustomProviderThinkingSupport(
|
|
257
|
+
`customProviders.${name}.thinking`,
|
|
258
|
+
providerConfig.thinking,
|
|
259
|
+
diagnostics
|
|
260
|
+
);
|
|
246
261
|
const env = parseOptionalStringRecord(
|
|
247
262
|
`customProviders.${name}.env`,
|
|
248
263
|
providerConfig.env,
|
|
@@ -255,12 +270,74 @@ function parseCustomProviders(value, diagnostics) {
|
|
|
255
270
|
binary: providerConfig.binary,
|
|
256
271
|
...args === void 0 ? {} : { args },
|
|
257
272
|
...interactiveArgs === void 0 ? {} : { interactiveArgs },
|
|
273
|
+
...model === void 0 ? {} : { model },
|
|
274
|
+
...thinking === void 0 ? {} : { thinking },
|
|
258
275
|
...typeof providerConfig.cwd === "string" ? { cwd: providerConfig.cwd } : {},
|
|
259
276
|
...env === void 0 ? {} : { env }
|
|
260
277
|
};
|
|
261
278
|
}
|
|
262
279
|
return customProviders;
|
|
263
280
|
}
|
|
281
|
+
function parseCustomProviderModelSupport(path, value, diagnostics) {
|
|
282
|
+
if (value === void 0) {
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
if (!isRecord(value) || typeof value.supported !== "boolean") {
|
|
286
|
+
diagnostics.push(`${path} must be an object with a boolean supported field when present.`);
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
if (!value.supported) {
|
|
290
|
+
return { supported: false };
|
|
291
|
+
}
|
|
292
|
+
if (value.flag !== void 0 && (typeof value.flag !== "string" || value.flag.length === 0)) {
|
|
293
|
+
diagnostics.push(`${path}.flag must be a non-empty string when present.`);
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
supported: true,
|
|
298
|
+
...typeof value.flag === "string" ? { flag: value.flag } : {}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function parseCustomProviderThinkingSupport(path, value, diagnostics) {
|
|
302
|
+
if (value === void 0) {
|
|
303
|
+
return void 0;
|
|
304
|
+
}
|
|
305
|
+
if (!isRecord(value) || typeof value.supported !== "boolean") {
|
|
306
|
+
diagnostics.push(`${path} must be an object with a boolean supported field when present.`);
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
if (!value.supported) {
|
|
310
|
+
if (value.levels !== void 0 && (!Array.isArray(value.levels) || value.levels.length > 0)) {
|
|
311
|
+
diagnostics.push(`${path}.levels must be an empty array when thinking support is disabled.`);
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
return value.levels === void 0 ? { supported: false } : { supported: false, levels: [] };
|
|
315
|
+
}
|
|
316
|
+
if (value.flag !== void 0 && (typeof value.flag !== "string" || value.flag.length === 0)) {
|
|
317
|
+
diagnostics.push(`${path}.flag must be a non-empty string when present.`);
|
|
318
|
+
return void 0;
|
|
319
|
+
}
|
|
320
|
+
if (!Array.isArray(value.levels) || value.levels.length === 0) {
|
|
321
|
+
diagnostics.push(`${path}.levels must be a non-empty array of supported thinking levels.`);
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
const levels = [];
|
|
325
|
+
for (const [index, level] of value.levels.entries()) {
|
|
326
|
+
if (typeof level !== "string" || !THINKING_LEVELS2.has(level)) {
|
|
327
|
+
diagnostics.push(`${path}.levels[${index}] must be one of low, medium, high, xhigh, or max.`);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
levels.push(level);
|
|
331
|
+
}
|
|
332
|
+
if (levels.length !== value.levels.length) {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
supported: true,
|
|
337
|
+
...typeof value.flag === "string" ? { flag: value.flag } : {},
|
|
338
|
+
levels
|
|
339
|
+
};
|
|
340
|
+
}
|
|
264
341
|
|
|
265
342
|
// src/agent-targeting/parse-trailstep-config/parse-settings.ts
|
|
266
343
|
function parseSettings(path, value, diagnostics) {
|
|
@@ -530,6 +607,41 @@ function promptFileReference(promptFile) {
|
|
|
530
607
|
|
|
531
608
|
// src/known-cli-providers/providers/claude/claude-provider.ts
|
|
532
609
|
var CLAUDE_BINARY = "claude";
|
|
610
|
+
var CLAUDE_RESULT_FIELD = "result";
|
|
611
|
+
var CLAUDE_SPEC = {
|
|
612
|
+
id: "claude",
|
|
613
|
+
displayName: "Claude",
|
|
614
|
+
model: { supported: true, flag: "--model" },
|
|
615
|
+
thinking: {
|
|
616
|
+
supported: true,
|
|
617
|
+
flag: "--effort",
|
|
618
|
+
levels: ["low", "medium", "high", "xhigh", "max"]
|
|
619
|
+
},
|
|
620
|
+
working: {
|
|
621
|
+
command: CLAUDE_BINARY,
|
|
622
|
+
prompt: { kind: "prompt-file", reference: "at-prefixed-argument" },
|
|
623
|
+
baseArgs: [
|
|
624
|
+
"-p",
|
|
625
|
+
"@{{promptFile}}",
|
|
626
|
+
"--output-format",
|
|
627
|
+
"json",
|
|
628
|
+
"--dangerously-skip-permissions"
|
|
629
|
+
],
|
|
630
|
+
output: {
|
|
631
|
+
style: "stdout-json-envelope",
|
|
632
|
+
parsing: { resultField: CLAUDE_RESULT_FIELD }
|
|
633
|
+
},
|
|
634
|
+
repair: { supported: true, resumeFlag: "--resume", promptDelivery: "stdin" }
|
|
635
|
+
},
|
|
636
|
+
interactive: {
|
|
637
|
+
supported: true,
|
|
638
|
+
command: CLAUDE_BINARY,
|
|
639
|
+
requiresSystemPromptFile: true,
|
|
640
|
+
systemPromptFileFlag: "--append-system-prompt-file",
|
|
641
|
+
modelFlag: "--model",
|
|
642
|
+
permissionBypassFlag: "--dangerously-skip-permissions"
|
|
643
|
+
}
|
|
644
|
+
};
|
|
533
645
|
async function runWorking(request, runner = spawnClaudeCapturingStdout) {
|
|
534
646
|
const args = buildClaudeWorkingArgs(request);
|
|
535
647
|
let result;
|
|
@@ -569,7 +681,7 @@ function extractFailureContext(rawStdout) {
|
|
|
569
681
|
const metadata = extractEnvelopeMetadata(rawStdout, { harnessDurationMs: 0 });
|
|
570
682
|
let rawResultText;
|
|
571
683
|
try {
|
|
572
|
-
rawResultText = extractEnvelopeText(rawStdout, { resultField:
|
|
684
|
+
rawResultText = extractEnvelopeText(rawStdout, { resultField: CLAUDE_RESULT_FIELD });
|
|
573
685
|
} catch {
|
|
574
686
|
rawResultText = void 0;
|
|
575
687
|
}
|
|
@@ -583,7 +695,7 @@ async function writeCapturedOutput(options) {
|
|
|
583
695
|
if (options.captureMode === "raw-text") {
|
|
584
696
|
let text;
|
|
585
697
|
try {
|
|
586
|
-
text = extractEnvelopeText(options.stdout, { resultField:
|
|
698
|
+
text = extractEnvelopeText(options.stdout, { resultField: CLAUDE_RESULT_FIELD });
|
|
587
699
|
} catch (error) {
|
|
588
700
|
throw new TrailStepFailureError({
|
|
589
701
|
code: "agent_provider_output_invalid",
|
|
@@ -598,7 +710,7 @@ async function writeCapturedOutput(options) {
|
|
|
598
710
|
} else {
|
|
599
711
|
let output;
|
|
600
712
|
try {
|
|
601
|
-
output = extractEnvelopeOutput(options.stdout, { resultField:
|
|
713
|
+
output = extractEnvelopeOutput(options.stdout, { resultField: CLAUDE_RESULT_FIELD });
|
|
602
714
|
} catch (error) {
|
|
603
715
|
throw new TrailStepFailureError({
|
|
604
716
|
code: "agent_provider_output_invalid",
|
|
@@ -819,6 +931,7 @@ function terminateChildProcessTree(child) {
|
|
|
819
931
|
}
|
|
820
932
|
var claudeProvider = {
|
|
821
933
|
id: "claude",
|
|
934
|
+
spec: CLAUDE_SPEC,
|
|
822
935
|
runWorking,
|
|
823
936
|
repairOutput,
|
|
824
937
|
runInteractive
|
|
@@ -827,7 +940,31 @@ var claudeProvider = {
|
|
|
827
940
|
// src/known-cli-providers/providers/codex/codex-provider.ts
|
|
828
941
|
import { spawn as spawn2 } from "child_process";
|
|
829
942
|
var CODEX_BINARY = "codex";
|
|
830
|
-
var
|
|
943
|
+
var CODEX_THINKING_LEVELS = ["low", "medium", "high", "xhigh"];
|
|
944
|
+
var SUPPORTED_CODEX_THINKING = new Set(CODEX_THINKING_LEVELS);
|
|
945
|
+
var CODEX_SPEC = {
|
|
946
|
+
id: "codex",
|
|
947
|
+
displayName: "Codex",
|
|
948
|
+
model: { supported: true, flag: "-m" },
|
|
949
|
+
thinking: {
|
|
950
|
+
supported: true,
|
|
951
|
+
flag: "-c model_reasoning_effort",
|
|
952
|
+
levels: CODEX_THINKING_LEVELS
|
|
953
|
+
},
|
|
954
|
+
working: {
|
|
955
|
+
command: CODEX_BINARY,
|
|
956
|
+
prompt: { kind: "prompt-file", reference: "at-prefixed-argument" },
|
|
957
|
+
baseArgs: [
|
|
958
|
+
"exec",
|
|
959
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
960
|
+
"-o",
|
|
961
|
+
"{{outputFile}}",
|
|
962
|
+
"@{{promptFile}}"
|
|
963
|
+
],
|
|
964
|
+
output: { style: "provider-output-file" }
|
|
965
|
+
},
|
|
966
|
+
interactive: { supported: true, command: CODEX_BINARY, modelFlag: "--model" }
|
|
967
|
+
};
|
|
831
968
|
async function runWorking2(request, runner = spawnCodexInheritingStdio) {
|
|
832
969
|
const args = buildCodexWorkingArgs(request);
|
|
833
970
|
let result;
|
|
@@ -945,6 +1082,7 @@ function terminateChildProcessTree2(child) {
|
|
|
945
1082
|
}
|
|
946
1083
|
var codexProvider = {
|
|
947
1084
|
id: "codex",
|
|
1085
|
+
spec: CODEX_SPEC,
|
|
948
1086
|
runWorking: runWorking2,
|
|
949
1087
|
runInteractive: runInteractive2
|
|
950
1088
|
};
|
|
@@ -954,6 +1092,22 @@ import { spawn as spawn3 } from "child_process";
|
|
|
954
1092
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
955
1093
|
var GEMINI_BINARY = "gemini";
|
|
956
1094
|
var GEMINI_RESULT_FIELD = "response";
|
|
1095
|
+
var GEMINI_SPEC = {
|
|
1096
|
+
id: "gemini",
|
|
1097
|
+
displayName: "Gemini",
|
|
1098
|
+
model: { supported: true, flag: "-m" },
|
|
1099
|
+
thinking: { supported: false },
|
|
1100
|
+
working: {
|
|
1101
|
+
command: GEMINI_BINARY,
|
|
1102
|
+
prompt: { kind: "prompt-file", reference: "at-prefixed-argument" },
|
|
1103
|
+
baseArgs: ["-p", "@{{promptFile}}", "--yolo", "--output-format", "json"],
|
|
1104
|
+
output: {
|
|
1105
|
+
style: "stdout-json-envelope",
|
|
1106
|
+
parsing: { resultField: GEMINI_RESULT_FIELD }
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
interactive: { supported: true, command: GEMINI_BINARY, modelFlag: "-m" }
|
|
1110
|
+
};
|
|
957
1111
|
async function runWorking3(request, runner = spawnGeminiCapturingStdout) {
|
|
958
1112
|
const args = buildGeminiWorkingArgs(request);
|
|
959
1113
|
let result;
|
|
@@ -1095,6 +1249,7 @@ function terminateChildProcessTree3(child) {
|
|
|
1095
1249
|
}
|
|
1096
1250
|
var geminiProvider = {
|
|
1097
1251
|
id: "gemini",
|
|
1252
|
+
spec: GEMINI_SPEC,
|
|
1098
1253
|
runWorking: runWorking3,
|
|
1099
1254
|
runInteractive: runInteractive3
|
|
1100
1255
|
};
|
|
@@ -1203,6 +1358,34 @@ function windowsPathDelimiter() {
|
|
|
1203
1358
|
var PI_BINARY = "pi";
|
|
1204
1359
|
var PI_RESULT_FIELD = "message";
|
|
1205
1360
|
var PI_STDOUT_FALLBACK_TAIL_CHARS = 128e3;
|
|
1361
|
+
var PI_SPEC = {
|
|
1362
|
+
id: "pi",
|
|
1363
|
+
displayName: "Pi",
|
|
1364
|
+
model: {
|
|
1365
|
+
supported: true,
|
|
1366
|
+
flag: "--model",
|
|
1367
|
+
discovery: {
|
|
1368
|
+
command: PI_BINARY,
|
|
1369
|
+
args: ["--list-models"],
|
|
1370
|
+
outputParser: "pi-list-models-table"
|
|
1371
|
+
}
|
|
1372
|
+
},
|
|
1373
|
+
thinking: {
|
|
1374
|
+
supported: true,
|
|
1375
|
+
flag: "--thinking",
|
|
1376
|
+
levels: ["low", "medium", "high", "xhigh", "max"]
|
|
1377
|
+
},
|
|
1378
|
+
working: {
|
|
1379
|
+
command: PI_BINARY,
|
|
1380
|
+
prompt: { kind: "prompt-file", reference: "at-prefixed-argument" },
|
|
1381
|
+
baseArgs: ["-p", "@{{promptFile}}", "--mode", "json"],
|
|
1382
|
+
output: {
|
|
1383
|
+
style: "stdout-jsonl-transcript",
|
|
1384
|
+
parsing: { resultField: PI_RESULT_FIELD }
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1387
|
+
interactive: { supported: true, command: PI_BINARY, modelFlag: "--model" }
|
|
1388
|
+
};
|
|
1206
1389
|
async function runWorking4(request, runner = spawnPiCapturingStdout) {
|
|
1207
1390
|
const args = buildPiWorkingArgs(request);
|
|
1208
1391
|
let result;
|
|
@@ -1386,6 +1569,7 @@ function terminateChildProcessTree4(child) {
|
|
|
1386
1569
|
}
|
|
1387
1570
|
var piProvider = {
|
|
1388
1571
|
id: "pi",
|
|
1572
|
+
spec: PI_SPEC,
|
|
1389
1573
|
runWorking: runWorking4,
|
|
1390
1574
|
runInteractive: runInteractive4
|
|
1391
1575
|
};
|
|
@@ -1511,6 +1695,7 @@ function defaultRunsRoot(cwd) {
|
|
|
1511
1695
|
}
|
|
1512
1696
|
async function createRunDirectory(options) {
|
|
1513
1697
|
const runsRoot = options.runsRoot ?? defaultRunsRoot(options.cwd);
|
|
1698
|
+
validateRunName(options.runName);
|
|
1514
1699
|
await mkdir(runsRoot, { recursive: true });
|
|
1515
1700
|
await ensureTrailStepGitignoreForRunsRoot(runsRoot);
|
|
1516
1701
|
for (let suffix = 1; ; suffix += 1) {
|
|
@@ -1593,6 +1778,15 @@ async function writeRunState(runDir, state2) {
|
|
|
1593
1778
|
function isNodeError(error) {
|
|
1594
1779
|
return error instanceof Error && "code" in error;
|
|
1595
1780
|
}
|
|
1781
|
+
function validateRunName(runName) {
|
|
1782
|
+
if (runName.trim().length === 0 || runName === "." || runName === ".." || runName.includes("/") || runName.includes("\\")) {
|
|
1783
|
+
throw new TrailStepFailureError({
|
|
1784
|
+
code: "run_name_invalid",
|
|
1785
|
+
message: "Run name must be a non-empty single path segment.",
|
|
1786
|
+
details: { runName }
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1596
1790
|
function isRunState(value) {
|
|
1597
1791
|
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
1598
1792
|
}
|
|
@@ -2113,6 +2307,168 @@ function formatDiagnostics3(diagnostics) {
|
|
|
2113
2307
|
return diagnostics.map((diagnostic) => `${diagnostic.path} ${diagnostic.message}`).join("; ");
|
|
2114
2308
|
}
|
|
2115
2309
|
|
|
2310
|
+
// src/agent-execution/custom-provider/render-custom-provider-args.ts
|
|
2311
|
+
var PLACEHOLDER_NAMES = /* @__PURE__ */ new Set([
|
|
2312
|
+
"prompt",
|
|
2313
|
+
"promptFile",
|
|
2314
|
+
"outputFile",
|
|
2315
|
+
"model",
|
|
2316
|
+
"thinking"
|
|
2317
|
+
]);
|
|
2318
|
+
var CONDITIONAL_NAMES = /* @__PURE__ */ new Set(["model", "thinking"]);
|
|
2319
|
+
var TEMPLATE_TOKEN_PATTERN = /^{{([#/]?)([A-Za-z][A-Za-z0-9]*)}}$/;
|
|
2320
|
+
function renderCustomProviderArgs(options) {
|
|
2321
|
+
const values = {
|
|
2322
|
+
...options.values.prompt === void 0 ? {} : { prompt: options.values.prompt },
|
|
2323
|
+
...options.values.promptFile === void 0 ? {} : { promptFile: options.values.promptFile },
|
|
2324
|
+
...options.values.outputFile === void 0 ? {} : { outputFile: options.values.outputFile },
|
|
2325
|
+
...optionalNonEmptyString2(options.values.model) === void 0 ? {} : { model: optionalNonEmptyString2(options.values.model) },
|
|
2326
|
+
...options.values.thinking === void 0 ? {} : { thinking: options.values.thinking }
|
|
2327
|
+
};
|
|
2328
|
+
return renderRange({
|
|
2329
|
+
argv: options.argv,
|
|
2330
|
+
values,
|
|
2331
|
+
errorCode: options.errorCode,
|
|
2332
|
+
commandDescription: options.commandDescription
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
function renderRange(options) {
|
|
2336
|
+
const rendered = [];
|
|
2337
|
+
for (let index = 0; index < options.argv.length; index += 1) {
|
|
2338
|
+
const arg = options.argv[index] ?? "";
|
|
2339
|
+
const token = parseTemplateToken(arg);
|
|
2340
|
+
if (token?.kind === "open") {
|
|
2341
|
+
const closeIndex = findConditionalClose({
|
|
2342
|
+
argv: options.argv,
|
|
2343
|
+
openIndex: index,
|
|
2344
|
+
condition: token.name,
|
|
2345
|
+
errorCode: options.errorCode,
|
|
2346
|
+
commandDescription: options.commandDescription
|
|
2347
|
+
});
|
|
2348
|
+
if (options.values[token.name] !== void 0) {
|
|
2349
|
+
rendered.push(
|
|
2350
|
+
...renderRange({
|
|
2351
|
+
...options,
|
|
2352
|
+
argv: options.argv.slice(index + 1, closeIndex)
|
|
2353
|
+
})
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
index = closeIndex;
|
|
2357
|
+
continue;
|
|
2358
|
+
}
|
|
2359
|
+
if (token?.kind === "close") {
|
|
2360
|
+
throw templateError({
|
|
2361
|
+
code: options.errorCode,
|
|
2362
|
+
message: `${options.commandDescription} template has unexpected ${closingToken(token.name)} without a matching ${openingToken(token.name)}.`
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
if (token?.kind === "placeholder") {
|
|
2366
|
+
rendered.push(renderPlaceholder({ ...options, name: token.name }));
|
|
2367
|
+
continue;
|
|
2368
|
+
}
|
|
2369
|
+
if (TEMPLATE_TOKEN_PATTERN.test(arg)) {
|
|
2370
|
+
throw templateError({
|
|
2371
|
+
code: options.errorCode,
|
|
2372
|
+
message: `${options.commandDescription} template token ${arg} is not supported.`,
|
|
2373
|
+
details: { arg }
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
if (arg.includes("{{") || arg.includes("}}")) {
|
|
2377
|
+
throw templateError({
|
|
2378
|
+
code: options.errorCode,
|
|
2379
|
+
message: `${options.commandDescription} placeholders and conditional blocks must be whole argv values.`,
|
|
2380
|
+
details: { arg }
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
rendered.push(arg);
|
|
2384
|
+
}
|
|
2385
|
+
return rendered;
|
|
2386
|
+
}
|
|
2387
|
+
function parseTemplateToken(arg) {
|
|
2388
|
+
const match = TEMPLATE_TOKEN_PATTERN.exec(arg);
|
|
2389
|
+
if (!match) {
|
|
2390
|
+
return void 0;
|
|
2391
|
+
}
|
|
2392
|
+
const prefix = match[1] ?? "";
|
|
2393
|
+
const name = match[2] ?? "";
|
|
2394
|
+
if (prefix === "") {
|
|
2395
|
+
if (PLACEHOLDER_NAMES.has(name)) {
|
|
2396
|
+
return { kind: "placeholder", name };
|
|
2397
|
+
}
|
|
2398
|
+
return void 0;
|
|
2399
|
+
}
|
|
2400
|
+
if (CONDITIONAL_NAMES.has(name)) {
|
|
2401
|
+
return {
|
|
2402
|
+
kind: prefix === "#" ? "open" : "close",
|
|
2403
|
+
name
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
return void 0;
|
|
2407
|
+
}
|
|
2408
|
+
function findConditionalClose(options) {
|
|
2409
|
+
let depth = 1;
|
|
2410
|
+
for (let index = options.openIndex + 1; index < options.argv.length; index += 1) {
|
|
2411
|
+
const token = parseTemplateToken(options.argv[index] ?? "");
|
|
2412
|
+
if (token?.name !== options.condition) {
|
|
2413
|
+
continue;
|
|
2414
|
+
}
|
|
2415
|
+
if (token.kind === "open") {
|
|
2416
|
+
depth += 1;
|
|
2417
|
+
continue;
|
|
2418
|
+
}
|
|
2419
|
+
if (token.kind === "close") {
|
|
2420
|
+
depth -= 1;
|
|
2421
|
+
if (depth === 0) {
|
|
2422
|
+
return index;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
throw templateError({
|
|
2427
|
+
code: options.errorCode,
|
|
2428
|
+
message: `${options.commandDescription} template is missing ${closingToken(options.condition)} for ${openingToken(options.condition)}.`
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
function renderPlaceholder(options) {
|
|
2432
|
+
const value = options.values[options.name];
|
|
2433
|
+
if (value !== void 0) {
|
|
2434
|
+
return value;
|
|
2435
|
+
}
|
|
2436
|
+
if (options.name === "model" || options.name === "thinking") {
|
|
2437
|
+
throw templateError({
|
|
2438
|
+
code: options.errorCode,
|
|
2439
|
+
message: `${options.commandDescription} placeholder ${placeholderToken(options.name)} must be guarded with ${openingToken(options.name)} ... ${closingToken(options.name)} when no ${options.name} override is set.`,
|
|
2440
|
+
details: { placeholder: placeholderToken(options.name) }
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
throw templateError({
|
|
2444
|
+
code: options.errorCode,
|
|
2445
|
+
message: `${options.commandDescription} placeholder ${placeholderToken(options.name)} is not available in this command template.`,
|
|
2446
|
+
details: { placeholder: placeholderToken(options.name) }
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
function templateError(options) {
|
|
2450
|
+
return new TrailStepFailureError({
|
|
2451
|
+
code: options.code,
|
|
2452
|
+
message: options.message,
|
|
2453
|
+
...options.details === void 0 ? {} : { details: options.details }
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
function placeholderToken(name) {
|
|
2457
|
+
return `{{${name}}}`;
|
|
2458
|
+
}
|
|
2459
|
+
function openingToken(name) {
|
|
2460
|
+
return `{{#${name}}}`;
|
|
2461
|
+
}
|
|
2462
|
+
function closingToken(name) {
|
|
2463
|
+
return `{{/${name}}}`;
|
|
2464
|
+
}
|
|
2465
|
+
function optionalNonEmptyString2(value) {
|
|
2466
|
+
if (value === void 0 || value.trim().length === 0) {
|
|
2467
|
+
return void 0;
|
|
2468
|
+
}
|
|
2469
|
+
return value;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2116
2472
|
// src/agent-execution/interactive-agent/run-interactive-agent-command.ts
|
|
2117
2473
|
async function runInteractiveAgentCommand(options) {
|
|
2118
2474
|
const [target] = resolveAgentTargets({
|
|
@@ -2187,12 +2543,22 @@ async function runInteractiveAgentTarget(options) {
|
|
|
2187
2543
|
details: { provider: options.target.provider }
|
|
2188
2544
|
});
|
|
2189
2545
|
}
|
|
2190
|
-
const
|
|
2546
|
+
const thinking = options.target.thinking ?? options.role.thinking;
|
|
2547
|
+
const args = renderCustomProviderArgs({
|
|
2191
2548
|
argv: options.target.args ?? agentConfig.interactiveArgs,
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2549
|
+
values: {
|
|
2550
|
+
prompt,
|
|
2551
|
+
promptFile: files.promptFile,
|
|
2552
|
+
...options.target.model === void 0 ? {} : { model: options.target.model },
|
|
2553
|
+
...thinking === void 0 ? {} : { thinking }
|
|
2554
|
+
},
|
|
2555
|
+
errorCode: "interactive_command_invalid",
|
|
2556
|
+
commandDescription: "Interactive agent command"
|
|
2195
2557
|
});
|
|
2558
|
+
if (args.includes(files.promptFile)) {
|
|
2559
|
+
await mkdir2(dirname3(files.promptFile), { recursive: true });
|
|
2560
|
+
await writeFile5(files.promptFile, prompt, "utf8");
|
|
2561
|
+
}
|
|
2196
2562
|
return await runProcessUntilExitOrCompletion({
|
|
2197
2563
|
stepId: options.stepId,
|
|
2198
2564
|
target: options.target.provider,
|
|
@@ -2331,33 +2697,6 @@ function exampleJsonObject(schema) {
|
|
|
2331
2697
|
})
|
|
2332
2698
|
);
|
|
2333
2699
|
}
|
|
2334
|
-
async function substitutePromptPlaceholders(options) {
|
|
2335
|
-
let needsPromptFile = false;
|
|
2336
|
-
const substituted = options.argv.map((arg) => {
|
|
2337
|
-
if (arg === "{{prompt}}") {
|
|
2338
|
-
return options.prompt;
|
|
2339
|
-
}
|
|
2340
|
-
if (arg === "{{promptFile}}") {
|
|
2341
|
-
needsPromptFile = true;
|
|
2342
|
-
return options.promptFile;
|
|
2343
|
-
}
|
|
2344
|
-
if (arg === "{{model}}") {
|
|
2345
|
-
return options.model ?? "";
|
|
2346
|
-
}
|
|
2347
|
-
if (arg.includes("{{prompt}}") || arg.includes("{{promptFile}}") || arg.includes("{{model}}")) {
|
|
2348
|
-
throw new TrailStepFailureError({
|
|
2349
|
-
code: "interactive_command_invalid",
|
|
2350
|
-
message: "Interactive prompt placeholders must be whole argv values."
|
|
2351
|
-
});
|
|
2352
|
-
}
|
|
2353
|
-
return arg;
|
|
2354
|
-
});
|
|
2355
|
-
if (needsPromptFile) {
|
|
2356
|
-
await mkdir2(dirname3(options.promptFile), { recursive: true });
|
|
2357
|
-
await writeFile5(options.promptFile, options.prompt, "utf8");
|
|
2358
|
-
}
|
|
2359
|
-
return substituted;
|
|
2360
|
-
}
|
|
2361
2700
|
var spawnInteractiveProcess = async ({
|
|
2362
2701
|
command,
|
|
2363
2702
|
args,
|
|
@@ -2503,23 +2842,16 @@ function buildWorkingAgentArgs(options) {
|
|
|
2503
2842
|
...options.model ? ["--model", options.model] : []
|
|
2504
2843
|
];
|
|
2505
2844
|
}
|
|
2506
|
-
return
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
throw new TrailStepFailureError({
|
|
2517
|
-
code: "agent_provider_invalid",
|
|
2518
|
-
message: "Working agent command placeholders must be whole argv values."
|
|
2519
|
-
});
|
|
2520
|
-
}
|
|
2521
|
-
return arg;
|
|
2522
|
-
}
|
|
2845
|
+
return renderCustomProviderArgs({
|
|
2846
|
+
argv: options.argv,
|
|
2847
|
+
values: {
|
|
2848
|
+
promptFile: options.promptFile,
|
|
2849
|
+
outputFile: options.outputFile,
|
|
2850
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
2851
|
+
...options.thinking === void 0 ? {} : { thinking: options.thinking }
|
|
2852
|
+
},
|
|
2853
|
+
errorCode: "agent_provider_invalid",
|
|
2854
|
+
commandDescription: "Working agent command"
|
|
2523
2855
|
});
|
|
2524
2856
|
}
|
|
2525
2857
|
|
|
@@ -2573,11 +2905,13 @@ async function runCustomWorkingProvider(options) {
|
|
|
2573
2905
|
details: { provider: options.target.provider }
|
|
2574
2906
|
});
|
|
2575
2907
|
}
|
|
2908
|
+
const thinking = options.target.thinking ?? options.role.thinking;
|
|
2576
2909
|
const args = buildWorkingAgentArgs({
|
|
2577
2910
|
argv: options.target.args ?? agentConfig.args,
|
|
2578
2911
|
promptFile: options.files.promptFile,
|
|
2579
2912
|
outputFile: options.files.outputFile,
|
|
2580
|
-
model: options.target.model
|
|
2913
|
+
model: options.target.model,
|
|
2914
|
+
thinking
|
|
2581
2915
|
});
|
|
2582
2916
|
let result;
|
|
2583
2917
|
try {
|
|
@@ -2786,6 +3120,7 @@ async function runWorkingAgentTargetAttempt(options) {
|
|
|
2786
3120
|
return runCustomWorkingProvider({
|
|
2787
3121
|
config: options.config,
|
|
2788
3122
|
step: options.step,
|
|
3123
|
+
role: options.role,
|
|
2789
3124
|
cwd: options.cwd,
|
|
2790
3125
|
runner: options.runner,
|
|
2791
3126
|
target: options.target,
|
|
@@ -4315,7 +4650,13 @@ function isFlattenedAgentMappings(value) {
|
|
|
4315
4650
|
);
|
|
4316
4651
|
}
|
|
4317
4652
|
function isFlattenedAgentTarget(value) {
|
|
4318
|
-
|
|
4653
|
+
if (!isPlainRecord(value) || typeof value.provider !== "string") {
|
|
4654
|
+
return false;
|
|
4655
|
+
}
|
|
4656
|
+
if (value.model !== void 0) {
|
|
4657
|
+
return typeof value.model === "string" && value.model.trim().length > 0;
|
|
4658
|
+
}
|
|
4659
|
+
return true;
|
|
4319
4660
|
}
|
|
4320
4661
|
function isPlainRecord(value) {
|
|
4321
4662
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|