@agentproto/driver-agent-cli 1.2.0 → 2.0.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.
@@ -6,7 +6,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, rmdirSync,
6
6
  import { createInterface } from 'readline';
7
7
  import { join } from 'path';
8
8
  import { randomUUID } from 'crypto';
9
- import { tmpdir } from 'os';
9
+ import { tmpdir, homedir } from 'os';
10
10
  import { createDoctype } from '@agentproto/define-doctype';
11
11
 
12
12
  /**
@@ -212,6 +212,14 @@ var capabilitiesSchema = z.object({
212
212
  var modeSchema = z.object({
213
213
  id: z.string().regex(MODE_ID_PATTERN),
214
214
  description: z.string().optional(),
215
+ // Which orthogonal session-config axis this mode expresses (SPEC-tracked in
216
+ // @agentproto/runtime's session-config.ts). Narrowed to only "context" (SPEC
217
+ // §3.4a): route is derived from the model catalog (@route), not a manifest
218
+ // mode, and posture comes from the harness's own ACP mode registry
219
+ // (SessionModeState.availableModes) — neither is a `modes[]` entry anymore.
220
+ // The legacy "posture"/"route" classification still exists, but only inside
221
+ // the daemon's back-compat `decomposeMode` shim, not this manifest surface.
222
+ kind: z.enum(["context"]).optional(),
215
223
  bin_args_prepend: z.array(z.string()).optional(),
216
224
  bin_args_append: z.array(z.string()).optional(),
217
225
  env: z.record(z.string(), z.string()).optional(),
@@ -438,6 +446,15 @@ function createAcpProtocolArm(options) {
438
446
  get sessionId() {
439
447
  return session?.sessionId;
440
448
  },
449
+ get availableConfigOptions() {
450
+ return session?.availableConfigOptions ?? [];
451
+ },
452
+ get availableModes() {
453
+ return session?.availableModes ?? [];
454
+ },
455
+ get currentModeId() {
456
+ return session?.currentModeId;
457
+ },
441
458
  async connect(opts) {
442
459
  const permissionHandler = options.onPermissionRequest ?? defaultPermissionHandlerForMode(options.requestedMode);
443
460
  client = await createAcpClient({
@@ -495,6 +512,14 @@ function createAcpProtocolArm(options) {
495
512
  if (!session) return;
496
513
  await session.cancel();
497
514
  },
515
+ async setConfigOption(configId, value) {
516
+ if (!session) return { applied: false, reason: "not-connected" };
517
+ return session.setConfigOption(configId, value);
518
+ },
519
+ async setSessionMode(modeId) {
520
+ if (!session) return { applied: false, reason: "not-connected" };
521
+ return session.setSessionMode(modeId);
522
+ },
498
523
  respondPermission(requestId, resolution) {
499
524
  return client?.respondPermission(requestId, resolution) ?? false;
500
525
  },
@@ -608,6 +633,33 @@ function createPrintSession(opts) {
608
633
  async cancel() {
609
634
  activeChild?.kill("SIGTERM");
610
635
  },
636
+ // The print arm spawns a fresh subprocess PER TURN from `baseArgs`,
637
+ // which already has the model baked in (composed once, before this
638
+ // session object was even constructed — see `bin_args_template`
639
+ // handling in define-agent-cli.ts/compose.ts). There is no live
640
+ // connection to apply a mid-session config change to, and rebuilding
641
+ // `baseArgs` would mean a fresh session, not a live switch — so this
642
+ // is honestly `requires-restart`, same as the `arg` model-apply
643
+ // strategy for ACP arms.
644
+ async setModel() {
645
+ return { applied: false, reason: "requires-restart" };
646
+ },
647
+ // No ACP session — no wrapper-advertised capability read-surface and
648
+ // no `session/set_mode` RPC to switch live. Mirrors the "arm has no
649
+ // mid-session config surface" case `define-agent-cli.ts` reports for
650
+ // `setModel` on other arms.
651
+ availableConfigOptions: [],
652
+ availableModes: [],
653
+ currentModeId: void 0,
654
+ async setSessionMode() {
655
+ return { applied: false, reason: "not-supported" };
656
+ },
657
+ // Same as `setSessionMode`: a print arm has no live ACP session to apply
658
+ // `set_config_option(configId:"effort")` against — effort is baked into
659
+ // the spawn argv/env, so a live change is honestly `not-supported`.
660
+ async setEffort() {
661
+ return { applied: false, reason: "not-supported" };
662
+ },
611
663
  async close() {
612
664
  activeChild?.kill("SIGTERM");
613
665
  mcpRestore?.();
@@ -767,8 +819,12 @@ function createMastraMapperState() {
767
819
  return { lastTextLength: 0 };
768
820
  }
769
821
  function extractTextFromBlocks(content) {
770
- if (!Array.isArray(content)) return "";
771
- return content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
822
+ let blocks = content;
823
+ if (blocks !== null && typeof blocks === "object" && !Array.isArray(blocks) && Array.isArray(blocks.parts)) {
824
+ blocks = blocks.parts;
825
+ }
826
+ if (!Array.isArray(blocks)) return "";
827
+ return blocks.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
772
828
  }
773
829
  function mapMastraEvent(evt, sessionId, stderrLines, state) {
774
830
  switch (evt.type) {
@@ -982,6 +1038,31 @@ async function createProprietaryProtocolArm(options) {
982
1038
  return await candidate(options.definition);
983
1039
  }
984
1040
 
1041
+ // src/legacy-modes.ts
1042
+ var LEGACY_GATEWAY_MODE_IDS = /* @__PURE__ */ new Set([
1043
+ "moonshot",
1044
+ "openrouter",
1045
+ "requesty",
1046
+ "deepseek"
1047
+ ]);
1048
+ var LEGACY_POSTURE_MODE_IDS = /* @__PURE__ */ new Set([
1049
+ "default",
1050
+ "plan",
1051
+ "accept-edits",
1052
+ "bypass-permissions",
1053
+ "read-only",
1054
+ "full-access",
1055
+ "build"
1056
+ ]);
1057
+ function inferLegacyModeKind(modeId) {
1058
+ if (LEGACY_GATEWAY_MODE_IDS.has(modeId)) return "route";
1059
+ if (LEGACY_POSTURE_MODE_IDS.has(modeId)) return "posture";
1060
+ return "context";
1061
+ }
1062
+ function isLegacyExtractedModeId(modeId) {
1063
+ return LEGACY_GATEWAY_MODE_IDS.has(modeId) || LEGACY_POSTURE_MODE_IDS.has(modeId);
1064
+ }
1065
+
985
1066
  // src/manifest/compose.ts
