@agentproto/driver-agent-cli 2.4.4 → 2.5.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.
@@ -1,13 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  import { Writable, Readable } from 'stream';
3
3
  import { createAcpClient } from '@agentproto/acp/client';
4
- import { spawn } from 'child_process';
4
+ import { spawn, execFile } from 'child_process';
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync, rmdirSync, mkdtempSync } from 'fs';
6
6
  import { createInterface } from 'readline';
7
- import { join, dirname, delimiter } from 'path';
7
+ import { join, delimiter, dirname, basename } from 'path';
8
8
  import { tmpdir, homedir } from 'os';
9
9
  import { loadAdapterSpawnSandboxConfig, resolveCommandSandbox } from '@agentproto/command-sandbox';
10
- import { randomUUID } from 'crypto';
10
+ import { randomUUID, createHash } from 'crypto';
11
11
  import { createDoctype } from '@agentproto/define-doctype';
12
12
 
13
13
  /**
@@ -584,6 +584,18 @@ function createAcpProtocolArm(options) {
584
584
  if (!session) return { applied: false, reason: "not-connected" };
585
585
  return session.setSessionMode(modeId);
586
586
  },
587
+ onOutOfTurnEvent(listener) {
588
+ if (!session) return () => {
589
+ };
590
+ return session.onOutOfTurnEvent(listener);
591
+ },
592
+ get steeringSupported() {
593
+ return session?.steeringSupported ?? false;
594
+ },
595
+ async steer(content) {
596
+ if (!session) return "promptRequired";
597
+ return session.steer(content);
598
+ },
587
599
  respondPermission(requestId, resolution) {
588
600
  return client?.respondPermission(requestId, resolution) ?? false;
589
601
  },
@@ -599,10 +611,108 @@ function toFileBasedMcpServers(servers) {
599
611
  if (!servers || servers.length === 0) return void 0;
600
612
  const out = {};
601
613
  for (const s of servers) {
602
- out[s.name] = s.transport === "stdio" ? { command: s.ref ?? "" } : { url: s.ref ?? "", ...s.headers !== void 0 ? { headers: s.headers } : {} };
614
+ out[s.name] = s.transport === "stdio" ? {
615
+ command: s.ref ?? "",
616
+ ...s.args !== void 0 ? { args: s.args } : {},
617
+ ...s.env !== void 0 ? { env: s.env } : {}
618
+ } : { url: s.ref ?? "", ...s.headers !== void 0 ? { headers: s.headers } : {} };
603
619
  }
604
620
  return out;
605
621
  }
622
+ var DEFAULT_GRACE_MS = 3e3;
623
+ var DEFAULT_POLL_MS = 100;
624
+ function descendantsOf(rootPid, rows) {
625
+ const byParent = /* @__PURE__ */ new Map();
626
+ for (const r of rows) {
627
+ const list = byParent.get(r.ppid);
628
+ if (list) list.push(r.pid);
629
+ else byParent.set(r.ppid, [r.pid]);
630
+ }
631
+ const out = [];
632
+ const seen = /* @__PURE__ */ new Set([rootPid]);
633
+ const queue = [rootPid];
634
+ while (queue.length > 0) {
635
+ const pid = queue.shift();
636
+ for (const kid of byParent.get(pid) ?? []) {
637
+ if (seen.has(kid)) continue;
638
+ seen.add(kid);
639
+ out.push(kid);
640
+ queue.push(kid);
641
+ }
642
+ }
643
+ return out;
644
+ }
645
+ async function terminateChildTree(child, opts = {}) {
646
+ const graceMs = opts.graceMs ?? DEFAULT_GRACE_MS;
647
+ const pollMs = opts.pollMs ?? DEFAULT_POLL_MS;
648
+ const list = opts.listProcesses ?? listProcesses;
649
+ const rootPid = child.pid;
650
+ const rootAlive = () => child.exitCode === null && child.signalCode === null;
651
+ if (typeof rootPid !== "number" || rootPid <= 0 || rootPid === process.pid || !rootAlive()) {
652
+ return { descendants: [], killed: [] };
653
+ }
654
+ const descendants = descendantsOf(rootPid, await list()).filter((p) => p !== process.pid);
655
+ const tracked = new Set(descendants);
656
+ try {
657
+ child.kill("SIGTERM");
658
+ } catch {
659
+ }
660
+ for (const pid of tracked) signal(pid, "SIGTERM");
661
+ const anyAlive = () => rootAlive() || [...tracked].some(isAlive);
662
+ const deadline = Date.now() + graceMs;
663
+ while (anyAlive() && Date.now() < deadline) {
664
+ await new Promise((r) => setTimeout(r, pollMs));
665
+ }
666
+ if (!anyAlive()) return { descendants, killed: [] };
667
+ const rows = await list();
668
+ const survivors = [...tracked].filter(isAlive);
669
+ for (const from of rootAlive() ? [rootPid, ...survivors] : survivors) {
670
+ for (const pid of descendantsOf(from, rows)) {
671
+ if (pid !== process.pid) tracked.add(pid);
672
+ }
673
+ }
674
+ const killed = [];
675
+ if (rootAlive()) {
676
+ try {
677
+ child.kill("SIGKILL");
678
+ killed.push(rootPid);
679
+ } catch {
680
+ }
681
+ }
682
+ for (const pid of tracked) {
683
+ if (isAlive(pid) && signal(pid, "SIGKILL")) killed.push(pid);
684
+ }
685
+ return { descendants, killed };
686
+ }
687
+ function signal(pid, sig) {
688
+ try {
689
+ process.kill(pid, sig);
690
+ return true;
691
+ } catch {
692
+ return false;
693
+ }
694
+ }
695
+ function isAlive(pid) {
696
+ try {
697
+ process.kill(pid, 0);
698
+ return true;
699
+ } catch (err) {
700
+ return err.code === "EPERM";
701
+ }
702
+ }
703
+ function listProcesses() {
704
+ return new Promise((resolve) => {
705
+ execFile("ps", ["-axo", "pid=,ppid="], { maxBuffer: 32 * 1024 * 1024 }, (err, stdout) => {
706
+ if (err) return resolve([]);
707
+ const out = [];
708
+ for (const line of stdout.split("\n")) {
709
+ const m = line.match(/^\s*(\d+)\s+(\d+)\s*$/);
710
+ if (m) out.push({ pid: Number(m[1]), ppid: Number(m[2]) });
711
+ }
712
+ resolve(out);
713
+ });
714
+ });
715
+ }
606
716
  function defaultToolchainWritePaths() {
607
717
  const home = homedir();
608
718
  return [
@@ -667,12 +777,12 @@ async function wrapAgentCliSpawn(bin, args, opts) {
667
777
 
668
778
  // src/protocol/print-arm.ts
669
779
  function printModelMismatch(requested, reported) {
670
- const basename = (id) => {
780
+ const basename2 = (id) => {
671
781
  const noSuffix = id.split("@")[0] ?? id;
672
782
  const segs = noSuffix.split("/");
673
783
  return (segs[segs.length - 1] ?? noSuffix).trim().toLowerCase();
674
784
  };
675
- return basename(requested) !== basename(reported);
785
+ return basename2(requested) !== basename2(reported);
676
786
  }
677
787
  var DEFAULT_OUTPUT = ["--output-format", "stream-json"];
678
788
  var DEFAULT_PRE_PROMPT = ["--no-interactive"];
@@ -839,8 +949,9 @@ function createPrintSession(opts) {
839
949
  return { applied: false, reason: "not-supported" };
840
950
  },
841
951
  async close() {
842
- activeChild?.kill("SIGTERM");
952
+ const closing = activeChild ? terminateChildTree(activeChild) : void 0;
843
953
  mcpRestore?.();
954
+ await closing;
844
955
  }
845
956
  };
846
957
  }
@@ -1724,6 +1835,48 @@ async function* promptTurn(arm, turnId, message) {
1724
1835
  yield evt;
1725
1836
  }
1726
1837
  }
1838
+ function npxCacheKey(packages) {
1839
+ return createHash("sha512").update([...packages].sort((a, b) => a.localeCompare(b, "en")).join("\n")).digest("hex").slice(0, 16);
1840
+ }
1841
+ function parseExactSpec(spec) {
1842
+ const m = /^((?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*)@(\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?)$/i.exec(spec);
1843
+ return m ? { name: m[1], version: m[2] } : void 0;
1844
+ }
1845
+ function binNameFromManifest(name, bin) {
1846
+ const unscoped = name.replace(/^@[^/]+\//, "");
1847
+ if (typeof bin === "string") return unscoped;
1848
+ if (!bin || typeof bin !== "object") return void 0;
1849
+ const names = Object.keys(bin);
1850
+ if (names.length === 1) return names[0];
1851
+ return names.includes(unscoped) ? unscoped : void 0;
1852
+ }
1853
+ function resolveNpxFastPath(bin, args, env, deps) {
1854
+ if ((process.platform) === "win32") return void 0;
1855
+ if (basename(bin) !== "npx") return void 0;
1856
+ const [yes, spec, ...rest] = args;
1857
+ if (yes !== "-y" && yes !== "--yes") return void 0;
1858
+ if (spec === void 0) return void 0;
1859
+ const parsed = parseExactSpec(spec);
1860
+ if (!parsed) return void 0;
1861
+ const exists = existsSync;
1862
+ const readFile = ((p) => readFileSync(p, "utf8"));
1863
+ const cacheRoot = env["npm_config_cache"] ?? env["NPM_CONFIG_CACHE"] ?? join(homedir(), ".npm");
1864
+ const installDir = join(cacheRoot, "_npx", npxCacheKey([spec]));
1865
+ try {
1866
+ const pkg = JSON.parse(readFile(join(installDir, "node_modules", parsed.name, "package.json")));
1867
+ if (pkg.version !== parsed.version) return void 0;
1868
+ const binName = binNameFromManifest(parsed.name, pkg.bin);
1869
+ if (!binName) return void 0;
1870
+ const binDir = join(installDir, "node_modules", ".bin");
1871
+ const binPath = join(binDir, binName);
1872
+ if (!exists(binPath)) return void 0;
1873
+ return { bin: binPath, args: rest, binDir };
1874
+ } catch {
1875
+ return void 0;
1876
+ }
1877
+ }
1878
+
1879
+ // src/define-agent-cli.ts
1727
1880
  var defineAgentCli = createDoctype(
1728
1881
  {
1729
1882
  aip: 45,
@@ -1827,6 +1980,12 @@ function createAgentCliRuntime(definition) {
1827
1980
  }
1828
1981
  Object.assign(env, opts?.env ?? {});
1829
1982
  ensureExecDirOnPath(env);
1983
+ const npxFast = resolveNpxFastPath(resolvedBin, composed.binArgs, env);
1984
+ if (npxFast) {
1985
+ env.PATH = [npxFast.binDir, ...(env.PATH ?? "").split(delimiter).filter(Boolean)].join(delimiter);
1986
+ }
1987
+ const spawnBin = npxFast?.bin ?? resolvedBin;
1988
+ const spawnArgs = npxFast?.args ?? composed.binArgs;
1830
1989
  if (opts?.additionalReadPaths?.length) {
1831
1990
  env.AGENTPROTO_ADDITIONAL_READ_PATHS = JSON.stringify(opts.additionalReadPaths);
1832
1991
  }
@@ -1874,8 +2033,8 @@ function createAgentCliRuntime(definition) {
1874
2033
  if (definition.protocol === "print") {
1875
2034
  const printModel = config?.options?.model;
1876
2035
  return createPrintSession({
1877
- bin: resolvedBin,
1878
- baseArgs: composed.binArgs,
2036
+ bin: spawnBin,
2037
+ baseArgs: spawnArgs,
1879
2038
  cwd,
1880
2039
  env,
1881
2040
  ...opts?.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {},
@@ -1892,8 +2051,8 @@ function createAgentCliRuntime(definition) {
1892
2051
  const stderrBuf = [];
1893
2052
  if (definition.protocol !== "proprietary") {
1894
2053
  const [execBin, execArgs] = await wrapAgentCliSpawn(
1895
- resolvedBin,
1896
- composed.binArgs,
2054
+ spawnBin,
2055
+ spawnArgs,
1897
2056
  {
1898
2057
  mode: opts?.commandSandbox,
1899
2058
  cwd,
@@ -2045,9 +2204,22 @@ cwd '${cwd}' does not exist \u2014 Node reports a missing working directory with
2045
2204
  return arm.respondPermission(requestId, resolution);
2046
2205
  }
2047
2206
  } : {},
2207
+ ...arm.onOutOfTurnEvent ? {
2208
+ onOutOfTurnEvent(listener) {
2209
+ return arm.onOutOfTurnEvent(listener);
2210
+ }
2211
+ } : {},
2212
+ ...arm.steer ? {
2213
+ // Snapshotted after `arm.connect()` resolved, like the
2214
+ // config/mode read surfaces above.
2215
+ steeringSupported: arm.steeringSupported === true,
2216
+ steer(content) {
2217
+ return arm.steer(content);
2218
+ }
2219
+ } : {},
2048
2220
  async close() {
2049
2221
  await arm.close();
2050
- if (child && !child.killed) child.kill("SIGTERM");
2222
+ if (child) await terminateChildTree(child);
2051
2223
  }
2052
2224
  };
2053
2225
  }
@@ -2113,5 +2285,5 @@ function derivedModelRefusalError(definition, requested, reason) {
2113
2285
  }
2114
2286
 
2115
2287
  export { LEGACY_GATEWAY_MODE_IDS, LEGACY_POSTURE_MODE_IDS, RuntimeConfigError, agentCliFrontmatterSchema, applyModelCommand, autoAllowPermissionHandler, composeSpawn, createAcpProtocolArm, createAgentCliRuntime, createArmSessionControls, createJcodeMapperState, createMastraMapperState, createPrintSession, createProprietaryProtocolArm, defineAgentCli, inferLegacyModeKind, isLegacyExtractedModeId, isModelSwitchAcknowledgement, mapJcodeEvent, mapMastraEvent, parseModelSwitchCommand, planModePermissionHandler, promptTurn, resolveContinuationStrategy, runtimeConfigSchema, toFileBasedMcpServers };
2116
- //# sourceMappingURL=chunk-UBRGTH3A.mjs.map
2117
- //# sourceMappingURL=chunk-UBRGTH3A.mjs.map
2288
+ //# sourceMappingURL=chunk-JPL6CMLQ.mjs.map
2289
+ //# sourceMappingURL=chunk-JPL6CMLQ.mjs.map