@agentproto/driver-agent-cli 0.3.0 → 0.4.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.
@@ -164,6 +164,10 @@ var sessionSchema = z.object({
164
164
  var modelsSchema = z.object({
165
165
  default: z.string().optional(),
166
166
  allowed: z.array(z.string()).optional(),
167
+ // Model-id patterns the adapter must never route to (case-insensitive,
168
+ // trailing `*` = prefix match). Enforced at compose time — see
169
+ // AgentCliModels.deny. Reserves premium providers for dedicated adapters.
170
+ deny: z.array(z.string()).optional(),
167
171
  env: z.record(z.string(), z.string()).optional(),
168
172
  // How a model is selected at session start: "config" (ACP
169
173
  // set_config_option, default) | "command" (a `/model <id>` control turn,
@@ -190,8 +194,21 @@ var capabilitiesSchema = z.object({
190
194
  var modeSchema = z.object({
191
195
  id: z.string().regex(MODE_ID_PATTERN),
192
196
  description: z.string().optional(),
197
+ bin_args_prepend: z.array(z.string()).optional(),
193
198
  bin_args_append: z.array(z.string()).optional(),
194
- env: z.record(z.string(), z.string()).optional()
199
+ env: z.record(z.string(), z.string()).optional(),
200
+ // Env keys to DELETE from the spawn env (auth hygiene for gateway
201
+ // modes — see AgentCliMode.env_unset). Applied at the runtime merge
202
+ // point, not as a static set.
203
+ env_unset: z.array(z.string()).optional(),
204
+ // Honest support status surfaced to clients. Absent ⇒ "active".
205
+ status: z.enum(["active", "noop", "planned"]).optional(),
206
+ status_note: z.string().optional(),
207
+ // How the host activates this mode: "bin_args" (default, argv/env
208
+ // composed at spawn) | "config" (no CLI surface — forwarded to the ACP
209
+ // arm's connect({mode}) and applied via session/set_config_option
210
+ // configId:"mode", e.g. opencode).
211
+ apply: z.enum(["bin_args", "config"]).optional()
195
212
  }).strict();
196
213
  var optionSchema = z.object({
197
214
  id: z.string().regex(OPTION_ID_PATTERN),
@@ -201,9 +218,14 @@ var optionSchema = z.object({
201
218
  default: z.union([z.boolean(), z.number(), z.string()]).optional(),
202
219
  min: z.number().int().optional(),
203
220
  max: z.number().int().optional(),
221
+ bin_args_prepend: z.array(z.string()).optional(),
204
222
  bin_args_template: z.array(z.string()).optional(),
205
223
  bin_args_append_when_true: z.array(z.string()).optional(),
206
- env: z.record(z.string(), z.string()).optional()
224
+ env: z.record(z.string(), z.string()).optional(),
225
+ // Env keys to DELETE from the spawn env when this option is active
226
+ // (auth hygiene for value-bearing gateway options — see
227
+ // AgentCliOption.env_unset). Symmetric with modeSchema.env_unset.
228
+ env_unset: z.array(z.string()).optional()
207
229
  }).strict().refine(
208
230
  // type === "enum" REQUIRES an `enum` array. The JSON Schema enforces
209
231
  // this via `if/then`; the zod side mirrors with a refine so both
@@ -211,6 +233,17 @@ var optionSchema = z.object({
211
233
  (o) => o.type !== "enum" || Array.isArray(o.enum) && o.enum.length > 0,
212
234
  { message: "option.type === 'enum' requires a non-empty `enum` array" }
213
235
  );
236
+ var presetSchema = z.object({
237
+ id: z.string().min(1),
238
+ label: z.string().min(1),
239
+ description: z.string().optional(),
240
+ schemaFlavor: z.enum(["anthropic", "openai"]),
241
+ baseUrl: z.string().min(1),
242
+ keyEnv: z.string().min(1),
243
+ scrubEnv: z.array(z.string()).optional(),
244
+ defaultModel: z.string().optional(),
245
+ homepage: z.string().optional()
246
+ }).strict();
214
247
  var continuationStrategyIdSchema = z.enum(CONTINUATION_STRATEGY_IDS);
215
248
  var continuationSchema = z.object({
216
249
  default: continuationStrategyIdSchema,
@@ -266,6 +299,9 @@ var agentCliFrontmatterSchema = z.object({
266
299
  capabilities: capabilitiesSchema.optional(),
267
300
  modes: z.array(modeSchema).optional(),
268
301
  options: z.array(optionSchema).optional(),
302
+ // AIP-45 gateway presets this adapter can drive (merged into the
303
+ // provider-preset catalog; built-in wins on id collision).
304
+ presets: z.array(presetSchema).optional(),
269
305
  continuation: continuationSchema.optional(),
270
306
  requires: z.object({
271
307
  os: z.array(z.enum(["darwin", "linux", "windows"])).optional(),
@@ -406,7 +442,8 @@ function createAcpProtocolArm(options) {
406
442
  cwd,
407
443
  mcpServers: opts.mcpServers,
408
444
  ...opts.model ? { model: opts.model } : {},
409
- ...opts.effort ? { effort: opts.effort } : {}
445
+ ...opts.effort ? { effort: opts.effort } : {},
446
+ ...opts.mode ? { mode: opts.mode } : {}
410
447
  });
411
448
  }
412
449
  },
@@ -493,22 +530,26 @@ function createPrintSession(opts) {
493
530
  try {
494
531
  if (!child.stdout)
495
532
  throw new Error("print-arm: child has no stdout pipe");
496
- const rl = createInterface({ input: child.stdout, crlfDelay: Infinity });
497
533
  let capturedSessionId = "";
498
534
  const mastraState = eventSchema === "mastra-jsonl" ? createMastraMapperState() : void 0;
499
- for await (const line of rl) {
500
- if (!line.trim()) continue;
535
+ const rl = createInterface({ input: child.stdout, crlfDelay: Infinity });
536
+ const queue = new EventQueue();
537
+ rl.on("line", (line) => {
538
+ if (!line.trim()) return;
501
539
  let evt;
502
540
  try {
503
541
  evt = JSON.parse(line);
504
542
  } catch {
505
- continue;
543
+ return;
506
544
  }
507
545
  const csid = captureSessionId(evt, eventSchema);
508
546
  if (csid) capturedSessionId = csid;
509
547
  const sid = capturedSessionId || sessionId || "";
510
548
  const mapped = mapEvent(evt, sid, stderrLines, eventSchema, mastraState);
511
- if (!mapped) continue;
549
+ if (mapped) queue.push(mapped);
550
+ });
551
+ rl.once("close", () => queue.end());
552
+ for await (const mapped of queue) {
512
553
  yield mapped;
513
554
  }
514
555
  const exitCode = await waitForExit(child);
@@ -774,6 +815,37 @@ function mapMastraEvent(evt, sessionId, stderrLines, state) {
774
815
  }
775
816
  };
776
817
  }
818
+ // ── Token usage ─────────────────────────────────────────────
819
+ // Mastra Code DOES expose usage: its `AgentController` emits a native
820
+ // `usage_update` event ({ type: "usage_update", usage: TokenUsage })
821
+ // on every model step-finish — verified against @mastra/core 1.48.0's
822
+ // AgentControllerEvent union and its emit site
823
+ // (`this.#session.emit({ type: "usage_update", usage: stepUsage })`).
824
+ // `TokenUsage` carries `{ promptTokens, completionTokens, totalTokens }`.
825
+ // Map it to this repo's `usage_update` StreamEvent (the SAME shape
826
+ // claude-code emits over ACP) so mastracode sessions — both the print
827
+ // arm and the in-process arm, which share this mapper — carry token
828
+ // telemetry in the transcript. mastracode reports these cumulatively
829
+ // (its own headless `runMC` result-usage is last-write-wins over these
830
+ // events), and last-write-wins on the descriptor matches that. It
831
+ // carries no context-window size/used or per-turn cost here, so those
832
+ // stay 0 / absent (projectEvent guards on >0, never clobbering a real
833
+ // size).
834
+ case "usage_update": {
835
+ const usage = evt.usage;
836
+ const promptTokens = typeof usage?.promptTokens === "number" ? usage.promptTokens : void 0;
837
+ const completionTokens = typeof usage?.completionTokens === "number" ? usage.completionTokens : void 0;
838
+ if (promptTokens === void 0 && completionTokens === void 0)
839
+ return null;
840
+ return {
841
+ kind: "usage_update",
842
+ sessionId,
843
+ size: 0,
844
+ used: 0,
845
+ ...promptTokens !== void 0 ? { tokensIn: promptTokens } : {},
846
+ ...completionTokens !== void 0 ? { tokensOut: completionTokens } : {}
847
+ };
848
+ }
777
849
  // ── Shell output (tool-like) ────────────────────────────────
778
850
  case "shell_output":
779
851
  return typeof evt.output === "string" ? {
@@ -794,6 +866,45 @@ function mapMastraEvent(evt, sessionId, stderrLines, state) {
794
866
  return null;
795
867
  }
796
868
  }
869
+ var QUEUE_HIGH_WATER_MARK = 1e4;
870
+ var EventQueue = class {
871
+ buffer = [];
872
+ resolveNext = null;
873
+ ended = false;
874
+ highWaterWarned = false;
875
+ push(evt) {
876
+ this.buffer.push(evt);
877
+ if (this.buffer.length > QUEUE_HIGH_WATER_MARK && !this.highWaterWarned) {
878
+ this.highWaterWarned = true;
879
+ console.warn(
880
+ `print-arm: event backlog exceeded ${QUEUE_HIGH_WATER_MARK} buffered events \u2014 downstream consumer is not keeping up. The child's stdout is still being drained (no ENOBUFS), but memory will grow until the consumer catches up.`
881
+ );
882
+ }
883
+ const resolve = this.resolveNext;
884
+ this.resolveNext = null;
885
+ resolve?.();
886
+ }
887
+ /** Signal that no further events will be pushed. */
888
+ end() {
889
+ this.ended = true;
890
+ const resolve = this.resolveNext;
891
+ this.resolveNext = null;
892
+ resolve?.();
893
+ }
894
+ async *[Symbol.asyncIterator]() {
895
+ for (; ; ) {
896
+ const evt = this.buffer.shift();
897
+ if (evt !== void 0) {
898
+ yield evt;
899
+ continue;
900
+ }
901
+ if (this.ended) return;
902
+ await new Promise((resolve) => {
903
+ this.resolveNext = resolve;
904
+ });
905
+ }
906
+ }
907
+ };
797
908
  function mapMastraFinishReason(raw) {
798
909
  switch (raw) {
799
910
  case "complete":
@@ -847,9 +958,11 @@ var RuntimeConfigError = class extends Error {
847
958
  }
848
959
  };
849
960
  function composeSpawn(handle, config) {
850
- const binArgs = [...handle.bin_args ?? []];
961
+ if (!config) return { binArgs: [...handle.bin_args ?? []], env: {}, envUnset: [] };
962
+ const prepend = [];
963
+ const append = [];
851
964
  const env = {};
852
- if (!config) return { binArgs, env };
965
+ const envUnset = [];
853
966
  if (config.mode !== void 0) {
854
967
  const mode = (handle.modes ?? []).find((m) => m.id === config.mode);
855
968
  if (!mode) {
@@ -860,8 +973,12 @@ function composeSpawn(handle, config) {
860
973
  `Mode '${config.mode}' is not declared by manifest '${handle.id}'. Known modes: ${known}`
861
974
  );
862
975
  }
863
- if (mode.bin_args_append) binArgs.push(...mode.bin_args_append);
864
- if (mode.env) Object.assign(env, mode.env);
976
+ if ((mode.apply ?? "bin_args") === "bin_args") {
977
+ if (mode.bin_args_prepend) prepend.push(...mode.bin_args_prepend);
978
+ if (mode.bin_args_append) append.push(...mode.bin_args_append);
979
+ if (mode.env) Object.assign(env, mode.env);
980
+ if (mode.env_unset) envUnset.push(...mode.env_unset);
981
+ }
865
982
  }
866
983
  if (config.options && Object.keys(config.options).length > 0) {
867
984
  const declared = handle.options ?? [];
@@ -881,8 +998,23 @@ function composeSpawn(handle, config) {
881
998
  const value = config.options[option.id];
882
999
  validateOptionValue(option, value);
883
1000
  const patch = renderOptionPatch(option, value);
884
- binArgs.push(...patch.binArgs);
1001
+ prepend.push(...patch.prepend);
1002
+ append.push(...patch.append);
885
1003
  Object.assign(env, patch.env);
1004
+ if (patch.envUnset.length) envUnset.push(...patch.envUnset);
1005
+ }
1006
+ }
1007
+ const requestedModel = config.options?.model;
1008
+ if (typeof requestedModel === "string" && handle.models?.deny?.length) {
1009
+ const denyHit = handle.models.deny.find(
1010
+ (pattern) => matchesModelPattern(pattern, requestedModel)
1011
+ );
1012
+ if (denyHit) {
1013
+ throw new RuntimeConfigError(
1014
+ "model_denied",
1015
+ "config.options.model",
1016
+ `Model '${requestedModel}' is denied by manifest '${handle.id}' (matches deny pattern '${denyHit}'). This adapter deliberately does not route to that provider \u2014 use a permitted model or a different adapter.`
1017
+ );
886
1018
  }
887
1019
  }
888
1020
  if (config.continuation !== void 0 && handle.continuation) {
@@ -894,12 +1026,21 @@ function composeSpawn(handle, config) {
894
1026
  );
895
1027
  }
896
1028
  }
897
- return { binArgs, env };
1029
+ return {
1030
+ binArgs: [...prepend, ...handle.bin_args ?? [], ...append],
1031
+ env,
1032
+ envUnset
1033
+ };
898
1034
  }
899
1035
  function resolveContinuationStrategy(handle, config) {
900
1036
  if (config?.continuation) return config.continuation;
901
1037
  return handle.continuation?.default ?? "none";
902
1038
  }
1039
+ function matchesModelPattern(pattern, modelId) {
1040
+ const p = pattern.toLowerCase();
1041
+ const m = modelId.toLowerCase();
1042
+ return p.endsWith("*") ? m.startsWith(p.slice(0, -1)) : m === p;
1043
+ }
903
1044
  function validateOptionValue(option, value) {
904
1045
  const path = `config.options.${option.id}`;
905
1046
  switch (option.type) {
@@ -965,17 +1106,23 @@ function validateOptionValue(option, value) {
965
1106
  function renderOptionPatch(option, value) {
966
1107
  const stringValue = String(value);
967
1108
  if (option.type === "boolean") {
968
- if (value !== true) return { binArgs: [], env: {} };
1109
+ if (value !== true) return { prepend: [], append: [], env: {}, envUnset: [] };
969
1110
  return {
970
- binArgs: option.bin_args_append_when_true ? [...option.bin_args_append_when_true] : [],
971
- env: option.env ? interpolateEnv(option.env, stringValue) : {}
1111
+ prepend: [],
1112
+ append: option.bin_args_append_when_true ? [...option.bin_args_append_when_true] : [],
1113
+ env: option.env ? interpolateEnv(option.env, stringValue) : {},
1114
+ envUnset: option.env_unset ? [...option.env_unset] : []
972
1115
  };
973
1116
  }
974
1117
  return {
975
- binArgs: option.bin_args_template ? option.bin_args_template.map(
1118
+ prepend: option.bin_args_prepend ? option.bin_args_prepend.map(
1119
+ (token) => token.replace(/\{value\}/g, stringValue)
1120
+ ) : [],
1121
+ append: option.bin_args_template ? option.bin_args_template.map(
976
1122
  (token) => token.replace(/\{value\}/g, stringValue)
977
1123
  ) : [],
978
- env: option.env ? interpolateEnv(option.env, stringValue) : {}
1124
+ env: option.env ? interpolateEnv(option.env, stringValue) : {},
1125
+ envUnset: option.env_unset ? [...option.env_unset] : []
979
1126
  };
980
1127
  }
981
1128
  function interpolateEnv(env, value) {
@@ -1032,9 +1179,10 @@ function createAgentCliRuntime(definition) {
1032
1179
  const composed = composeSpawn(definition, opts?.config);
1033
1180
  const env = {
1034
1181
  ...filterStringEnv(process.env),
1035
- ...composed.env,
1036
- ...opts?.env ?? {}
1182
+ ...composed.env
1037
1183
  };
1184
+ for (const k of composed.envUnset) delete env[k];
1185
+ Object.assign(env, opts?.env ?? {});
1038
1186
  const permissionMode = resolveClaudeCodePermissionMode(
1039
1187
  definition,
1040
1188
  opts?.config?.mode
@@ -1091,6 +1239,9 @@ function createAgentCliRuntime(definition) {
1091
1239
  const optEffort = opts?.config?.options?.effort;
1092
1240
  const modelApply = definition.models?.apply ?? "config";
1093
1241
  const configModel = optModel && modelApply === "config" ? String(optModel) : void 0;
1242
+ const requestedModeId = opts?.config?.mode;
1243
+ const requestedModeDecl = requestedModeId ? (definition.modes ?? []).find((m) => m.id === requestedModeId) : void 0;
1244
+ const configMode = requestedModeDecl?.apply === "config" ? requestedModeId : void 0;
1094
1245
  const turnIdleTimeoutMs = opts?.turnIdleTimeoutMs ?? definition.session?.turn_idle_timeout_ms;
1095
1246
  await arm.connect({
1096
1247
  cwd,
@@ -1100,6 +1251,7 @@ function createAgentCliRuntime(definition) {
1100
1251
  ...opts?.mcpServers ? { mcpServers: opts.mcpServers } : {},
1101
1252
  ...configModel ? { model: configModel } : {},
1102
1253
  ...optEffort ? { effort: String(optEffort) } : {},
1254
+ ...configMode ? { mode: configMode } : {},
1103
1255
  ...opts?.onActivity ? { onActivity: opts.onActivity } : {},
1104
1256
  ...turnIdleTimeoutMs !== void 0 ? { turnIdleTimeoutMs } : {}
1105
1257
  });
@@ -1204,5 +1356,5 @@ function filterStringEnv(env) {
1204
1356
  }
1205
1357
 
1206
1358
  export { RuntimeConfigError, agentCliFrontmatterSchema, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, mapMastraEvent, planModePermissionHandler, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
1207
- //# sourceMappingURL=chunk-KHHRPQ2Q.mjs.map
1208
- //# sourceMappingURL=chunk-KHHRPQ2Q.mjs.map
1359
+ //# sourceMappingURL=chunk-L6BRNMVP.mjs.map
1360
+ //# sourceMappingURL=chunk-L6BRNMVP.mjs.map