986
1067
  var RuntimeConfigError = class extends Error {
987
1068
  code;
@@ -993,7 +1074,7 @@ var RuntimeConfigError = class extends Error {
993
1074
  this.name = "RuntimeConfigError";
994
1075
  }
995
1076
  };
996
- function composeSpawn(handle, config) {
1077
+ function composeSpawn(handle, config, opts) {
997
1078
  if (!config)
998
1079
  return {
999
1080
  binArgs: [...handle.bin_args ?? []],
@@ -1007,14 +1088,15 @@ function composeSpawn(handle, config) {
1007
1088
  if (config.mode !== void 0) {
1008
1089
  const mode = (handle.modes ?? []).find((m) => m.id === config.mode);
1009
1090
  if (!mode) {
1010
- const known = (handle.modes ?? []).map((m) => m.id).join(", ") || "(none)";
1011
- throw new RuntimeConfigError(
1012
- "unknown_mode",
1013
- "config.mode",
1014
- `Mode '${config.mode}' is not declared by manifest '${handle.id}'. Known modes: ${known}`
1015
- );
1016
- }
1017
- if ((mode.apply ?? "bin_args") === "bin_args") {
1091
+ if (!isLegacyExtractedModeId(config.mode)) {
1092
+ const known = (handle.modes ?? []).map((m) => m.id).join(", ") || "(none)";
1093
+ throw new RuntimeConfigError(
1094
+ "unknown_mode",
1095
+ "config.mode",
1096
+ `Mode '${config.mode}' is not declared by manifest '${handle.id}'. Known modes: ${known}`
1097
+ );
1098
+ }
1099
+ } else if ((mode.apply ?? "bin_args") === "bin_args") {
1018
1100
  if (mode.bin_args_prepend) prepend.push(...mode.bin_args_prepend);
1019
1101
  if (mode.bin_args_append) append.push(...mode.bin_args_append);
1020
1102
  if (mode.env) Object.assign(env, mode.env);
@@ -1058,7 +1140,7 @@ function composeSpawn(handle, config) {
1058
1140
  );
1059
1141
  }
1060
1142
  }
1061
- if (typeof requestedModel === "string" && handle.models?.apply === "arg" && handle.models.bin_args_template) {
1143
+ if (typeof requestedModel === "string" && handle.models?.apply === "arg" && handle.models.bin_args_template && !opts?.skipModelArg) {
1062
1144
  append.push(
1063
1145
  ...handle.models.bin_args_template.map(
1064
1146
  (token) => token.replace("{model}", requestedModel)
@@ -1229,7 +1311,21 @@ function createAgentCliRuntime(definition) {
1229
1311
  definition,
1230
1312
  async start(opts) {
1231
1313
  const cwd = opts?.cwd ?? process.cwd();
1232
- const composed = composeSpawn(definition, opts?.config);
1314
+ const cliEnv = {
1315
+ ...filterStringEnv(process.env),
1316
+ ...opts?.env ?? {}
1317
+ };
1318
+ const codexAuthMode = definition.id === "codex" ? detectCodexAuthMode(cliEnv, opts?.auth) : void 0;
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
+ }
1233
1329
  const env = {
1234
1330
  ...filterStringEnv(process.env),
1235
1331
  ...composed.env
@@ -1237,7 +1333,8 @@ function createAgentCliRuntime(definition) {
1237
1333
  for (const k of composed.envUnset) delete env[k];
1238
1334
  const authSpec = opts?.auth;
1239
1335
  const hasGatewayAuthToken = "ANTHROPIC_AUTH_TOKEN" in composed.env;
1240
- const engageAuth = !!authSpec && !hasGatewayAuthToken && (authSpec.enforce === "always" || authSpec.explicit === true);
1336
+ const hasGatewayBaseUrl = "ANTHROPIC_BASE_URL" in composed.env;
1337
+ const engageAuth = !!authSpec && !hasGatewayAuthToken && !hasGatewayBaseUrl && (authSpec.enforce === "always" || authSpec.explicit === true);
1241
1338
  if (authSpec && engageAuth) {
1242
1339
  for (const key of authSpec.unsetEnv) {
1243
1340
  if (!(key in composed.env)) delete env[key];
@@ -1341,6 +1438,15 @@ function createAgentCliRuntime(definition) {
1341
1438
  return {
1342
1439
  sessionId,
1343
1440
  pid: child?.pid,
1441
+ // Capability read-surface (SPEC §3.9/§3.4a) — snapshotted once
1442
+ // `arm.connect()` above has resolved, so an ACP arm's captured
1443
+ // `newSession`/`loadSession` response is already populated. Arms
1444
+ // that don't model this (print, proprietary) leave the getters
1445
+ // undefined, defaulted here to the same empty/absent shape their
1446
+ // `setModel`-style counterparts use for "not supported".
1447
+ availableConfigOptions: arm.availableConfigOptions ?? [],
1448
+ availableModes: arm.availableModes ?? [],
1449
+ currentModeId: arm.currentModeId,
1344
1450
  send(message) {
1345
1451
  const turnId = randomUUID();
1346
1452
  currentTurnId = turnId;
@@ -1379,6 +1485,58 @@ function createAgentCliRuntime(definition) {
1379
1485
  return arm.respondPermission(requestId, resolution);
1380
1486
  }
1381
1487
  } : {},
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
+ },
1382
1540
  async close() {
1383
1541
  await arm.close();
1384
1542
  if (child && !child.killed) child.kill("SIGTERM");
@@ -1398,17 +1556,19 @@ async function applyModelCommand(arm, modelId) {
1398
1556
  if (/switch|model\s+set|now using/i.test(JSON.stringify(evt))) acked = true;
1399
1557
  }
1400
1558
  } catch (err) {
1559
+ const reason = err instanceof Error ? err.message : String(err);
1401
1560
  console.warn(
1402
1561
  `[agent-cli] /model ${modelId} control turn failed (continuing on default):`,
1403
1562
  err instanceof Error ? err.message : err
1404
1563
  );
1405
- return;
1564
+ return { applied: false, reason };
1406
1565
  }
1407
1566
  if (!acked) {
1408
- console.warn(
1409
- `[agent-cli] /model ${modelId}: no switch acknowledgement \u2014 agent may be on its default model`
1410
- );
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 };
1411
1570
  }
1571
+ return { applied: true, model: modelId };
1412
1572
  }
1413
1573
  async function* promptTurn(arm, turnId, message) {
1414
1574
  await arm.send(turnId, message);
@@ -1465,7 +1625,22 @@ function filterStringEnv(env) {
1465
1625
  }
1466
1626
  return out;
1467
1627
  }
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
+ }
1468
1643
 
1469
- export { RuntimeConfigError, agentCliFrontmatterSchema, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, mapMastraEvent, planModePermissionHandler, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
1470
- //# sourceMappingURL=chunk-TJHKDKCU.mjs.map
1471
- //# sourceMappingURL=chunk-TJHKDKCU.mjs.map
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-H43KNN32.mjs.map
1646
+ //# sourceMappingURL=chunk-H43KNN32.mjs.map