@agentproto/driver-agent-cli 2.4.4 → 2.5.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.
@@ -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
  }
@@ -922,14 +1033,26 @@ function setupMcpConfigFile(opts, eventSchema) {
922
1033
  }
923
1034
  function extractPromptText(message) {
924
1035
  if (typeof message === "string") return message;
925
- if (message !== null && typeof message === "object") {
926
- const m = message;
927
- if (typeof m.text === "string") return m.text;
928
- if (Array.isArray(message)) {
929
- return message.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
1036
+ const blocks = Array.isArray(message) ? message : [message];
1037
+ const texts = [];
1038
+ for (const block of blocks) {
1039
+ if (block !== null && typeof block === "object") {
1040
+ const b = block;
1041
+ if (b.type === "text" && typeof b.text === "string") {
1042
+ texts.push(b.text);
1043
+ continue;
1044
+ }
1045
+ if (b.type === void 0 && typeof b.text === "string") {
1046
+ texts.push(b.text);
1047
+ continue;
1048
+ }
930
1049
  }
1050
+ const kind = block !== null && typeof block === "object" && typeof block.type === "string" ? block.type : typeof block;
1051
+ throw new Error(
1052
+ `this adapter has no multimodal input (capabilities.multimodal: false) \u2014 got a "${kind}" content block it can't send. Upload the file via POST /files/upload and reference its path in the prompt text instead.`
1053
+ );
931
1054
  }
932
- return JSON.stringify(message);
1055
+ return texts.join("\n");
933
1056
  }
934
1057
  function captureSessionId(evt, schema) {
935
1058
  switch (schema) {
@@ -1724,6 +1847,48 @@ async function* promptTurn(arm, turnId, message) {
1724
1847
  yield evt;
1725
1848
  }
1726
1849
  }
1850
+ function npxCacheKey(packages) {
1851
+ return createHash("sha512").update([...packages].sort((a, b) => a.localeCompare(b, "en")).join("\n")).digest("hex").slice(0, 16);
1852
+ }
1853
+ function parseExactSpec(spec) {
1854
+ const m = /^((?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*)@(\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?)$/i.exec(spec);
1855
+ return m ? { name: m[1], version: m[2] } : void 0;
1856
+ }
1857
+ function binNameFromManifest(name, bin) {
1858
+ const unscoped = name.replace(/^@[^/]+\//, "");
1859
+ if (typeof bin === "string") return unscoped;
1860
+ if (!bin || typeof bin !== "object") return void 0;
1861
+ const names = Object.keys(bin);
1862
+ if (names.length === 1) return names[0];
1863
+ return names.includes(unscoped) ? unscoped : void 0;
1864
+ }
1865
+ function resolveNpxFastPath(bin, args, env, deps) {
1866
+ if ((process.platform) === "win32") return void 0;
1867
+ if (basename(bin) !== "npx") return void 0;
1868
+ const [yes, spec, ...rest] = args;
1869
+ if (yes !== "-y" && yes !== "--yes") return void 0;
1870
+ if (spec === void 0) return void 0;
1871
+ const parsed = parseExactSpec(spec);
1872
+ if (!parsed) return void 0;
1873
+ const exists = existsSync;
1874
+ const readFile = ((p) => readFileSync(p, "utf8"));
1875
+ const cacheRoot = env["npm_config_cache"] ?? env["NPM_CONFIG_CACHE"] ?? join(homedir(), ".npm");
1876
+ const installDir = join(cacheRoot, "_npx", npxCacheKey([spec]));
1877
+ try {
1878
+ const pkg = JSON.parse(readFile(join(installDir, "node_modules", parsed.name, "package.json")));
1879
+ if (pkg.version !== parsed.version) return void 0;
1880
+ const binName = binNameFromManifest(parsed.name, pkg.bin);
1881
+ if (!binName) return void 0;
1882
+ const binDir = join(installDir, "node_modules", ".bin");
1883
+ const binPath = join(binDir, binName);
1884
+ if (!exists(binPath)) return void 0;
1885
+ return { bin: binPath, args: rest, binDir };
1886
+ } catch {
1887
+ return void 0;
1888
+ }
1889
+ }
1890
+
1891
+ // src/define-agent-cli.ts
1727
1892
  var defineAgentCli = createDoctype(
1728
1893
  {
1729
1894
  aip: 45,
@@ -1827,6 +1992,12 @@ function createAgentCliRuntime(definition) {
1827
1992
  }
1828
1993
  Object.assign(env, opts?.env ?? {});
1829
1994
  ensureExecDirOnPath(env);
1995
+ const npxFast = resolveNpxFastPath(resolvedBin, composed.binArgs, env);
1996
+ if (npxFast) {
1997
+ env.PATH = [npxFast.binDir, ...(env.PATH ?? "").split(delimiter).filter(Boolean)].join(delimiter);
1998
+ }
1999
+ const spawnBin = npxFast?.bin ?? resolvedBin;
2000
+ const spawnArgs = npxFast?.args ?? composed.binArgs;
1830
2001
  if (opts?.additionalReadPaths?.length) {
1831
2002
  env.AGENTPROTO_ADDITIONAL_READ_PATHS = JSON.stringify(opts.additionalReadPaths);
1832
2003
  }
@@ -1874,8 +2045,8 @@ function createAgentCliRuntime(definition) {
1874
2045
  if (definition.protocol === "print") {
1875
2046
  const printModel = config?.options?.model;
1876
2047
  return createPrintSession({
1877
- bin: resolvedBin,
1878
- baseArgs: composed.binArgs,
2048
+ bin: spawnBin,
2049
+ baseArgs: spawnArgs,
1879
2050
  cwd,
1880
2051
  env,
1881
2052
  ...opts?.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {},
@@ -1892,8 +2063,8 @@ function createAgentCliRuntime(definition) {
1892
2063
  const stderrBuf = [];
1893
2064
  if (definition.protocol !== "proprietary") {
1894
2065
  const [execBin, execArgs] = await wrapAgentCliSpawn(
1895
- resolvedBin,
1896
- composed.binArgs,
2066
+ spawnBin,
2067
+ spawnArgs,
1897
2068
  {
1898
2069
  mode: opts?.commandSandbox,
1899
2070
  cwd,
@@ -2045,9 +2216,22 @@ cwd '${cwd}' does not exist \u2014 Node reports a missing working directory with
2045
2216
  return arm.respondPermission(requestId, resolution);
2046
2217
  }
2047
2218
  } : {},
2219
+ ...arm.onOutOfTurnEvent ? {
2220
+ onOutOfTurnEvent(listener) {
2221
+ return arm.onOutOfTurnEvent(listener);
2222
+ }
2223
+ } : {},
2224
+ ...arm.steer ? {
2225
+ // Snapshotted after `arm.connect()` resolved, like the
2226
+ // config/mode read surfaces above.
2227
+ steeringSupported: arm.steeringSupported === true,
2228
+ steer(content) {
2229
+ return arm.steer(content);
2230
+ }
2231
+ } : {},
2048
2232
  async close() {
2049
2233
  await arm.close();
2050
- if (child && !child.killed) child.kill("SIGTERM");
2234
+ if (child) await terminateChildTree(child);
2051
2235
  }
2052
2236
  };
2053
2237
  }
@@ -2113,5 +2297,5 @@ function derivedModelRefusalError(definition, requested, reason) {
2113
2297
  }
2114
2298
 
2115
2299
  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
2300
+ //# sourceMappingURL=chunk-ES4VLTGZ.mjs.map
2301
+ //# sourceMappingURL=chunk-ES4VLTGZ.mjs.map