@agentproto/driver-agent-cli 2.7.1 → 2.9.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.
@@ -2,7 +2,7 @@ import { z } from 'zod';
2
2
  import { Writable, Readable } from 'stream';
3
3
  import { createAcpClient } from '@agentproto/acp/client';
4
4
  import { spawn, execFile } from 'child_process';
5
- import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, rmdirSync, realpathSync, mkdtempSync } from 'fs';
5
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, rmdirSync, realpathSync, mkdtempSync, lstatSync, unlinkSync, symlinkSync } from 'fs';
6
6
  import { createInterface } from 'readline';
7
7
  import { join, isAbsolute, resolve, delimiter, dirname, basename } from 'path';
8
8
  import { tmpdir, homedir } from 'os';
@@ -242,6 +242,13 @@ var capabilitiesSchema = z.object({
242
242
  * as a PTY session. Distinct from ACP-level `resumable`.
243
243
  */
244
244
  nativeTerminalResume: z.boolean().optional(),
245
+ /**
246
+ * Harness defers mounted MCP tools behind its own native search tool
247
+ * (Claude Code `ToolSearch`). Makes the daemon's `/mcp` self-mount default
248
+ * to eager for this adapter instead of double-deferring behind the
249
+ * daemon's `tool_search`. Defaults to false (conservative).
250
+ */
251
+ nativeToolSearch: z.boolean().optional(),
245
252
  /**
246
253
  * Adapter can ingest a filesystem path that the host UI just placed
247
254
  * on disk (host-side drag-drop into a terminal pastes the path
@@ -385,6 +392,14 @@ var agentCliFrontmatterSchema = z.object({
385
392
  routeSelection: z.enum(["free", "derived-from-model"]).optional(),
386
393
  authEnforce: z.enum(["always", "when-configured"]).optional(),
387
394
  sandbox: z.union([z.string(), z.record(z.string(), z.unknown())]),
395
+ stateHome: z.object({
396
+ env: z.string().regex(/^[A-Z][A-Z0-9_]*$/),
397
+ defaultDir: z.string().min(1).refine((d) => !d.startsWith("/") && !d.split("/").includes(".."), {
398
+ message: "defaultDir must be relative to $HOME and stay inside it"
399
+ }),
400
+ share: z.array(z.string().regex(/^[^/]+$/)).optional(),
401
+ seed: z.record(z.string().regex(/^[^/]+$/).refine((n) => n !== "." && n !== "..", { message: "seed names a file of the home" }), z.string()).optional()
402
+ }).strict().optional(),
388
403
  runner: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),
389
404
  protocol: z.enum(["acp", "mcp", "proprietary", "print"]),
390
405
  acp: z.string().optional(),
@@ -713,15 +728,15 @@ function isAlive(pid) {
713
728
  }
714
729
  }
715
730
  function listProcesses() {
716
- return new Promise((resolve2) => {
731
+ return new Promise((resolve3) => {
717
732
  execFile("ps", ["-axo", "pid=,ppid="], { maxBuffer: 32 * 1024 * 1024 }, (err, stdout) => {
718
- if (err) return resolve2([]);
733
+ if (err) return resolve3([]);
719
734
  const out = [];
720
735
  for (const line of stdout.split("\n")) {
721
736
  const m = line.match(/^\s*(\d+)\s+(\d+)\s*$/);
722
737
  if (m) out.push({ pid: Number(m[1]), ppid: Number(m[2]) });
723
738
  }
724
- resolve2(out);
739
+ resolve3(out);
725
740
  });
726
741
  });
727
742
  }
@@ -770,9 +785,17 @@ function defaultToolchainReadPaths() {
770
785
  // macOS Keychain DB — git/gh credential retrieval (read-only; see doc above)
771
786
  ];
772
787
  }
788
+ function effectiveSpawnSandboxMode(opts, configured) {
789
+ return opts.zones ? opts.mode ?? "workspace" : opts.mode ?? configured;
790
+ }
791
+ async function willConfineAgentCliSpawn(opts) {
792
+ const cfg = await loadAdapterSpawnSandboxConfig(opts.cwd);
793
+ const mode = effectiveSpawnSandboxMode(opts, cfg.mode);
794
+ return mode !== void 0 && mode !== "off";
795
+ }
773
796
  async function wrapAgentCliSpawn(bin, args, opts) {
774
797
  const cfg = await loadAdapterSpawnSandboxConfig(opts.cwd);
775
- const mode = opts.zones ? opts.mode ?? "workspace" : opts.mode ?? cfg.mode;
798
+ const mode = effectiveSpawnSandboxMode(opts, cfg.mode);
776
799
  if (mode === void 0) return [bin, args];
777
800
  if (mode === "off" && opts.zones) {
778
801
  throw new Error(
@@ -873,11 +896,11 @@ function createPrintSession(opts) {
873
896
  });
874
897
  activeChild = child;
875
898
  let spawnFailure;
876
- await new Promise((resolve2) => {
877
- child.once("spawn", () => resolve2());
899
+ await new Promise((resolve3) => {
900
+ child.once("spawn", () => resolve3());
878
901
  child.once("error", (err) => {
879
902
  spawnFailure = err;
880
- resolve2();
903
+ resolve3();
881
904
  });
882
905
  });
883
906
  if (spawnFailure) {
@@ -1496,16 +1519,16 @@ var EventQueue = class {
1496
1519
  `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.`
1497
1520
  );
1498
1521
  }
1499
- const resolve2 = this.resolveNext;
1522
+ const resolve3 = this.resolveNext;
1500
1523
  this.resolveNext = null;
1501
- resolve2?.();
1524
+ resolve3?.();
1502
1525
  }
1503
1526
  /** Signal that no further events will be pushed. */
1504
1527
  end() {
1505
1528
  this.ended = true;
1506
- const resolve2 = this.resolveNext;
1529
+ const resolve3 = this.resolveNext;
1507
1530
  this.resolveNext = null;
1508
- resolve2?.();
1531
+ resolve3?.();
1509
1532
  }
1510
1533
  async *[Symbol.asyncIterator]() {
1511
1534
  for (; ; ) {
@@ -1515,8 +1538,8 @@ var EventQueue = class {
1515
1538
  continue;
1516
1539
  }
1517
1540
  if (this.ended) return;
1518
- await new Promise((resolve2) => {
1519
- this.resolveNext = resolve2;
1541
+ await new Promise((resolve3) => {
1542
+ this.resolveNext = resolve3;
1520
1543
  });
1521
1544
  }
1522
1545
  }
@@ -1535,9 +1558,9 @@ function mapMastraFinishReason(raw) {
1535
1558
  }
1536
1559
  }
1537
1560
  function waitForExit(child) {
1538
- return new Promise((resolve2) => {
1539
- child.once("exit", (code) => resolve2(code));
1540
- child.once("error", () => resolve2(null));
1561
+ return new Promise((resolve3) => {
1562
+ child.once("exit", (code) => resolve3(code));
1563
+ child.once("error", () => resolve3(null));
1541
1564
  });
1542
1565
  }
1543
1566
  async function createProprietaryProtocolArm(options) {
@@ -1910,20 +1933,132 @@ async function applyModelCommand(arm, modelId) {
1910
1933
  }
1911
1934
  return { applied: true, model: modelId };
1912
1935
  }
1913
- async function* promptTurn(arm, turnId, message) {
1914
- await arm.send(turnId, message);
1936
+ function parseLogfmtLine(line) {
1937
+ const out = {};
1938
+ const re = /([A-Za-z0-9_.-]+)=("(?:[^"\\]|\\.)*"|\S+)/g;
1939
+ let m;
1940
+ while ((m = re.exec(line)) !== null) {
1941
+ const key = m[1];
1942
+ let value = m[2];
1943
+ if (value.startsWith('"')) {
1944
+ value = value.slice(1, -1).replace(/\\(.)/g, "$1");
1945
+ }
1946
+ out[key] = value;
1947
+ }
1948
+ return out;
1949
+ }
1950
+ function providerMessageFromError(raw) {
1951
+ const m = raw.match(/^[A-Za-z_][\w.]*(?:Error|Exception)\s*:\s*([\s\S]+)$/);
1952
+ return (m ? m[1] : raw).trim();
1953
+ }
1954
+ function parseStderrStreamError(line) {
1955
+ if (!line.includes("stream error")) return void 0;
1956
+ const fields = parseLogfmtLine(line);
1957
+ if ((fields.level ?? "").toUpperCase() !== "ERROR") return void 0;
1958
+ if (fields.message !== "stream error") return void 0;
1959
+ if (fields.small === "true") return void 0;
1960
+ if (fields.agent === "title") return void 0;
1961
+ const raw = fields["error.error"] ?? fields.error;
1962
+ if (!raw) return void 0;
1963
+ return providerMessageFromError(raw);
1964
+ }
1965
+ async function* promptTurn(arm, turnId, message, stderrTurnError) {
1915
1966
  const stderrTail = arm._stderrTail;
1916
- for await (const evt of arm.events()) {
1917
- if (evt.kind === "error" && typeof stderrTail === "function") {
1918
- const tail = stderrTail();
1919
- if (tail) {
1920
- const existing = evt.error.data ?? {};
1921
- evt.error.data = { ...existing, stderr: tail };
1967
+ const pendingErrors = [];
1968
+ let wake;
1969
+ const unsubscribe = stderrTurnError && arm._onStderrLine ? arm._onStderrLine((line) => {
1970
+ const parsed = stderrTurnError(line);
1971
+ if (parsed === void 0) return;
1972
+ pendingErrors.push(parsed);
1973
+ wake?.();
1974
+ wake = void 0;
1975
+ }) : void 0;
1976
+ let iterator;
1977
+ try {
1978
+ await arm.send(turnId, message);
1979
+ iterator = arm.events()[Symbol.asyncIterator]();
1980
+ while (true) {
1981
+ if (pendingErrors.length > 0) {
1982
+ await arm.cancel(turnId).catch(() => {
1983
+ });
1984
+ yield {
1985
+ kind: "error",
1986
+ sessionId: arm.sessionId,
1987
+ error: {
1988
+ message: pendingErrors.join("\n"),
1989
+ data: { stderr: stderrTail?.() ?? "" }
1990
+ }
1991
+ };
1992
+ yield {
1993
+ kind: "turn-end",
1994
+ sessionId: arm.sessionId ?? "",
1995
+ reason: "error"
1996
+ };
1997
+ return;
1998
+ }
1999
+ const nextEvent = iterator.next();
2000
+ const stderrArrived = new Promise((resolve3) => {
2001
+ wake = resolve3;
2002
+ });
2003
+ const winner = await Promise.race([
2004
+ nextEvent.then((result) => ({ source: "event", result })),
2005
+ stderrArrived.then(() => ({ source: "stderr" }))
2006
+ ]);
2007
+ if (winner.source === "stderr") continue;
2008
+ if (winner.result.done) return;
2009
+ const evt = winner.result.value;
2010
+ if (evt.kind === "error" && typeof stderrTail === "function") {
2011
+ const tail = stderrTail();
2012
+ if (tail) {
2013
+ const existing = evt.error.data ?? {};
2014
+ evt.error.data = { ...existing, stderr: tail };
2015
+ }
1922
2016
  }
2017
+ yield evt;
1923
2018
  }
1924
- yield evt;
2019
+ } finally {
2020
+ unsubscribe?.();
2021
+ void iterator?.return?.()?.catch?.(() => {
2022
+ });
1925
2023
  }
1926
2024
  }
2025
+ function realStateHome(stateHome, env) {
2026
+ const fromEnv = env[stateHome.env];
2027
+ if (fromEnv && isAbsolute(fromEnv)) return fromEnv;
2028
+ return resolve(homedir(), stateHome.defaultDir);
2029
+ }
2030
+ function prepareIsolatedStateHome(stateHome, opts) {
2031
+ const real = realStateHome(stateHome, opts.env);
2032
+ let dir;
2033
+ if (opts.configDir) {
2034
+ dir = opts.configDir;
2035
+ mkdirSync(dir, { recursive: true });
2036
+ } else {
2037
+ dir = mkdtempSync(join(tmpdir(), `agentproto-${stateHome.env.toLowerCase()}-`));
2038
+ }
2039
+ const writePaths = [dir];
2040
+ for (const name of stateHome.share ?? []) {
2041
+ const target = join(real, name);
2042
+ const link = join(dir, name);
2043
+ try {
2044
+ lstatSync(link);
2045
+ unlinkSync(link);
2046
+ } catch {
2047
+ }
2048
+ if (!existsSync(target)) continue;
2049
+ symlinkSync(target, link);
2050
+ writePaths.push(realpathSync(target));
2051
+ }
2052
+ for (const [name, content] of Object.entries(stateHome.seed ?? {})) {
2053
+ const file = join(dir, name);
2054
+ try {
2055
+ unlinkSync(file);
2056
+ } catch {
2057
+ }
2058
+ writeFileSync(file, content);
2059
+ }
2060
+ return { dir, writePaths };
2061
+ }
1927
2062
  function npxCacheKey(packages) {
1928
2063
  return createHash("sha512").update([...packages].sort((a, b) => a.localeCompare(b, "en")).join("\n")).digest("hex").slice(0, 16);
1929
2064
  }
@@ -2130,6 +2265,15 @@ function createAgentCliRuntime(definition) {
2130
2265
  );
2131
2266
  env.CLAUDE_CONFIG_DIR = claudeConfigDir;
2132
2267
  }
2268
+ const extraWritePaths = claudeConfigDir ? [claudeConfigDir] : [];
2269
+ if (definition.stateHome && definition.protocol !== "proprietary" && await willConfineAgentCliSpawn({ mode: opts?.commandSandbox, cwd, zones: opts?.fsZones })) {
2270
+ const isolated = prepareIsolatedStateHome(definition.stateHome, {
2271
+ ...opts?.configDir ? { configDir: opts.configDir } : {},
2272
+ env
2273
+ });
2274
+ env[definition.stateHome.env] = isolated.dir;
2275
+ extraWritePaths.push(...isolated.writePaths);
2276
+ }
2133
2277
  if (definition.protocol === "print") {
2134
2278
  const printModel = config?.options?.model;
2135
2279
  return createPrintSession({
@@ -2142,7 +2286,7 @@ function createAgentCliRuntime(definition) {
2142
2286
  printConfig: definition.print,
2143
2287
  commandSandbox: opts?.commandSandbox,
2144
2288
  ...opts?.fsZones ? { fsZones: opts.fsZones } : {},
2145
- ...claudeConfigDir ? { extraWritePaths: [claudeConfigDir] } : {},
2289
+ ...extraWritePaths.length ? { extraWritePaths } : {},
2146
2290
  // Exact-file read grant — see the ACP arm's wrapAgentCliSpawn call.
2147
2291
  ...opts?.additionalReadPaths?.length ? { extraReadPaths: opts.additionalReadPaths } : {},
2148
2292
  ...definition.routeSelection === "derived-from-model" && printModel ? { expectedModel: String(printModel) } : {}
@@ -2150,6 +2294,7 @@ function createAgentCliRuntime(definition) {
2150
2294
  }
2151
2295
  let child;
2152
2296
  const stderrBuf = [];
2297
+ const stderrLineListeners = /* @__PURE__ */ new Set();
2153
2298
  if (definition.protocol !== "proprietary") {
2154
2299
  const [execBin, execArgs] = await wrapAgentCliSpawn(
2155
2300
  finalBin,
@@ -2158,7 +2303,7 @@ function createAgentCliRuntime(definition) {
2158
2303
  mode: opts?.commandSandbox,
2159
2304
  cwd,
2160
2305
  ...opts?.fsZones ? { zones: opts.fsZones } : {},
2161
- ...claudeConfigDir ? { extraWritePaths: [claudeConfigDir] } : {},
2306
+ ...extraWritePaths.length ? { extraWritePaths } : {},
2162
2307
  // Exact-file read grant (see additionalReadPaths above):
2163
2308
  // READ-only exceptions to the confinement boundary, never writes.
2164
2309
  ...opts?.additionalReadPaths?.length ? { extraReadPaths: opts.additionalReadPaths } : {},
@@ -2178,11 +2323,11 @@ function createAgentCliRuntime(definition) {
2178
2323
  });
2179
2324
  child = spawned;
2180
2325
  let spawnFailure;
2181
- await new Promise((resolve2) => {
2182
- spawned.once("spawn", () => resolve2());
2326
+ await new Promise((resolve3) => {
2327
+ spawned.once("spawn", () => resolve3());
2183
2328
  spawned.once("error", (err) => {
2184
2329
  spawnFailure = err;
2185
- resolve2();
2330
+ resolve3();
2186
2331
  });
2187
2332
  });
2188
2333
  if (spawnFailure) {
@@ -2205,6 +2350,7 @@ cwd '${cwd}' does not exist \u2014 Node reports a missing working directory with
2205
2350
  if (!line) continue;
2206
2351
  stderrBuf.push(line);
2207
2352
  if (stderrBuf.length > STDERR_KEEP_LINES) stderrBuf.shift();
2353
+ for (const listener of stderrLineListeners) listener(line);
2208
2354
  }
2209
2355
  });
2210
2356
  }
@@ -2216,6 +2362,10 @@ cwd '${cwd}' does not exist \u2014 Node reports a missing working directory with
2216
2362
  opts?.permissionHold ?? false
2217
2363
  );
2218
2364
  arm._stderrTail = () => stderrBuf.join("\n");
2365
+ arm._onStderrLine = (listener) => {
2366
+ stderrLineListeners.add(listener);
2367
+ return () => stderrLineListeners.delete(listener);
2368
+ };
2219
2369
  const abortController = new AbortController();
2220
2370
  if (opts?.signal) {
2221
2371
  opts.signal.addEventListener("abort", () => abortController.abort(), {
@@ -2282,7 +2432,8 @@ cwd '${cwd}' does not exist \u2014 Node reports a missing working directory with
2282
2432
  send(message) {
2283
2433
  const turnId = randomUUID();
2284
2434
  currentTurnId = turnId;
2285
- return promptTurn(arm, turnId, message);
2435
+ const stderrTurnError = definition.id === "opencode" ? parseStderrStreamError : void 0;
2436
+ return promptTurn(arm, turnId, message, stderrTurnError);
2286
2437
  },
2287
2438
  /**
2288
2439
  * Cancel the in-flight turn — the wire equivalent of Ctrl-C at the
@@ -2398,5 +2549,5 @@ function derivedModelRefusalError(definition, requested, reason) {
2398
2549
  }
2399
2550
 
2400
2551
  export { LEGACY_GATEWAY_MODE_IDS, LEGACY_POSTURE_MODE_IDS, RuntimeConfigError, agentCliFrontmatterSchema, agentCliSupportsHostContextIsolation, applyModelCommand, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createArmSessionControls, createJcodeMapperState, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, hostContextExcludes, inferLegacyModeKind, isLegacyExtractedModeId, isModelSwitchAcknowledgement, mapJcodeEvent, mapMastraEvent, parseModelSwitchCommand, planModePermissionHandler, promptTurn, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
2401
- //# sourceMappingURL=chunk-Y3FGLQ4R.mjs.map
2402
- //# sourceMappingURL=chunk-Y3FGLQ4R.mjs.map
2552
+ //# sourceMappingURL=chunk-AN5G6ENG.mjs.map
2553
+ //# sourceMappingURL=chunk-AN5G6ENG.mjs.map