@agentproto/driver-agent-cli 2.0.1 → 2.2.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/{chunk-H43KNN32.mjs → chunk-TFB4QYOT.mjs} +392 -166
- package/dist/chunk-TFB4QYOT.mjs.map +1 -0
- package/dist/index.d.ts +81 -6
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/manifest/index.d.ts +3 -2
- package/dist/manifest/index.mjs +2 -2
- package/dist/{schema-BqxySb_M.d.ts → schema-BGmDJ1ZC.d.ts} +163 -8
- package/package.json +4 -3
- package/dist/chunk-H43KNN32.mjs.map +0 -1
|
@@ -5,8 +5,9 @@ import { spawn } from 'child_process';
|
|
|
5
5
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, rmdirSync, mkdtempSync } from 'fs';
|
|
6
6
|
import { createInterface } from 'readline';
|
|
7
7
|
import { join } from 'path';
|
|
8
|
-
import { randomUUID } from 'crypto';
|
|
9
8
|
import { tmpdir, homedir } from 'os';
|
|
9
|
+
import { loadAdapterSpawnSandboxConfig, resolveCommandSandbox } from '@agentproto/command-sandbox';
|
|
10
|
+
import { randomUUID } from 'crypto';
|
|
10
11
|
import { createDoctype } from '@agentproto/define-doctype';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -151,10 +152,27 @@ var authSchema = z.object({
|
|
|
151
152
|
}).strict().optional()
|
|
152
153
|
}).strict();
|
|
153
154
|
var authSubscriptionSchema = z.object({
|
|
154
|
-
setEnv: z.string().min(1),
|
|
155
|
+
setEnv: z.string().min(1).optional(),
|
|
156
|
+
external: z.boolean().optional(),
|
|
155
157
|
conflictEnv: z.array(z.string()).optional(),
|
|
156
158
|
unsetEnvAdd: z.array(z.string()).optional()
|
|
157
|
-
}).strict()
|
|
159
|
+
}).strict().superRefine((v, ctx) => {
|
|
160
|
+
if (v.external) {
|
|
161
|
+
if (v.setEnv !== void 0) {
|
|
162
|
+
ctx.addIssue({
|
|
163
|
+
code: z.ZodIssueCode.custom,
|
|
164
|
+
message: "authSubscription.external: true must NOT declare setEnv \u2014 an external (file-based) subscription injects no bearer; the CLI reads its own login file.",
|
|
165
|
+
path: ["setEnv"]
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} else if (v.setEnv === void 0) {
|
|
169
|
+
ctx.addIssue({
|
|
170
|
+
code: z.ZodIssueCode.custom,
|
|
171
|
+
message: "authSubscription requires setEnv unless external: true (the bearer must be injected into some env var).",
|
|
172
|
+
path: ["setEnv"]
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
158
176
|
var sessionSchema = z.object({
|
|
159
177
|
mode: z.enum(["ephemeral", "persistent", "resumable"]).default("ephemeral"),
|
|
160
178
|
idle_timeout_ms: z.number().int().min(1e3).default(6e5),
|
|
@@ -185,8 +203,7 @@ var modelsSchema = z.object({
|
|
|
185
203
|
// How a model is selected at session start: "config" (ACP
|
|
186
204
|
// set_config_option, default) | "command" (a `/model <id>` control turn,
|
|
187
205
|
// for agents like hermes that ignore the ACP session model config) |
|
|
188
|
-
// "arg" (a CLI argument composed into bin_args at spawn
|
|
189
|
-
// like codex-acp that take their model as `-c model="<id>"`).
|
|
206
|
+
// "arg" (a CLI argument composed into bin_args at spawn).
|
|
190
207
|
apply: z.enum(["config", "command", "arg"]).optional(),
|
|
191
208
|
// Argv template for apply:"arg" — {model} interpolates the requested
|
|
192
209
|
// model id. See AgentCliModels.bin_args_template.
|
|
@@ -200,6 +217,13 @@ var capabilitiesSchema = z.object({
|
|
|
200
217
|
multimodal: z.boolean().optional(),
|
|
201
218
|
resumable: z.boolean().optional(),
|
|
202
219
|
bidirectional: z.boolean().optional(),
|
|
220
|
+
/**
|
|
221
|
+
* Adapter has a verified native CLI resume into a real terminal/TUI
|
|
222
|
+
* session (e.g. `claude --resume <id>` or `hermes --resume <id> --tui`).
|
|
223
|
+
* When true, the host MAY restart this adapter via its native resume argv
|
|
224
|
+
* as a PTY session. Distinct from ACP-level `resumable`.
|
|
225
|
+
*/
|
|
226
|
+
nativeTerminalResume: z.boolean().optional(),
|
|
203
227
|
/**
|
|
204
228
|
* Adapter can ingest a filesystem path that the host UI just placed
|
|
205
229
|
* on disk (host-side drag-drop into a terminal pastes the path
|
|
@@ -300,7 +324,7 @@ var printConfigSchema = z.object({
|
|
|
300
324
|
flag: z.string(),
|
|
301
325
|
kind: z.enum(["value", "boolean"])
|
|
302
326
|
}).strict().optional(),
|
|
303
|
-
event_schema: z.enum(["claude-stream-json", "mastra-jsonl"]).optional()
|
|
327
|
+
event_schema: z.enum(["claude-stream-json", "mastra-jsonl", "antigravity-stream-json"]).optional()
|
|
304
328
|
}).strict().optional();
|
|
305
329
|
var agentCliFrontmatterSchema = z.object({
|
|
306
330
|
name: z.string().min(1).max(80),
|
|
@@ -321,6 +345,26 @@ var agentCliFrontmatterSchema = z.object({
|
|
|
321
345
|
// keep this generic doctype decoupled from @agentproto/model-catalog).
|
|
322
346
|
provider: z.string().min(1).optional(),
|
|
323
347
|
authSubscription: authSubscriptionSchema.optional(),
|
|
348
|
+
// How this adapter receives a GATEWAY-routed bearer credential — see the
|
|
349
|
+
// doc on AgentCliDefinition.gatewayAuth. Distinct from a gateway preset's
|
|
350
|
+
// `keyEnv` (the providers-store lookup key) and from
|
|
351
|
+
// authSubscription.setEnv (this adapter's own native bearer).
|
|
352
|
+
gatewayAuth: z.object({
|
|
353
|
+
setEnv: z.string().min(1)
|
|
354
|
+
}).strict().optional(),
|
|
355
|
+
// Auth-derivation axis (DECISION 3): true when the adapter's api-key auth
|
|
356
|
+
// is derived from the requested model rather than a fixed `provider`
|
|
357
|
+
// (e.g. `pi`, `opencode`, `mastracode`). When set, the runtime resolver
|
|
358
|
+
// allows `"api-key"` mode on the model-derived direct endpoint and
|
|
359
|
+
// includes it in eligibility manifests for by-model routers. DISTINCT
|
|
360
|
+
// from `routeSelection` (the UI-facing route-choice axis).
|
|
361
|
+
modelDerivedApiKey: z.boolean().optional(),
|
|
362
|
+
// UI-facing route-choice axis (AIP-45 launch-menu drill-down, WP1).
|
|
363
|
+
// Absent ⇒ "free" (route is an independent choice); "derived-from-model"
|
|
364
|
+
// means the endpoint falls out of the model id's vendor prefix. DISTINCT
|
|
365
|
+
// from `modelDerivedApiKey` (the auth-derivation axis) — see the field
|
|
366
|
+
// doc on AgentCliDefinition.routeSelection.
|
|
367
|
+
routeSelection: z.enum(["free", "derived-from-model"]).optional(),
|
|
324
368
|
authEnforce: z.enum(["always", "when-configured"]).optional(),
|
|
325
369
|
sandbox: z.union([z.string(), z.record(z.string(), z.unknown())]),
|
|
326
370
|
runner: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
|
|
@@ -539,6 +583,69 @@ function toFileBasedMcpServers(servers) {
|
|
|
539
583
|
}
|
|
540
584
|
return out;
|
|
541
585
|
}
|
|
586
|
+
function defaultToolchainWritePaths() {
|
|
587
|
+
const home = homedir();
|
|
588
|
+
return [
|
|
589
|
+
join(home, "Library", "pnpm", ".tools"),
|
|
590
|
+
// macOS pnpm self-managed toolchain
|
|
591
|
+
join(home, ".local", "share", "pnpm"),
|
|
592
|
+
// Linux pnpm self-managed toolchain
|
|
593
|
+
join(home, ".npm"),
|
|
594
|
+
// npm cache — self-update / global installs
|
|
595
|
+
join(home, ".claude", "session-env")
|
|
596
|
+
// claude-code's own Bash-tool session bookkeeping
|
|
597
|
+
];
|
|
598
|
+
}
|
|
599
|
+
function defaultToolchainReadPaths() {
|
|
600
|
+
const home = homedir();
|
|
601
|
+
return [
|
|
602
|
+
join(home, ".npmrc"),
|
|
603
|
+
join(home, "Library", "Preferences", "pnpm", "rc"),
|
|
604
|
+
join(home, ".gitconfig"),
|
|
605
|
+
// git's own global config (credential.helper, user.*, …)
|
|
606
|
+
join(home, ".config", "git"),
|
|
607
|
+
// XDG git config fallback
|
|
608
|
+
join(home, ".config", "gh"),
|
|
609
|
+
// gh's config.yml + hosts.yml
|
|
610
|
+
join(home, "Library", "Keychains")
|
|
611
|
+
// macOS Keychain DB — git/gh credential retrieval (read-only; see doc above)
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
async function wrapAgentCliSpawn(bin, args, opts) {
|
|
615
|
+
const cfg = await loadAdapterSpawnSandboxConfig(opts.cwd);
|
|
616
|
+
const mode = opts.mode ?? cfg.mode;
|
|
617
|
+
if (mode === void 0) return [bin, args];
|
|
618
|
+
if (mode === "off") {
|
|
619
|
+
console.error(
|
|
620
|
+
`[agent-cli] \u26A0 spawning '${opts.label}' UNCONFINED \u2014 no OS-level sandbox is active (commandSandbox mode is "off").`
|
|
621
|
+
);
|
|
622
|
+
return [bin, args];
|
|
623
|
+
}
|
|
624
|
+
const backend = resolveCommandSandbox();
|
|
625
|
+
if (!backend) {
|
|
626
|
+
throw new Error(
|
|
627
|
+
`agent-cli '${opts.label}': commandSandbox mode="${mode}" is configured but no sandbox backend is available on ${process.platform} (macOS needs sandbox-exec, Linux needs bwrap installed). Refusing to spawn unconfined \u2014 pass commandSandbox: "off" to explicitly accept unconfined execution, or install the missing backend.`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
const policy = {
|
|
631
|
+
workspace: opts.cwd,
|
|
632
|
+
extraReadPaths: [
|
|
633
|
+
...defaultToolchainReadPaths(),
|
|
634
|
+
...cfg.extraReadPaths,
|
|
635
|
+
...opts.extraReadPaths ?? []
|
|
636
|
+
],
|
|
637
|
+
extraWritePaths: [
|
|
638
|
+
...defaultToolchainWritePaths(),
|
|
639
|
+
...cfg.extraWritePaths,
|
|
640
|
+
...opts.extraWritePaths ?? []
|
|
641
|
+
],
|
|
642
|
+
network: mode === "strict" || cfg.network === "deny" ? "deny" : "allow"
|
|
643
|
+
};
|
|
644
|
+
const wrapped = backend.wrap([bin, ...args], policy);
|
|
645
|
+
return [wrapped[0] ?? bin, wrapped.slice(1)];
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// src/protocol/print-arm.ts
|
|
542
649
|
var DEFAULT_OUTPUT = ["--output-format", "stream-json"];
|
|
543
650
|
var DEFAULT_PRE_PROMPT = ["--no-interactive"];
|
|
544
651
|
var DEFAULT_RESUME = { flag: "--resume", kind: "value" };
|
|
@@ -572,7 +679,13 @@ function createPrintSession(opts) {
|
|
|
572
679
|
} else {
|
|
573
680
|
args.push(prompt);
|
|
574
681
|
}
|
|
575
|
-
const
|
|
682
|
+
const [execBin, execArgs] = await wrapAgentCliSpawn(opts.bin, args, {
|
|
683
|
+
mode: opts.commandSandbox,
|
|
684
|
+
cwd: opts.cwd,
|
|
685
|
+
...opts.extraWritePaths ? { extraWritePaths: opts.extraWritePaths } : {},
|
|
686
|
+
label: "print-arm"
|
|
687
|
+
});
|
|
688
|
+
const child = spawn(execBin, execArgs, {
|
|
576
689
|
cwd: opts.cwd,
|
|
577
690
|
env: opts.env,
|
|
578
691
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -607,7 +720,11 @@ function createPrintSession(opts) {
|
|
|
607
720
|
if (csid) capturedSessionId = csid;
|
|
608
721
|
const sid = capturedSessionId || sessionId || "";
|
|
609
722
|
const mapped = mapEvent(evt, sid, stderrLines, eventSchema, mastraState);
|
|
610
|
-
if (
|
|
723
|
+
if (Array.isArray(mapped)) {
|
|
724
|
+
for (const m of mapped) queue.push(m);
|
|
725
|
+
} else if (mapped) {
|
|
726
|
+
queue.push(mapped);
|
|
727
|
+
}
|
|
611
728
|
});
|
|
612
729
|
rl.once("close", () => queue.end());
|
|
613
730
|
for await (const mapped of queue) {
|
|
@@ -616,7 +733,7 @@ function createPrintSession(opts) {
|
|
|
616
733
|
const exitCode = await waitForExit(child);
|
|
617
734
|
if (capturedSessionId) sessionId = capturedSessionId;
|
|
618
735
|
if (exitCode !== 0 && exitCode !== null) {
|
|
619
|
-
const binLabel = eventSchema === "mastra-jsonl" ? "mastracode" : "claude";
|
|
736
|
+
const binLabel = eventSchema === "mastra-jsonl" ? "mastracode" : eventSchema === "antigravity-stream-json" ? "agy" : "claude";
|
|
620
737
|
const errEvt = {
|
|
621
738
|
kind: "error",
|
|
622
739
|
error: {
|
|
@@ -751,6 +868,16 @@ function captureSessionId(evt, schema) {
|
|
|
751
868
|
if (evt.type === "result" && typeof evt.session_id === "string" && evt.session_id)
|
|
752
869
|
return evt.session_id;
|
|
753
870
|
return null;
|
|
871
|
+
case "antigravity-stream-json": {
|
|
872
|
+
if (evt.event === "init" && typeof evt.conversation_id === "string" && evt.conversation_id)
|
|
873
|
+
return evt.conversation_id;
|
|
874
|
+
if (evt.event === "result") {
|
|
875
|
+
const r = evt.result;
|
|
876
|
+
if (typeof r?.conversation_id === "string" && r.conversation_id)
|
|
877
|
+
return r.conversation_id;
|
|
878
|
+
}
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
754
881
|
case "mastra-jsonl":
|
|
755
882
|
if (evt.type === "result" && typeof evt.threadId === "string" && evt.threadId)
|
|
756
883
|
return evt.threadId;
|
|
@@ -761,6 +888,8 @@ function mapEvent(evt, sessionId, stderrLines, schema, mastraState) {
|
|
|
761
888
|
switch (schema) {
|
|
762
889
|
case "claude-stream-json":
|
|
763
890
|
return mapClaudeEvent(evt, sessionId, stderrLines);
|
|
891
|
+
case "antigravity-stream-json":
|
|
892
|
+
return mapAntigravityEvent(evt, sessionId, stderrLines);
|
|
764
893
|
case "mastra-jsonl": {
|
|
765
894
|
if (!mastraState) {
|
|
766
895
|
return mapClaudeEvent(evt, sessionId, stderrLines);
|
|
@@ -815,6 +944,93 @@ function mapClaudeEvent(evt, sessionId, stderrLines) {
|
|
|
815
944
|
return null;
|
|
816
945
|
}
|
|
817
946
|
}
|
|
947
|
+
function mapAntigravityEvent(evt, sessionId, stderrLines) {
|
|
948
|
+
switch (evt.event) {
|
|
949
|
+
case "step_update": {
|
|
950
|
+
const su = evt.step_update;
|
|
951
|
+
if (!su) return null;
|
|
952
|
+
const stepType = su.step_type;
|
|
953
|
+
if (stepType === "agent_response" && typeof su.text_delta === "string" && su.text_delta) {
|
|
954
|
+
return { kind: "text-delta", sessionId, text: su.text_delta };
|
|
955
|
+
}
|
|
956
|
+
if (stepType === "tool" && su.state === "DONE") {
|
|
957
|
+
const ti = su.tool_info;
|
|
958
|
+
const toolName = typeof su.tool_name === "string" && su.tool_name ? su.tool_name : typeof ti?.name === "string" && ti.name ? ti.name : "?";
|
|
959
|
+
const toolCallId = typeof su.step_index === "number" ? `step-${su.step_index}` : "";
|
|
960
|
+
const err = ti?.error;
|
|
961
|
+
const call = {
|
|
962
|
+
kind: "tool-call",
|
|
963
|
+
sessionId,
|
|
964
|
+
toolCallId,
|
|
965
|
+
toolName,
|
|
966
|
+
arguments: ti?.parameters ?? {}
|
|
967
|
+
};
|
|
968
|
+
const result = {
|
|
969
|
+
kind: "tool-result",
|
|
970
|
+
sessionId,
|
|
971
|
+
toolCallId,
|
|
972
|
+
result: err ? typeof err.message === "string" ? err.message : err : ti?.output ?? null,
|
|
973
|
+
isError: !!err
|
|
974
|
+
};
|
|
975
|
+
return [call, result];
|
|
976
|
+
}
|
|
977
|
+
return null;
|
|
978
|
+
}
|
|
979
|
+
case "result": {
|
|
980
|
+
const r = evt.result;
|
|
981
|
+
if (!r) return null;
|
|
982
|
+
const status = typeof r.status === "string" ? r.status : "";
|
|
983
|
+
const out = [];
|
|
984
|
+
const usage = mapAntigravityUsage(r.usage, sessionId);
|
|
985
|
+
if (usage) out.push(usage);
|
|
986
|
+
if (status === "ERROR" || status === "INVALID") {
|
|
987
|
+
out.push({
|
|
988
|
+
kind: "error",
|
|
989
|
+
sessionId,
|
|
990
|
+
error: {
|
|
991
|
+
message: typeof r.error === "string" && r.error ? r.error : `agy run ended with status ${status || "ERROR"}`,
|
|
992
|
+
...stderrLines.length ? { data: { stderr: stderrLines.join("\n") } } : {}
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
} else {
|
|
996
|
+
out.push({
|
|
997
|
+
kind: "turn-end",
|
|
998
|
+
sessionId,
|
|
999
|
+
reason: mapAntigravityStatus(status)
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
return out.length ? out : null;
|
|
1003
|
+
}
|
|
1004
|
+
// init and any unknown event carry no host-facing StreamEvent.
|
|
1005
|
+
default:
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function mapAntigravityStatus(raw) {
|
|
1010
|
+
switch (raw) {
|
|
1011
|
+
case "CANCELED":
|
|
1012
|
+
case "INTERRUPTED":
|
|
1013
|
+
return "cancelled";
|
|
1014
|
+
// SUCCESS, WAITING, RUNNING (or anything else non-error): the turn ended.
|
|
1015
|
+
default:
|
|
1016
|
+
return "completed";
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function mapAntigravityUsage(usage, sessionId) {
|
|
1020
|
+
if (usage === null || typeof usage !== "object") return null;
|
|
1021
|
+
const u = usage;
|
|
1022
|
+
const tokensIn = typeof u.input_tokens === "number" ? u.input_tokens : void 0;
|
|
1023
|
+
const tokensOut = typeof u.output_tokens === "number" ? u.output_tokens : void 0;
|
|
1024
|
+
if (tokensIn === void 0 && tokensOut === void 0) return null;
|
|
1025
|
+
return {
|
|
1026
|
+
kind: "usage_update",
|
|
1027
|
+
sessionId,
|
|
1028
|
+
size: 0,
|
|
1029
|
+
used: 0,
|
|
1030
|
+
...tokensIn !== void 0 ? { tokensIn } : {},
|
|
1031
|
+
...tokensOut !== void 0 ? { tokensOut } : {}
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
818
1034
|
function createMastraMapperState() {
|
|
819
1035
|
return { lastTextLength: 0 };
|
|
820
1036
|
}
|
|
@@ -1262,6 +1478,102 @@ function interpolateEnv(env, value) {
|
|
|
1262
1478
|
}
|
|
1263
1479
|
return out;
|
|
1264
1480
|
}
|
|
1481
|
+
function createArmSessionControls(arm, definition) {
|
|
1482
|
+
const modelApply = definition.models?.apply ?? "config";
|
|
1483
|
+
return {
|
|
1484
|
+
// Capability read-surface (SPEC §3.9/§3.4a) — snapshotted once
|
|
1485
|
+
// `arm.connect()` has resolved. Not live-updated by a later
|
|
1486
|
+
// `setModel`/`setSessionMode` call.
|
|
1487
|
+
availableConfigOptions: arm.availableConfigOptions ?? [],
|
|
1488
|
+
availableModes: arm.availableModes ?? [],
|
|
1489
|
+
currentModeId: arm.currentModeId,
|
|
1490
|
+
/**
|
|
1491
|
+
* Mid-session model switch — the runtime counterpart to the
|
|
1492
|
+
* spawn-time model apply. Dispatches on the same
|
|
1493
|
+
* `definition.models.apply` strategy so a live switch behaves
|
|
1494
|
+
* exactly like the spawn-time apply would have, just against an
|
|
1495
|
+
* already-running session.
|
|
1496
|
+
*/
|
|
1497
|
+
async setModel(modelId) {
|
|
1498
|
+
if (modelApply === "arg") {
|
|
1499
|
+
return { applied: false, reason: "requires-restart" };
|
|
1500
|
+
}
|
|
1501
|
+
if (modelApply === "command") {
|
|
1502
|
+
return applyModelCommand(arm, modelId);
|
|
1503
|
+
}
|
|
1504
|
+
if (!arm.setConfigOption) {
|
|
1505
|
+
return { applied: false, reason: "not-supported" };
|
|
1506
|
+
}
|
|
1507
|
+
const result = await arm.setConfigOption("model", modelId);
|
|
1508
|
+
return result.applied ? { applied: true, model: modelId } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1509
|
+
},
|
|
1510
|
+
/**
|
|
1511
|
+
* Mid-session posture switch — the native-mode counterpart to
|
|
1512
|
+
* `setModel`, wired directly to the arm's `setSessionMode` (only the
|
|
1513
|
+
* ACP arm implements it).
|
|
1514
|
+
*/
|
|
1515
|
+
async setSessionMode(modeId) {
|
|
1516
|
+
if (!arm.setSessionMode) {
|
|
1517
|
+
return { applied: false, reason: "not-supported" };
|
|
1518
|
+
}
|
|
1519
|
+
const result = await arm.setSessionMode(modeId);
|
|
1520
|
+
return result.applied ? { applied: true, modeId } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1521
|
+
},
|
|
1522
|
+
/**
|
|
1523
|
+
* Mid-session effort switch — the effort-axis counterpart to
|
|
1524
|
+
* `setModel`'s `"config"` strategy, wired to the same
|
|
1525
|
+
* `arm.setConfigOption` surface with `configId:"effort"`. Best-effort:
|
|
1526
|
+
* an effort label the current model rejects resolves
|
|
1527
|
+
* `{applied:false, reason}` (SPEC risk R7).
|
|
1528
|
+
*/
|
|
1529
|
+
async setEffort(effort) {
|
|
1530
|
+
if (!arm.setConfigOption) {
|
|
1531
|
+
return { applied: false, reason: "not-supported" };
|
|
1532
|
+
}
|
|
1533
|
+
const result = await arm.setConfigOption("effort", effort);
|
|
1534
|
+
return result.applied ? { applied: true, effort } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
async function applyModelCommand(arm, modelId) {
|
|
1539
|
+
const turnId = randomUUID();
|
|
1540
|
+
let acked = false;
|
|
1541
|
+
try {
|
|
1542
|
+
for await (const evt of promptTurn(arm, turnId, {
|
|
1543
|
+
type: "text",
|
|
1544
|
+
text: `/model ${modelId}`
|
|
1545
|
+
})) {
|
|
1546
|
+
if (/switch|model\s+set|now using/i.test(JSON.stringify(evt))) acked = true;
|
|
1547
|
+
}
|
|
1548
|
+
} catch (err) {
|
|
1549
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1550
|
+
console.warn(
|
|
1551
|
+
`[agent-cli] /model ${modelId} control turn failed (continuing on default):`,
|
|
1552
|
+
err instanceof Error ? err.message : err
|
|
1553
|
+
);
|
|
1554
|
+
return { applied: false, reason };
|
|
1555
|
+
}
|
|
1556
|
+
if (!acked) {
|
|
1557
|
+
const reason = "no switch acknowledgement \u2014 agent may be on its default model";
|
|
1558
|
+
console.warn(`[agent-cli] /model ${modelId}: ${reason}`);
|
|
1559
|
+
return { applied: false, reason };
|
|
1560
|
+
}
|
|
1561
|
+
return { applied: true, model: modelId };
|
|
1562
|
+
}
|
|
1563
|
+
async function* promptTurn(arm, turnId, message) {
|
|
1564
|
+
await arm.send(turnId, message);
|
|
1565
|
+
const stderrTail = arm._stderrTail;
|
|
1566
|
+
for await (const evt of arm.events()) {
|
|
1567
|
+
if (evt.kind === "error" && typeof stderrTail === "function") {
|
|
1568
|
+
const tail = stderrTail();
|
|
1569
|
+
if (tail) {
|
|
1570
|
+
const existing = evt.error.data ?? {};
|
|
1571
|
+
evt.error.data = { ...existing, stderr: tail };
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
yield evt;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1265
1577
|
var defineAgentCli = createDoctype(
|
|
1266
1578
|
{
|
|
1267
1579
|
aip: 45,
|
|
@@ -1311,21 +1623,11 @@ function createAgentCliRuntime(definition) {
|
|
|
1311
1623
|
definition,
|
|
1312
1624
|
async start(opts) {
|
|
1313
1625
|
const cwd = opts?.cwd ?? process.cwd();
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
};
|
|
1318
|
-
const
|
|
1319
|
-
const skipModelArg = definition.id === "codex" && codexAuthMode !== "api-key";
|
|
1320
|
-
const composed = composeSpawn(definition, opts?.config, {
|
|
1321
|
-
skipModelArg
|
|
1322
|
-
});
|
|
1323
|
-
const requestedModel = opts?.config?.options?.model;
|
|
1324
|
-
if (skipModelArg && typeof requestedModel === "string") {
|
|
1325
|
-
console.warn(
|
|
1326
|
-
`[agent-cli] codex: skipped model override "${requestedModel}" for ChatGPT-account auth; using Codex's default model.`
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1626
|
+
const contextMode = opts?.contextProfile ? (definition.modes ?? []).find(
|
|
1627
|
+
(mode) => mode.id === opts.contextProfile && mode.kind === "context"
|
|
1628
|
+
) : void 0;
|
|
1629
|
+
const config = opts?.config?.mode || !contextMode ? opts?.config : { ...opts?.config, mode: contextMode.id };
|
|
1630
|
+
const composed = composeSpawn(definition, config);
|
|
1329
1631
|
const env = {
|
|
1330
1632
|
...filterStringEnv(process.env),
|
|
1331
1633
|
...composed.env
|
|
@@ -1334,36 +1636,48 @@ function createAgentCliRuntime(definition) {
|
|
|
1334
1636
|
const authSpec = opts?.auth;
|
|
1335
1637
|
const hasGatewayAuthToken = "ANTHROPIC_AUTH_TOKEN" in composed.env;
|
|
1336
1638
|
const hasGatewayBaseUrl = "ANTHROPIC_BASE_URL" in composed.env;
|
|
1337
|
-
const
|
|
1639
|
+
const resolverCoupledGateway = authSpec?.baseUrl !== void 0;
|
|
1640
|
+
const engageAuth = !!authSpec && !hasGatewayAuthToken && (resolverCoupledGateway || !hasGatewayBaseUrl) && (authSpec.enforce === "always" || authSpec.explicit === true);
|
|
1338
1641
|
if (authSpec && engageAuth) {
|
|
1339
1642
|
for (const key of authSpec.unsetEnv) {
|
|
1340
1643
|
if (!(key in composed.env)) delete env[key];
|
|
1341
1644
|
}
|
|
1342
|
-
if (!authSpec.
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1645
|
+
if (!authSpec.externalCredential) {
|
|
1646
|
+
if (!authSpec.credential) {
|
|
1647
|
+
const slug = definition.id;
|
|
1648
|
+
const providerLabel = definition.provider ?? "<provider>";
|
|
1649
|
+
const configBlock = `{"defaults":{"adapters":{"${slug}":{"auth":{"mode":"api-key"}}}}} in ~/.agentproto/config.json`;
|
|
1650
|
+
const storeHint = authSpec.ignoredApiKeyInStore ? ` (providers.json already has a stored key for ${providerLabel} \u2014 ignored until that block exists)` : "";
|
|
1651
|
+
const supportsSub = !!definition.authSubscription;
|
|
1652
|
+
const message = authSpec.neitherConfigured && supportsSub ? `agent-cli '${slug}': no billing auth. Subscription \u2192 \`claude setup-token\`. Api-key \u2192 \`agentproto auth provider set ${providerLabel} sk-\u2026\`${storeHint}. Add ${configBlock}. Never inherited from the shell.` : authSpec.mode === "subscription" ? `agent-cli '${slug}': run \`claude setup-token\` and configure the result \u2014 bills the Max/Pro subscription, not API credits. Never inherited from the shell.` : `agent-cli '${slug}': run \`agentproto auth provider set ${providerLabel} sk-\u2026\`${storeHint}, then add ${configBlock}. Never inherited from the shell.`;
|
|
1653
|
+
throw new RuntimeConfigError("missing_auth_credential", "opts.auth.credential", message);
|
|
1654
|
+
}
|
|
1655
|
+
env[authSpec.setEnv] = authSpec.credential;
|
|
1350
1656
|
}
|
|
1351
|
-
env[authSpec.setEnv] = authSpec.credential;
|
|
1352
1657
|
}
|
|
1353
1658
|
Object.assign(env, opts?.env ?? {});
|
|
1354
1659
|
const permissionMode = resolveClaudeCodePermissionMode(
|
|
1355
1660
|
definition,
|
|
1356
|
-
opts?.
|
|
1661
|
+
opts?.posture,
|
|
1662
|
+
config?.mode
|
|
1357
1663
|
);
|
|
1358
|
-
|
|
1359
|
-
|
|
1664
|
+
const isolateClaudeCodeConfig = definition.id === "claude-code";
|
|
1665
|
+
let claudeConfigDir;
|
|
1666
|
+
if (isolateClaudeCodeConfig) {
|
|
1667
|
+
claudeConfigDir = mkdtempSync(
|
|
1360
1668
|
join(tmpdir(), "agentproto-claude-config-")
|
|
1361
1669
|
);
|
|
1362
1670
|
writeFileSync(
|
|
1363
|
-
join(
|
|
1364
|
-
JSON.stringify({
|
|
1671
|
+
join(claudeConfigDir, ".claude.json"),
|
|
1672
|
+
JSON.stringify({ mcpServers: {} })
|
|
1365
1673
|
);
|
|
1366
|
-
|
|
1674
|
+
if (permissionMode) {
|
|
1675
|
+
writeFileSync(
|
|
1676
|
+
join(claudeConfigDir, "settings.json"),
|
|
1677
|
+
JSON.stringify({ permissions: { defaultMode: permissionMode } })
|
|
1678
|
+
);
|
|
1679
|
+
}
|
|
1680
|
+
env.CLAUDE_CONFIG_DIR = claudeConfigDir;
|
|
1367
1681
|
}
|
|
1368
1682
|
if (definition.protocol === "print") {
|
|
1369
1683
|
return createPrintSession({
|
|
@@ -1373,13 +1687,25 @@ function createAgentCliRuntime(definition) {
|
|
|
1373
1687
|
env,
|
|
1374
1688
|
...opts?.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {},
|
|
1375
1689
|
...opts?.mcpServers ? { mcpServers: opts.mcpServers } : {},
|
|
1376
|
-
printConfig: definition.print
|
|
1690
|
+
printConfig: definition.print,
|
|
1691
|
+
commandSandbox: opts?.commandSandbox,
|
|
1692
|
+
...claudeConfigDir ? { extraWritePaths: [claudeConfigDir] } : {}
|
|
1377
1693
|
});
|
|
1378
1694
|
}
|
|
1379
1695
|
let child;
|
|
1380
1696
|
const stderrBuf = [];
|
|
1381
1697
|
if (definition.protocol !== "proprietary") {
|
|
1382
|
-
|
|
1698
|
+
const [execBin, execArgs] = await wrapAgentCliSpawn(
|
|
1699
|
+
definition.bin,
|
|
1700
|
+
composed.binArgs,
|
|
1701
|
+
{
|
|
1702
|
+
mode: opts?.commandSandbox,
|
|
1703
|
+
cwd,
|
|
1704
|
+
...claudeConfigDir ? { extraWritePaths: [claudeConfigDir] } : {},
|
|
1705
|
+
label: definition.id
|
|
1706
|
+
}
|
|
1707
|
+
);
|
|
1708
|
+
child = spawn(execBin, execArgs, {
|
|
1383
1709
|
cwd,
|
|
1384
1710
|
env,
|
|
1385
1711
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -1399,7 +1725,7 @@ function createAgentCliRuntime(definition) {
|
|
|
1399
1725
|
definition,
|
|
1400
1726
|
child,
|
|
1401
1727
|
cwd,
|
|
1402
|
-
|
|
1728
|
+
config?.mode,
|
|
1403
1729
|
opts?.permissionHold ?? false
|
|
1404
1730
|
);
|
|
1405
1731
|
arm._stderrTail = () => stderrBuf.join("\n");
|
|
@@ -1409,11 +1735,11 @@ function createAgentCliRuntime(definition) {
|
|
|
1409
1735
|
once: true
|
|
1410
1736
|
});
|
|
1411
1737
|
}
|
|
1412
|
-
const optModel =
|
|
1413
|
-
const optEffort =
|
|
1738
|
+
const optModel = config?.options?.model;
|
|
1739
|
+
const optEffort = config?.options?.effort;
|
|
1414
1740
|
const modelApply = definition.models?.apply ?? "config";
|
|
1415
1741
|
const configModel = optModel && modelApply === "config" ? String(optModel) : void 0;
|
|
1416
|
-
const requestedModeId =
|
|
1742
|
+
const requestedModeId = config?.mode;
|
|
1417
1743
|
const requestedModeDecl = requestedModeId ? (definition.modes ?? []).find((m) => m.id === requestedModeId) : void 0;
|
|
1418
1744
|
const configMode = requestedModeDecl?.apply === "config" ? requestedModeId : void 0;
|
|
1419
1745
|
const turnIdleTimeoutMs = opts?.turnIdleTimeoutMs ?? definition.session?.turn_idle_timeout_ms;
|
|
@@ -1438,15 +1764,11 @@ function createAgentCliRuntime(definition) {
|
|
|
1438
1764
|
return {
|
|
1439
1765
|
sessionId,
|
|
1440
1766
|
pid: child?.pid,
|
|
1441
|
-
//
|
|
1442
|
-
//
|
|
1443
|
-
//
|
|
1444
|
-
//
|
|
1445
|
-
|
|
1446
|
-
// `setModel`-style counterparts use for "not supported".
|
|
1447
|
-
availableConfigOptions: arm.availableConfigOptions ?? [],
|
|
1448
|
-
availableModes: arm.availableModes ?? [],
|
|
1449
|
-
currentModeId: arm.currentModeId,
|
|
1767
|
+
// Live model/mode/effort switches + the capability read-surface,
|
|
1768
|
+
// all pure arm delegation — shared with every host that builds
|
|
1769
|
+
// its own session over another transport. See session-controls.ts.
|
|
1770
|
+
// Snapshotted after `arm.connect()` above resolved.
|
|
1771
|
+
...createArmSessionControls(arm, definition),
|
|
1450
1772
|
send(message) {
|
|
1451
1773
|
const turnId = randomUUID();
|
|
1452
1774
|
currentTurnId = turnId;
|
|
@@ -1485,58 +1807,6 @@ function createAgentCliRuntime(definition) {
|
|
|
1485
1807
|
return arm.respondPermission(requestId, resolution);
|
|
1486
1808
|
}
|
|
1487
1809
|
} : {},
|
|
1488
|
-
/**
|
|
1489
|
-
* Mid-session model switch — the runtime counterpart to the
|
|
1490
|
-
* spawn-time `modelApply` handling above. Dispatches on the same
|
|
1491
|
-
* `definition.models.apply` strategy so a live switch behaves
|
|
1492
|
-
* exactly like the spawn-time apply would have, just against an
|
|
1493
|
-
* already-running session. See {@link SetModelResult} for the
|
|
1494
|
-
* reason vocabulary; this never throws and never tears down the
|
|
1495
|
-
* session on a rejected switch.
|
|
1496
|
-
*/
|
|
1497
|
-
async setModel(modelId) {
|
|
1498
|
-
if (modelApply === "arg") {
|
|
1499
|
-
return { applied: false, reason: "requires-restart" };
|
|
1500
|
-
}
|
|
1501
|
-
if (modelApply === "command") {
|
|
1502
|
-
return applyModelCommand(arm, modelId);
|
|
1503
|
-
}
|
|
1504
|
-
if (!arm.setConfigOption) {
|
|
1505
|
-
return { applied: false, reason: "not-supported" };
|
|
1506
|
-
}
|
|
1507
|
-
const result = await arm.setConfigOption("model", modelId);
|
|
1508
|
-
return result.applied ? { applied: true, model: modelId } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1509
|
-
},
|
|
1510
|
-
/**
|
|
1511
|
-
* Mid-session posture switch — the native-mode counterpart to
|
|
1512
|
-
* `setModel`, wired directly to the arm's `setSessionMode` (only
|
|
1513
|
-
* the ACP arm implements it). See {@link SetSessionModeResult} for
|
|
1514
|
-
* the reason vocabulary; this never throws and never tears down
|
|
1515
|
-
* the session on a rejected switch.
|
|
1516
|
-
*/
|
|
1517
|
-
async setSessionMode(modeId) {
|
|
1518
|
-
if (!arm.setSessionMode) {
|
|
1519
|
-
return { applied: false, reason: "not-supported" };
|
|
1520
|
-
}
|
|
1521
|
-
const result = await arm.setSessionMode(modeId);
|
|
1522
|
-
return result.applied ? { applied: true, modeId } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1523
|
-
},
|
|
1524
|
-
/**
|
|
1525
|
-
* Mid-session effort switch — the effort-axis counterpart to
|
|
1526
|
-
* `setModel`'s `"config"` strategy, wired to the same
|
|
1527
|
-
* `arm.setConfigOption` surface with `configId:"effort"` (only the
|
|
1528
|
-
* ACP arm implements it). Best-effort and non-fatal: an effort label
|
|
1529
|
-
* the current model rejects resolves `{applied:false, reason}` (SPEC
|
|
1530
|
-
* risk R7), never thrown and never tearing down the session. See
|
|
1531
|
-
* {@link SetEffortResult} for the reason vocabulary.
|
|
1532
|
-
*/
|
|
1533
|
-
async setEffort(effort) {
|
|
1534
|
-
if (!arm.setConfigOption) {
|
|
1535
|
-
return { applied: false, reason: "not-supported" };
|
|
1536
|
-
}
|
|
1537
|
-
const result = await arm.setConfigOption("effort", effort);
|
|
1538
|
-
return result.applied ? { applied: true, effort } : { applied: false, ...result.reason ? { reason: result.reason } : {} };
|
|
1539
|
-
},
|
|
1540
1810
|
async close() {
|
|
1541
1811
|
await arm.close();
|
|
1542
1812
|
if (child && !child.killed) child.kill("SIGTERM");
|
|
@@ -1545,45 +1815,6 @@ function createAgentCliRuntime(definition) {
|
|
|
1545
1815
|
}
|
|
1546
1816
|
};
|
|
1547
1817
|
}
|
|
1548
|
-
async function applyModelCommand(arm, modelId) {
|
|
1549
|
-
const turnId = randomUUID();
|
|
1550
|
-
let acked = false;
|
|
1551
|
-
try {
|
|
1552
|
-
for await (const evt of promptTurn(arm, turnId, {
|
|
1553
|
-
type: "text",
|
|
1554
|
-
text: `/model ${modelId}`
|
|
1555
|
-
})) {
|
|
1556
|
-
if (/switch|model\s+set|now using/i.test(JSON.stringify(evt))) acked = true;
|
|
1557
|
-
}
|
|
1558
|
-
} catch (err) {
|
|
1559
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
1560
|
-
console.warn(
|
|
1561
|
-
`[agent-cli] /model ${modelId} control turn failed (continuing on default):`,
|
|
1562
|
-
err instanceof Error ? err.message : err
|
|
1563
|
-
);
|
|
1564
|
-
return { applied: false, reason };
|
|
1565
|
-
}
|
|
1566
|
-
if (!acked) {
|
|
1567
|
-
const reason = "no switch acknowledgement \u2014 agent may be on its default model";
|
|
1568
|
-
console.warn(`[agent-cli] /model ${modelId}: ${reason}`);
|
|
1569
|
-
return { applied: false, reason };
|
|
1570
|
-
}
|
|
1571
|
-
return { applied: true, model: modelId };
|
|
1572
|
-
}
|
|
1573
|
-
async function* promptTurn(arm, turnId, message) {
|
|
1574
|
-
await arm.send(turnId, message);
|
|
1575
|
-
const stderrTail = arm._stderrTail;
|
|
1576
|
-
for await (const evt of arm.events()) {
|
|
1577
|
-
if (evt.kind === "error" && typeof stderrTail === "function") {
|
|
1578
|
-
const tail = stderrTail();
|
|
1579
|
-
if (tail) {
|
|
1580
|
-
const existing = evt.error.data ?? {};
|
|
1581
|
-
evt.error.data = { ...existing, stderr: tail };
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
yield evt;
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
1818
|
async function buildProtocolArm(def, child, cwd, requestedMode, permissionHold) {
|
|
1588
1819
|
switch (def.protocol) {
|
|
1589
1820
|
case "acp":
|
|
@@ -1610,8 +1841,18 @@ async function buildProtocolArm(def, child, cwd, requestedMode, permissionHold)
|
|
|
1610
1841
|
throw new Error("createAgentCliRuntime: print arm bypasses buildProtocolArm");
|
|
1611
1842
|
}
|
|
1612
1843
|
}
|
|
1613
|
-
function resolveClaudeCodePermissionMode(definition, configMode) {
|
|
1614
|
-
if (definition.id !== "claude-code"
|
|
1844
|
+
function resolveClaudeCodePermissionMode(definition, posture, configMode) {
|
|
1845
|
+
if (definition.id !== "claude-code") return void 0;
|
|
1846
|
+
const canonicalPermissionMode = {
|
|
1847
|
+
plan: "plan",
|
|
1848
|
+
"read-only": "plan",
|
|
1849
|
+
"accept-edits": "acceptEdits",
|
|
1850
|
+
bypass: "bypassPermissions"
|
|
1851
|
+
};
|
|
1852
|
+
if (posture && canonicalPermissionMode[posture]) {
|
|
1853
|
+
return canonicalPermissionMode[posture];
|
|
1854
|
+
}
|
|
1855
|
+
if (!configMode) return void 0;
|
|
1615
1856
|
const mode = (definition.modes ?? []).find((m) => m.id === configMode);
|
|
1616
1857
|
const args = mode?.bin_args_append ?? [];
|
|
1617
1858
|
const flagIndex = args.indexOf("--permission-mode");
|
|
@@ -1625,22 +1866,7 @@ function filterStringEnv(env) {
|
|
|
1625
1866
|
}
|
|
1626
1867
|
return out;
|
|
1627
1868
|
}
|
|
1628
|
-
function detectCodexAuthMode(env, authSpec) {
|
|
1629
|
-
if (authSpec?.credential && (authSpec.explicit === true || authSpec.enforce === "always")) {
|
|
1630
|
-
return authSpec.mode === "api-key" ? "api-key" : "chatgpt";
|
|
1631
|
-
}
|
|
1632
|
-
if (env.OPENAI_API_KEY || env.CODEX_API_KEY) return "api-key";
|
|
1633
|
-
const authPath = join(homedir(), ".codex", "auth.json");
|
|
1634
|
-
try {
|
|
1635
|
-
const parsed = JSON.parse(readFileSync(authPath, "utf8"));
|
|
1636
|
-
if (parsed.auth_mode === "api-key" || parsed.auth_mode === "chatgpt") {
|
|
1637
|
-
return parsed.auth_mode;
|
|
1638
|
-
}
|
|
1639
|
-
} catch {
|
|
1640
|
-
}
|
|
1641
|
-
return "chatgpt";
|
|
1642
|
-
}
|
|
1643
1869
|
|
|
1644
|
-
export { LEGACY_GATEWAY_MODE_IDS, LEGACY_POSTURE_MODE_IDS, RuntimeConfigError, agentCliFrontmatterSchema, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, inferLegacyModeKind, isLegacyExtractedModeId, mapMastraEvent, planModePermissionHandler, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
|
|
1645
|
-
//# sourceMappingURL=chunk-
|
|
1646
|
-
//# sourceMappingURL=chunk-
|
|
1870
|
+
export { LEGACY_GATEWAY_MODE_IDS, LEGACY_POSTURE_MODE_IDS, RuntimeConfigError, agentCliFrontmatterSchema, applyModelCommand, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createArmSessionControls, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, inferLegacyModeKind, isLegacyExtractedModeId, mapMastraEvent, planModePermissionHandler, promptTurn, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
|
|
1871
|
+
//# sourceMappingURL=chunk-TFB4QYOT.mjs.map
|
|
1872
|
+
//# sourceMappingURL=chunk-TFB4QYOT.mjs.map
|