@moxt-ai/mobius 0.0.9 → 0.0.10

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.
Files changed (38) hide show
  1. package/dist/acp/session-manager.d.ts.map +1 -1
  2. package/dist/acp/session-manager.js +11 -2
  3. package/dist/acp/session-manager.js.map +1 -1
  4. package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -1
  5. package/dist/agent-catalog/agent-diagnostics.js +6 -2
  6. package/dist/agent-catalog/agent-diagnostics.js.map +1 -1
  7. package/dist/agent-catalog/discover-local-agents.d.ts +35 -7
  8. package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
  9. package/dist/agent-catalog/discover-local-agents.js +137 -26
  10. package/dist/agent-catalog/discover-local-agents.js.map +1 -1
  11. package/dist/agent-catalog/local-agent.d.ts +2 -1
  12. package/dist/agent-catalog/local-agent.d.ts.map +1 -1
  13. package/dist/agent-catalog/local-agent.js +7 -1
  14. package/dist/agent-catalog/local-agent.js.map +1 -1
  15. package/dist/agent-installation/fixed-agent-install-recipes.d.ts +1 -0
  16. package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +1 -1
  17. package/dist/agent-installation/fixed-agent-install-recipes.js +48 -0
  18. package/dist/agent-installation/fixed-agent-install-recipes.js.map +1 -1
  19. package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -1
  20. package/dist/agent-installation/fixed-installation-executor.js +2 -0
  21. package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
  22. package/dist/agent-installation/managed-adapter-installer.d.ts +3 -2
  23. package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
  24. package/dist/agent-installation/managed-adapter-installer.js +25 -126
  25. package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
  26. package/dist/agent-installation/managed-npm-package.d.ts +24 -0
  27. package/dist/agent-installation/managed-npm-package.d.ts.map +1 -0
  28. package/dist/agent-installation/managed-npm-package.js +227 -0
  29. package/dist/agent-installation/managed-npm-package.js.map +1 -0
  30. package/dist/agent-installation/pi-managed-packages.d.ts +4 -0
  31. package/dist/agent-installation/pi-managed-packages.d.ts.map +1 -0
  32. package/dist/agent-installation/pi-managed-packages.js +4 -0
  33. package/dist/agent-installation/pi-managed-packages.js.map +1 -0
  34. package/dist/cli.js +610 -159
  35. package/dist/cli.js.map +4 -4
  36. package/dist/index.js +601 -152
  37. package/dist/index.js.map +4 -4
  38. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -10,11 +10,12 @@ import { mkdir as mkdir4 } from "node:fs/promises";
10
10
 
11
11
  // src/agent-catalog/discover-local-agents.ts
12
12
  import { spawn as spawn2 } from "node:child_process";
13
- import { isAbsolute, join as join5 } from "node:path";
13
+ import { isAbsolute as isAbsolute2, join as join6 } from "node:path";
14
14
  import {
15
15
  methods as methods2,
16
16
  ndJsonStream,
17
- PROTOCOL_VERSION
17
+ PROTOCOL_VERSION,
18
+ RequestError
18
19
  } from "@agentclientprotocol/sdk";
19
20
 
20
21
  // src/acp/create-client.ts
@@ -77,6 +78,9 @@ async function readGrantedAgents(stateDirectory) {
77
78
  }
78
79
 
79
80
  // src/agent-installation/managed-adapter-installer.ts
81
+ import { join as join4 } from "node:path";
82
+
83
+ // src/agent-installation/managed-npm-package.ts
80
84
  import {
81
85
  mkdir,
82
86
  mkdtemp,
@@ -86,7 +90,7 @@ import {
86
90
  rm,
87
91
  stat as stat2
88
92
  } from "node:fs/promises";
89
- import { delimiter, dirname, join as join3, relative } from "node:path";
93
+ import { delimiter, dirname, isAbsolute, join as join3, relative, sep } from "node:path";
90
94
 
91
95
  // src/process/executable.ts
92
96
  import { spawn } from "node:child_process";
@@ -424,15 +428,35 @@ async function runBoundedProcess(options) {
424
428
  }
425
429
  }
426
430
 
427
- // src/agent-installation/managed-adapter-installer.ts
428
- var ADAPTER_INSTALL_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3;
431
+ // src/agent-installation/managed-npm-package.ts
432
+ var INSTALL_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
429
433
  var PACKAGE_METADATA_LIMIT_BYTES = 64 * 1024;
430
- var ManagedAdapterInstallError = class extends Error {
434
+ var PACKAGE_LOCK_LIMIT_BYTES = 8 * 1024 * 1024;
435
+ var ManagedNpmPackageDefinition = class {
436
+ executableName;
437
+ packageName;
438
+ version;
439
+ constructor(packageName, version2, executableName) {
440
+ this.executableName = executableName;
441
+ this.packageName = packageName;
442
+ this.version = version2;
443
+ }
444
+ };
445
+ var SriManagedNpmPackageDefinition = class extends ManagedNpmPackageDefinition {
446
+ integrity;
447
+ tarballUrl;
448
+ constructor(packageName, version2, executableName, integrity, tarballUrl) {
449
+ super(packageName, version2, executableName);
450
+ this.integrity = integrity;
451
+ this.tarballUrl = tarballUrl;
452
+ }
453
+ };
454
+ var ManagedNpmPackageError = class extends Error {
431
455
  code;
432
456
  constructor(code, message, options = {}) {
433
457
  super(message, options);
434
458
  this.code = code;
435
- this.name = "ManagedAdapterInstallError";
459
+ this.name = "ManagedNpmPackageError";
436
460
  }
437
461
  };
438
462
  function isRecord2(value) {
@@ -441,28 +465,43 @@ function isRecord2(value) {
441
465
  function packagePath(root, packageName) {
442
466
  return join3(root, "node_modules", ...packageName.split("/"));
443
467
  }
444
- async function readPackageJson(path2) {
445
- const handle = await open2(path2, "r");
468
+ function isContainedPath(root, candidate) {
469
+ const difference = relative(root, candidate);
470
+ return difference.length > 0 && difference !== ".." && !difference.startsWith(`..${sep}`) && !isAbsolute(difference);
471
+ }
472
+ async function readJsonRecord(path2, maximumBytes) {
446
473
  try {
447
- const metadata = await handle.stat();
448
- if (!metadata.isFile() || metadata.size > PACKAGE_METADATA_LIMIT_BYTES) {
449
- throw new ManagedAdapterInstallError(
450
- "adapter_verification_failed",
451
- "The managed adapter package metadata is invalid"
474
+ const handle = await open2(path2, "r");
475
+ try {
476
+ const metadata = await handle.stat();
477
+ if (!metadata.isFile() || metadata.size > maximumBytes) {
478
+ throw new ManagedNpmPackageError(
479
+ "managed_package_verification_failed",
480
+ "The managed npm package metadata is invalid"
481
+ );
482
+ }
483
+ const value = JSON.parse(
484
+ await handle.readFile({ encoding: "utf8" })
452
485
  );
486
+ if (!isRecord2(value)) {
487
+ throw new ManagedNpmPackageError(
488
+ "managed_package_verification_failed",
489
+ "The managed npm package metadata is invalid"
490
+ );
491
+ }
492
+ return value;
493
+ } finally {
494
+ await handle.close();
453
495
  }
454
- const value = JSON.parse(
455
- await handle.readFile({ encoding: "utf8" })
456
- );
457
- if (!isRecord2(value)) {
458
- throw new ManagedAdapterInstallError(
459
- "adapter_verification_failed",
460
- "The managed adapter package metadata is invalid"
461
- );
496
+ } catch (error61) {
497
+ if (error61 instanceof ManagedNpmPackageError) {
498
+ throw error61;
462
499
  }
463
- return value;
464
- } finally {
465
- await handle.close();
500
+ throw new ManagedNpmPackageError(
501
+ "managed_package_verification_failed",
502
+ "The managed npm package metadata is invalid",
503
+ { cause: error61 }
504
+ );
466
505
  }
467
506
  }
468
507
  function readBinPath(packageMetadata, executableName) {
@@ -473,83 +512,125 @@ function readBinPath(packageMetadata, executableName) {
473
512
  if (isRecord2(bin) && typeof bin[executableName] === "string") {
474
513
  return bin[executableName];
475
514
  }
476
- throw new ManagedAdapterInstallError(
477
- "adapter_verification_failed",
478
- "The managed adapter executable declaration is invalid"
515
+ throw new ManagedNpmPackageError(
516
+ "managed_package_verification_failed",
517
+ "The managed npm package executable declaration is invalid"
479
518
  );
480
519
  }
481
- async function validateManagedAdapter(root, specification) {
520
+ function verifyPackageLock(lock, definition) {
521
+ if (lock["lockfileVersion"] !== 3) {
522
+ throw new ManagedNpmPackageError(
523
+ "managed_package_verification_failed",
524
+ "The managed npm package lockfile is invalid"
525
+ );
526
+ }
527
+ const packages = lock["packages"];
528
+ if (!isRecord2(packages)) {
529
+ throw new ManagedNpmPackageError(
530
+ "managed_package_verification_failed",
531
+ "The managed npm package lockfile is invalid"
532
+ );
533
+ }
534
+ const suffix = `node_modules/${definition.packageName}`;
535
+ let entry;
536
+ let matches = 0;
537
+ for (const [key, candidate] of Object.entries(packages)) {
538
+ const normalizedKey = key.replaceAll("\\", "/");
539
+ if (normalizedKey === suffix || normalizedKey.endsWith(`/${suffix}`)) {
540
+ entry = candidate;
541
+ matches += 1;
542
+ }
543
+ }
544
+ if (matches !== 1 || !isRecord2(entry) || entry["integrity"] !== definition.integrity || entry["resolved"] !== definition.tarballUrl || entry["version"] !== definition.version) {
545
+ throw new ManagedNpmPackageError(
546
+ "managed_package_verification_failed",
547
+ "The managed npm package lockfile does not match the pinned artifact"
548
+ );
549
+ }
550
+ }
551
+ async function validateManagedNpmPackage(root, definition) {
552
+ try {
553
+ return await validateManagedNpmPackageContents(root, definition);
554
+ } catch (error61) {
555
+ if (error61 instanceof ManagedNpmPackageError) {
556
+ throw error61;
557
+ }
558
+ throw new ManagedNpmPackageError(
559
+ "managed_package_verification_failed",
560
+ "The managed npm package is invalid",
561
+ { cause: error61 }
562
+ );
563
+ }
564
+ }
565
+ async function validateManagedNpmPackageContents(root, definition) {
482
566
  const rootPath = await realpath2(root);
483
567
  const packageRoot = await realpath2(
484
- packagePath(rootPath, specification.adapterPackage)
568
+ packagePath(rootPath, definition.packageName)
569
+ );
570
+ if (!isContainedPath(rootPath, packageRoot)) {
571
+ throw new ManagedNpmPackageError(
572
+ "managed_package_verification_failed",
573
+ "The managed npm package escaped its private cache"
574
+ );
575
+ }
576
+ const metadata = await readJsonRecord(
577
+ join3(packageRoot, "package.json"),
578
+ PACKAGE_METADATA_LIMIT_BYTES
485
579
  );
486
- const packageDifference = relative(rootPath, packageRoot);
487
- if (packageDifference.startsWith("..") || packageDifference.length === 0) {
488
- throw new ManagedAdapterInstallError(
489
- "adapter_verification_failed",
490
- "The managed adapter package escaped its private cache"
580
+ if (metadata["name"] !== definition.packageName || metadata["version"] !== definition.version) {
581
+ throw new ManagedNpmPackageError(
582
+ "managed_package_verification_failed",
583
+ "The managed npm package identity is invalid"
491
584
  );
492
585
  }
493
- const metadata = await readPackageJson(join3(packageRoot, "package.json"));
494
- if (metadata["name"] !== specification.adapterPackage || metadata["version"] !== specification.adapterVersion) {
495
- throw new ManagedAdapterInstallError(
496
- "adapter_verification_failed",
497
- "The managed adapter package identity is invalid"
586
+ if (definition instanceof SriManagedNpmPackageDefinition) {
587
+ verifyPackageLock(
588
+ await readJsonRecord(
589
+ join3(rootPath, "package-lock.json"),
590
+ PACKAGE_LOCK_LIMIT_BYTES
591
+ ),
592
+ definition
498
593
  );
499
594
  }
500
- const declaredBin = readBinPath(metadata, specification.adapterExecutable);
595
+ const declaredBin = readBinPath(metadata, definition.executableName);
501
596
  const executable = await resolveExecutablePath(
502
597
  join3(packageRoot, declaredBin)
503
598
  );
504
- if (executable === null) {
505
- throw new ManagedAdapterInstallError(
506
- "adapter_verification_failed",
507
- "The managed adapter executable is invalid"
508
- );
509
- }
510
- const executableDifference = relative(rootPath, executable.path);
511
- if (executableDifference.startsWith("..") || executableDifference.length === 0) {
512
- throw new ManagedAdapterInstallError(
513
- "adapter_verification_failed",
514
- "The managed adapter executable escaped its private cache"
599
+ if (executable === null || !isContainedPath(rootPath, executable.path)) {
600
+ throw new ManagedNpmPackageError(
601
+ "managed_package_verification_failed",
602
+ "The managed npm package executable is invalid"
515
603
  );
516
604
  }
517
605
  return executable;
518
606
  }
519
- var ManagedAdapterInstaller = class {
520
- #environment;
521
- #stateDirectory;
522
- constructor(options) {
523
- this.#environment = options.environment;
524
- this.#stateDirectory = options.stateDirectory;
525
- }
526
- install = async (specification, signal) => {
607
+ var ManagedNpmPackageInstaller = class {
608
+ install = async (root, definition, environment, signal) => {
527
609
  signal.throwIfAborted();
528
- const agentCache = join3(this.#stateDirectory, "adapters", specification.id);
529
- const published = join3(agentCache, specification.adapterVersion);
530
610
  try {
531
- const metadata = await stat2(published);
611
+ const metadata = await stat2(root);
532
612
  if (metadata.isDirectory()) {
533
- return await validateManagedAdapter(published, specification);
613
+ return await validateManagedNpmPackage(root, definition);
534
614
  }
535
615
  } catch (error61) {
536
616
  if (!(error61 instanceof Error) || !("code" in error61) || error61.code !== "ENOENT") {
537
617
  throw error61;
538
618
  }
539
619
  }
540
- await mkdir(agentCache, { mode: 448, recursive: true });
620
+ const parent = dirname(root);
621
+ await mkdir(parent, { mode: 448, recursive: true });
541
622
  const staging = await mkdtemp(
542
- join3(agentCache, `.${specification.adapterVersion}.staging-`)
623
+ join3(parent, `.${definition.version}.staging-`)
543
624
  );
544
625
  try {
545
626
  const npm = await findExecutableOnPath(
546
627
  process.platform === "win32" ? "npm.cmd" : "npm",
547
- this.#environment
628
+ environment
548
629
  );
549
630
  if (npm === null) {
550
- throw new ManagedAdapterInstallError(
631
+ throw new ManagedNpmPackageError(
551
632
  "package_manager_not_found",
552
- "A supported npm executable is required to install the ACP adapter"
633
+ "A supported npm executable is required to install the managed package"
553
634
  );
554
635
  }
555
636
  await runBoundedProcess({
@@ -561,51 +642,57 @@ var ManagedAdapterInstaller = class {
561
642
  "--ignore-scripts",
562
643
  "--no-audit",
563
644
  "--no-fund",
564
- "--package-lock=false",
565
- "--save=false",
645
+ "--package-lock=true",
646
+ "--save-exact",
566
647
  "--registry=https://registry.npmjs.org/",
567
- `${specification.adapterPackage}@${specification.adapterVersion}`
648
+ `${definition.packageName}@${definition.version}`
568
649
  ],
569
650
  command: npm.path,
570
651
  cwd: staging,
571
652
  environment: {
572
653
  HOME: join3(staging, "home"),
573
654
  NPM_CONFIG_AUDIT: "false",
655
+ NPM_CONFIG_CACHE: join3(staging, ".npm-cache"),
574
656
  NPM_CONFIG_FUND: "false",
575
657
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
576
658
  NPM_CONFIG_OMIT: "optional",
659
+ NPM_CONFIG_PACKAGE_LOCK: "true",
577
660
  NPM_CONFIG_REGISTRY: "https://registry.npmjs.org/",
578
661
  PATH: `${dirname(process.execPath)}${delimiter}/usr/bin${delimiter}/bin`
579
662
  },
580
663
  outputLimitBytes: 64 * 1024,
581
664
  signal,
582
- timeoutMilliseconds: ADAPTER_INSTALL_TIMEOUT_MILLISECONDS
665
+ timeoutMilliseconds: INSTALL_TIMEOUT_MILLISECONDS
583
666
  });
584
- await validateManagedAdapter(staging, specification);
667
+ await Promise.all([
668
+ rm(join3(staging, ".npm-cache"), { force: true, recursive: true }),
669
+ rm(join3(staging, "home"), { force: true, recursive: true })
670
+ ]);
671
+ await validateManagedNpmPackage(staging, definition);
585
672
  try {
586
- await rename(staging, published);
673
+ await rename(staging, root);
587
674
  } catch (error61) {
588
675
  try {
589
- return await validateManagedAdapter(published, specification);
676
+ return await validateManagedNpmPackage(root, definition);
590
677
  } catch {
591
- throw new ManagedAdapterInstallError(
592
- "adapter_publish_failed",
593
- "The managed adapter could not be published",
678
+ throw new ManagedNpmPackageError(
679
+ "managed_package_publish_failed",
680
+ "The managed npm package could not be published",
594
681
  { cause: error61 }
595
682
  );
596
683
  }
597
684
  }
598
- return await validateManagedAdapter(published, specification);
685
+ return await validateManagedNpmPackage(root, definition);
599
686
  } catch (error61) {
600
687
  if (signal.aborted) {
601
688
  signal.throwIfAborted();
602
689
  }
603
- if (error61 instanceof ManagedAdapterInstallError) {
690
+ if (error61 instanceof ManagedNpmPackageError) {
604
691
  throw error61;
605
692
  }
606
- throw new ManagedAdapterInstallError(
607
- "adapter_install_failed",
608
- "The pinned ACP adapter could not be installed",
693
+ throw new ManagedNpmPackageError(
694
+ "managed_package_install_failed",
695
+ "The pinned npm package could not be installed",
609
696
  { cause: error61 }
610
697
  );
611
698
  } finally {
@@ -614,15 +701,115 @@ var ManagedAdapterInstaller = class {
614
701
  };
615
702
  };
616
703
 
704
+ // src/agent-installation/managed-adapter-installer.ts
705
+ var ManagedAdapterInstallError = class extends Error {
706
+ code;
707
+ constructor(code, message, options = {}) {
708
+ super(message, options);
709
+ this.code = code;
710
+ this.name = "ManagedAdapterInstallError";
711
+ }
712
+ };
713
+ function translateError(error61) {
714
+ if (error61 instanceof ManagedAdapterInstallError) {
715
+ return error61;
716
+ }
717
+ if (error61 instanceof ManagedNpmPackageError) {
718
+ if (error61.code === "package_manager_not_found") {
719
+ return new ManagedAdapterInstallError(
720
+ error61.code,
721
+ "A supported npm executable is required to install the ACP adapter",
722
+ { cause: error61 }
723
+ );
724
+ }
725
+ if (error61.code === "managed_package_verification_failed") {
726
+ return new ManagedAdapterInstallError(
727
+ "adapter_verification_failed",
728
+ "The managed adapter did not match its pinned artifact",
729
+ { cause: error61 }
730
+ );
731
+ }
732
+ if (error61.code === "managed_package_publish_failed") {
733
+ return new ManagedAdapterInstallError(
734
+ "adapter_publish_failed",
735
+ "The managed adapter could not be published",
736
+ { cause: error61 }
737
+ );
738
+ }
739
+ }
740
+ return new ManagedAdapterInstallError(
741
+ "adapter_install_failed",
742
+ "The pinned ACP adapter could not be installed",
743
+ { cause: error61 }
744
+ );
745
+ }
746
+ async function validateManagedAdapter(root, specification) {
747
+ try {
748
+ return await validateManagedNpmPackage(
749
+ root,
750
+ specification.adapterPackageDefinition
751
+ );
752
+ } catch (error61) {
753
+ throw translateError(error61);
754
+ }
755
+ }
756
+ var ManagedAdapterInstaller = class {
757
+ #environment;
758
+ #npmInstaller;
759
+ #stateDirectory;
760
+ constructor(options, npmInstaller = new ManagedNpmPackageInstaller()) {
761
+ this.#environment = options.environment;
762
+ this.#npmInstaller = npmInstaller;
763
+ this.#stateDirectory = options.stateDirectory;
764
+ }
765
+ install = async (specification, signal) => {
766
+ const root = join4(
767
+ this.#stateDirectory,
768
+ "adapters",
769
+ specification.id,
770
+ specification.adapterVersion
771
+ );
772
+ try {
773
+ return await this.#npmInstaller.install(
774
+ root,
775
+ specification.adapterPackageDefinition,
776
+ this.#environment,
777
+ signal
778
+ );
779
+ } catch (error61) {
780
+ if (signal.aborted) {
781
+ signal.throwIfAborted();
782
+ }
783
+ throw translateError(error61);
784
+ }
785
+ };
786
+ };
787
+
788
+ // src/agent-installation/pi-managed-packages.ts
789
+ var PI_ACP_PACKAGE = new SriManagedNpmPackageDefinition(
790
+ "pi-acp",
791
+ "0.0.33",
792
+ "pi-acp",
793
+ "sha512-vX9kY1tK14E72G4dBAx+RGCk/k7XPjTHls6dLUxA8WSkBav6B6JHuSBv3eusp50LCR/GTRsR2kIKsG0Z5jANzw==",
794
+ "https://registry.npmjs.org/pi-acp/-/pi-acp-0.0.33.tgz"
795
+ );
796
+ var PI_AGENT_PACKAGE = new SriManagedNpmPackageDefinition(
797
+ "@earendil-works/pi-coding-agent",
798
+ "0.85.0",
799
+ "pi",
800
+ "sha512-INxVkLAVfAMju5MojJpmyu/0bMP+r+ffZuS7UqVv32E2JwHBRbcHfELDfmFNvapEbgYfKN2r9OYO1p3TqDBR+g==",
801
+ "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.85.0.tgz"
802
+ );
803
+
617
804
  // src/storage/state-directory.ts
618
805
  import { homedir } from "node:os";
619
- import { join as join4, resolve as resolve2 } from "node:path";
806
+ import { join as join5, resolve as resolve2 } from "node:path";
620
807
  function readStateDirectory(environment = process.env) {
621
808
  const configured = environment["MOBIUS_STATE_DIRECTORY"]?.trim();
622
809
  if (configured !== void 0 && configured.length > 0) {
623
810
  return resolve2(configured);
624
811
  }
625
- return join4(homedir(), ".mobius");
812
+ return join5(homedir(), ".mobius");
626
813
  }
627
814
 
628
815
  // src/agent-catalog/local-agent.ts
@@ -650,7 +837,11 @@ var LocalAgentDefinition = class {
650
837
  environment;
651
838
  id;
652
839
  label;
653
- constructor(id, label, command, arguments_, environment = {}, adapterVersion = "", agentVersion = "", adapterIdentity = readTrustedIdentity(command), agentIdentity = adapterIdentity) {
840
+ promptTimeoutMilliseconds;
841
+ constructor(id, label, command, arguments_, environment = {}, adapterVersion = "", agentVersion = "", adapterIdentity = readTrustedIdentity(command), agentIdentity = adapterIdentity, promptTimeoutMilliseconds = 30 * 60 * 1e3) {
842
+ if (!Number.isSafeInteger(promptTimeoutMilliseconds) || promptTimeoutMilliseconds < 1) {
843
+ throw new Error("The local Agent prompt deadline is invalid");
844
+ }
654
845
  this.adapterIdentity = adapterIdentity;
655
846
  this.adapterPath = adapterIdentity.realPath;
656
847
  this.adapterVersion = adapterVersion;
@@ -658,6 +849,7 @@ var LocalAgentDefinition = class {
658
849
  this.agentVersion = agentVersion;
659
850
  this.id = id;
660
851
  this.label = label;
852
+ this.promptTimeoutMilliseconds = promptTimeoutMilliseconds;
661
853
  this.command = adapterIdentity.realPath;
662
854
  this.arguments = Object.freeze([...arguments_]);
663
855
  this.environment = Object.freeze({ ...environment });
@@ -729,16 +921,48 @@ var LocalAgentCatalog = class {
729
921
  var VERSION_PROBE_TIMEOUT_MILLISECONDS = 3e3;
730
922
  var ACP_PROBE_TIMEOUT_MILLISECONDS = 5e3;
731
923
  var ACP_OUTPUT_LIMIT_BYTES = 1024 * 1024;
924
+ var DEFAULT_PROMPT_TIMEOUT_MILLISECONDS = 30 * 60 * 1e3;
925
+ var PI_ACP_PROBE_TIMEOUT_MILLISECONDS = 3e4;
926
+ var PI_PROMPT_TIMEOUT_MILLISECONDS = 5 * 60 * 1e3;
732
927
  var LocalAgentSpecification = class {
928
+ acpProbeTimeoutMilliseconds = ACP_PROBE_TIMEOUT_MILLISECONDS;
929
+ authenticationCommand = () => "";
930
+ adapterVersionArguments = ["--version"];
931
+ agentVersionArguments = ["--version"];
932
+ promptTimeoutMilliseconds = DEFAULT_PROMPT_TIMEOUT_MILLISECONDS;
933
+ get adapterExecutable() {
934
+ return this.adapterPackageDefinition.executableName;
935
+ }
936
+ get adapterPackage() {
937
+ return this.adapterPackageDefinition.packageName;
938
+ }
939
+ get adapterVersion() {
940
+ return this.adapterPackageDefinition.version;
941
+ }
942
+ resolveManagedAgent = async (stateDirectory, platform) => {
943
+ const executable = await resolveExecutablePath(
944
+ join6(stateDirectory, ...this.managedAgentExecutablePath),
945
+ platform
946
+ );
947
+ if (executable === null) {
948
+ throw new Error("The managed Agent executable is unavailable");
949
+ }
950
+ return executable;
951
+ };
733
952
  recoveryCommand = () => `npm install -g --omit=optional ${this.adapterPackage}@${this.adapterVersion}`;
734
953
  supportsInstallation = (platform, architecture) => (platform === "darwin" || platform === "linux") && (architecture === "arm64" || architecture === "x64");
954
+ verifyAcp = (_connection, _initialized, _defaultWorkspace, _signal) => Promise.resolve();
735
955
  };
736
- var CodexAgentSpecification = class extends LocalAgentSpecification {
956
+ var BridgedAcpAgentDefinition = class extends LocalAgentSpecification {
957
+ };
958
+ var CodexAgentSpecification = class extends BridgedAcpAgentDefinition {
737
959
  adapterEnvironmentVariable = "CODEX_PATH";
738
- adapterExecutable = "codex-acp";
739
960
  adapterOverrideVariable = "MOBIUS_CODEX_ACP_PATH";
740
- adapterPackage = "@agentclientprotocol/codex-acp";
741
- adapterVersion = "1.8.0";
961
+ adapterPackageDefinition = new ManagedNpmPackageDefinition(
962
+ "@agentclientprotocol/codex-acp",
963
+ "1.8.0",
964
+ "codex-acp"
965
+ );
742
966
  agentExecutable = "codex";
743
967
  agentOverrideVariable = "MOBIUS_CODEX_PATH";
744
968
  id = "codex";
@@ -755,12 +979,14 @@ var CodexAgentSpecification = class extends LocalAgentSpecification {
755
979
  ];
756
980
  managedAgentExecutablePath = ["agents", "codex", "bin", "codex"];
757
981
  };
758
- var ClaudeCodeAgentSpecification = class extends LocalAgentSpecification {
982
+ var ClaudeCodeAgentSpecification = class extends BridgedAcpAgentDefinition {
759
983
  adapterEnvironmentVariable = "CLAUDE_CODE_EXECUTABLE";
760
- adapterExecutable = "claude-agent-acp";
761
984
  adapterOverrideVariable = "MOBIUS_CLAUDE_ACP_PATH";
762
- adapterPackage = "@agentclientprotocol/claude-agent-acp";
763
- adapterVersion = "0.73.0";
985
+ adapterPackageDefinition = new ManagedNpmPackageDefinition(
986
+ "@agentclientprotocol/claude-agent-acp",
987
+ "0.73.0",
988
+ "claude-agent-acp"
989
+ );
764
990
  agentExecutable = "claude";
765
991
  agentOverrideVariable = "MOBIUS_CLAUDE_PATH";
766
992
  id = "claude";
@@ -784,10 +1010,86 @@ var ClaudeCodeAgentSpecification = class extends LocalAgentSpecification {
784
1010
  "claude"
785
1011
  ];
786
1012
  };
1013
+ var PiAgentSpecification = class extends BridgedAcpAgentDefinition {
1014
+ acpProbeTimeoutMilliseconds = PI_ACP_PROBE_TIMEOUT_MILLISECONDS;
1015
+ adapterEnvironmentVariable = "PI_ACP_PI_COMMAND";
1016
+ adapterOverrideVariable = "MOBIUS_PI_ACP_PATH";
1017
+ adapterPackageDefinition = PI_ACP_PACKAGE;
1018
+ adapterVersionArguments = [];
1019
+ authenticationCommand = () => "pi-acp --terminal-login";
1020
+ agentExecutable = "pi";
1021
+ agentOverrideVariable = "MOBIUS_PI_PATH";
1022
+ id = "pi";
1023
+ label = "Pi";
1024
+ managedAdapterExecutablePath = [
1025
+ "adapters",
1026
+ "pi",
1027
+ PI_ACP_PACKAGE.version,
1028
+ "node_modules",
1029
+ "pi-acp",
1030
+ "dist",
1031
+ "index.js"
1032
+ ];
1033
+ managedAgentExecutablePath = [
1034
+ "agents",
1035
+ "pi",
1036
+ PI_AGENT_PACKAGE.version,
1037
+ "node_modules",
1038
+ "@earendil-works",
1039
+ "pi-coding-agent",
1040
+ "dist",
1041
+ "bundle",
1042
+ "cli.js"
1043
+ ];
1044
+ promptTimeoutMilliseconds = PI_PROMPT_TIMEOUT_MILLISECONDS;
1045
+ resolveManagedAgent = async (stateDirectory, _platform) => await validateManagedNpmPackage(
1046
+ join6(stateDirectory, "agents", this.id, PI_AGENT_PACKAGE.version),
1047
+ PI_AGENT_PACKAGE
1048
+ );
1049
+ verifyAcp = async (connection, initialized, defaultWorkspace, signal) => {
1050
+ const capabilities = initialized.agentCapabilities ?? {};
1051
+ if (capabilities.loadSession !== true) {
1052
+ throw new AgentProbeError(
1053
+ "acp_incompatible",
1054
+ "Pi ACP adapter does not support session recovery"
1055
+ );
1056
+ }
1057
+ const session = await connection.agent.request(
1058
+ methods2.agent.session.new,
1059
+ {
1060
+ additionalDirectories: [],
1061
+ cwd: defaultWorkspace,
1062
+ mcpServers: []
1063
+ },
1064
+ { cancellationSignal: signal }
1065
+ );
1066
+ if (session.sessionId.length === 0) {
1067
+ throw new AgentProbeError(
1068
+ "acp_incompatible",
1069
+ "Pi ACP adapter returned an invalid session"
1070
+ );
1071
+ }
1072
+ const prompt = await connection.agent.request(
1073
+ methods2.agent.session.prompt,
1074
+ {
1075
+ prompt: [{ text: "/session", type: "text" }],
1076
+ sessionId: session.sessionId
1077
+ },
1078
+ { cancellationSignal: signal }
1079
+ );
1080
+ if (prompt.stopReason !== "end_turn") {
1081
+ throw new AgentProbeError(
1082
+ "acp_incompatible",
1083
+ "Pi ACP adapter did not complete its conformance prompt"
1084
+ );
1085
+ }
1086
+ };
1087
+ };
787
1088
  function supportedLocalAgentSpecifications() {
788
1089
  return Object.freeze([
789
1090
  new CodexAgentSpecification(),
790
- new ClaudeCodeAgentSpecification()
1091
+ new ClaudeCodeAgentSpecification(),
1092
+ new PiAgentSpecification()
791
1093
  ]);
792
1094
  }
793
1095
  var AgentDiscovery = class {
@@ -840,6 +1142,14 @@ var IncompatibleAdapter = class extends UnavailableAgent {
840
1142
  super(specification);
841
1143
  }
842
1144
  };
1145
+ var AuthenticationRequiredAgent = class extends UnavailableAgent {
1146
+ code = "authentication_required";
1147
+ message = "Agent authentication is required";
1148
+ status = "authentication_required";
1149
+ constructor(specification) {
1150
+ super(specification);
1151
+ }
1152
+ };
843
1153
  var LocalAgentInventory = class {
844
1154
  available;
845
1155
  discoveries;
@@ -861,7 +1171,10 @@ var LocalAgentInventory = class {
861
1171
  this.discoveries = Object.freeze([...discoveries]);
862
1172
  this.installable = Object.freeze(
863
1173
  unavailable.filter(
864
- (discovery) => discovery.specification.supportsInstallation(platform, architecture)
1174
+ (discovery) => !(discovery instanceof AuthenticationRequiredAgent) && discovery.specification.supportsInstallation(
1175
+ platform,
1176
+ architecture
1177
+ )
865
1178
  ).map(
866
1179
  (discovery) => new InstallableAgentDefinition(
867
1180
  discovery.specification.id,
@@ -888,17 +1201,21 @@ var DiscoveryAcpHandler = class {
888
1201
  onPermission = (_request, _signal) => Promise.reject(new Error("ACP probe does not grant permissions"));
889
1202
  onUpdate = (_notification) => Promise.resolve();
890
1203
  };
891
- async function resolveManagedExecutable(managedExecutablePath, options) {
892
- return await resolveExecutablePath(
893
- join5(readStateDirectory(options.environment), ...managedExecutablePath),
894
- options.platform
895
- );
1204
+ async function resolveManagedExecutable(specification, options) {
1205
+ try {
1206
+ return await specification.resolveManagedAgent(
1207
+ readStateDirectory(options.environment),
1208
+ options.platform ?? process.platform
1209
+ );
1210
+ } catch {
1211
+ return null;
1212
+ }
896
1213
  }
897
1214
  async function resolveManagedAdapterExecutable(specification, options) {
898
1215
  let managed;
899
1216
  try {
900
1217
  managed = await validateManagedAdapter(
901
- join5(
1218
+ join6(
902
1219
  readStateDirectory(options.environment),
903
1220
  "adapters",
904
1221
  specification.id,
@@ -914,7 +1231,7 @@ async function resolveManagedAdapterExecutable(specification, options) {
914
1231
  async function resolveSpecificationExecutables(executableName, overrideVariable, resolveManaged, options) {
915
1232
  const override = options.environment[overrideVariable];
916
1233
  if (override !== void 0) {
917
- if (override.length === 0 || !isAbsolute(override)) {
1234
+ if (override.length === 0 || !isAbsolute2(override)) {
918
1235
  return [];
919
1236
  }
920
1237
  const executable = await resolveExecutablePath(override, options.platform);
@@ -938,10 +1255,7 @@ async function resolveAgentExecutables(specification, options) {
938
1255
  return await resolveSpecificationExecutables(
939
1256
  specification.agentExecutable,
940
1257
  specification.agentOverrideVariable,
941
- async () => await resolveManagedExecutable(
942
- specification.managedAgentExecutablePath,
943
- options
944
- ),
1258
+ async () => await resolveManagedExecutable(specification, options),
945
1259
  options
946
1260
  );
947
1261
  }
@@ -964,9 +1278,9 @@ async function resolveAdapterExecutable(specification, options) {
964
1278
  function normalizedVersion(output2) {
965
1279
  return output2.trim().split(/\r?\n/, 1)[0]?.trim().slice(0, 128) ?? "";
966
1280
  }
967
- async function probeVersion(executable, environment, defaultWorkspace, signal) {
1281
+ async function probeVersion(executable, arguments_, environment, defaultWorkspace, signal) {
968
1282
  const result = await runBoundedProcess({
969
- arguments: ["--version"],
1283
+ arguments: arguments_,
970
1284
  command: executable.path,
971
1285
  cwd: defaultWorkspace,
972
1286
  environment,
@@ -996,6 +1310,7 @@ async function findVerifiedAgentExecutable(specification, options) {
996
1310
  try {
997
1311
  const version2 = await probeVersion(
998
1312
  executable,
1313
+ specification.agentVersionArguments,
999
1314
  options.environment,
1000
1315
  options.defaultWorkspace,
1001
1316
  options.signal
@@ -1064,14 +1379,14 @@ function writableAcpStream(output2) {
1064
1379
  }
1065
1380
  });
1066
1381
  }
1067
- async function probeAcp(agent, environment, defaultWorkspace, signal) {
1382
+ async function probeAcp(specification, agent, environment, defaultWorkspace, signal) {
1068
1383
  signal.throwIfAborted();
1069
1384
  const controller = new AbortController();
1070
1385
  let timedOut = false;
1071
1386
  const timeout = setTimeout(() => {
1072
1387
  timedOut = true;
1073
1388
  controller.abort(new Error("ACP probe timed out"));
1074
- }, ACP_PROBE_TIMEOUT_MILLISECONDS);
1389
+ }, specification.acpProbeTimeoutMilliseconds);
1075
1390
  const abort = () => controller.abort(signal.reason);
1076
1391
  signal.addEventListener("abort", abort, { once: true });
1077
1392
  if (signal.aborted) {
@@ -1130,6 +1445,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
1130
1445
  "ACP adapter selected an unsupported protocol version"
1131
1446
  );
1132
1447
  }
1448
+ await specification.verifyAcp(
1449
+ connection,
1450
+ initialized,
1451
+ defaultWorkspace,
1452
+ controller.signal
1453
+ );
1454
+ return normalizedVersion(initialized.agentInfo?.version ?? "");
1133
1455
  } catch (error61) {
1134
1456
  if (signal.aborted) {
1135
1457
  signal.throwIfAborted();
@@ -1144,6 +1466,13 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
1144
1466
  if (error61 instanceof AgentProbeError) {
1145
1467
  throw error61;
1146
1468
  }
1469
+ if (error61 instanceof RequestError && error61.code === -32e3 && specification.authenticationCommand().length > 0) {
1470
+ throw new AgentProbeError(
1471
+ "authentication_required",
1472
+ "Agent authentication is required",
1473
+ { cause: error61 }
1474
+ );
1475
+ }
1147
1476
  throw new AgentProbeError(
1148
1477
  "acp_incompatible",
1149
1478
  "ACP adapter initialization failed",
@@ -1160,6 +1489,9 @@ async function probeAcp(agent, environment, defaultWorkspace, signal) {
1160
1489
  }
1161
1490
  function probeFailure(specification, executableKind, error61) {
1162
1491
  if (error61 instanceof AgentProbeError) {
1492
+ if (error61.code === "authentication_required") {
1493
+ return new AuthenticationRequiredAgent(specification);
1494
+ }
1163
1495
  return error61.code === "acp_incompatible" ? new IncompatibleAdapter(specification) : new ProbeFailed(specification, error61.code, error61.message);
1164
1496
  }
1165
1497
  if (error61 instanceof ProcessExecutionError) {
@@ -1194,6 +1526,7 @@ async function discoverSpecification(specification, options) {
1194
1526
  try {
1195
1527
  agentVersion = await probeVersion(
1196
1528
  agent,
1529
+ specification.agentVersionArguments,
1197
1530
  options.environment,
1198
1531
  options.defaultWorkspace,
1199
1532
  options.signal
@@ -1208,15 +1541,18 @@ async function discoverSpecification(specification, options) {
1208
1541
  for (const adapter of adapters) {
1209
1542
  let adapterVersion = "";
1210
1543
  try {
1211
- adapterVersion = await probeVersion(
1212
- adapter,
1213
- {
1214
- ...options.environment,
1215
- [specification.adapterEnvironmentVariable]: agent.path
1216
- },
1217
- options.defaultWorkspace,
1218
- options.signal
1219
- );
1544
+ if (specification.adapterVersionArguments.length > 0) {
1545
+ adapterVersion = await probeVersion(
1546
+ adapter,
1547
+ specification.adapterVersionArguments,
1548
+ {
1549
+ ...options.environment,
1550
+ [specification.adapterEnvironmentVariable]: agent.path
1551
+ },
1552
+ options.defaultWorkspace,
1553
+ options.signal
1554
+ );
1555
+ }
1220
1556
  } catch (error61) {
1221
1557
  if (options.signal.aborted) {
1222
1558
  options.signal.throwIfAborted();
@@ -1233,16 +1569,39 @@ async function discoverSpecification(specification, options) {
1233
1569
  adapterVersion,
1234
1570
  agentVersion,
1235
1571
  adapter.identity,
1236
- agent.identity
1572
+ agent.identity,
1573
+ specification.promptTimeoutMilliseconds
1237
1574
  );
1238
1575
  try {
1239
- await probeAcp(
1576
+ const acpVersion = await probeAcp(
1577
+ specification,
1240
1578
  definition,
1241
1579
  options.environment,
1242
1580
  options.defaultWorkspace,
1243
1581
  options.signal
1244
1582
  );
1245
- return new AvailableAgent(specification, definition);
1583
+ const verifiedAdapterVersion = adapterVersion.length > 0 ? adapterVersion : acpVersion;
1584
+ if (verifiedAdapterVersion.length === 0) {
1585
+ throw new AgentProbeError(
1586
+ "acp_incompatible",
1587
+ "ACP adapter did not report a version"
1588
+ );
1589
+ }
1590
+ return new AvailableAgent(
1591
+ specification,
1592
+ new LocalAgentDefinition(
1593
+ specification.id,
1594
+ specification.label,
1595
+ adapter.path,
1596
+ [],
1597
+ { [specification.adapterEnvironmentVariable]: agent.path },
1598
+ verifiedAdapterVersion,
1599
+ agentVersion,
1600
+ adapter.identity,
1601
+ agent.identity,
1602
+ specification.promptTimeoutMilliseconds
1603
+ )
1604
+ );
1246
1605
  } catch (error61) {
1247
1606
  if (options.signal.aborted) {
1248
1607
  options.signal.throwIfAborted();
@@ -1301,7 +1660,7 @@ var UnavailableAgentDiagnostic = class {
1301
1660
  this.id = discovery.specification.id;
1302
1661
  this.label = discovery.specification.label;
1303
1662
  this.message = discovery.message;
1304
- this.recoveryCommand = discovery instanceof MissingAdapter ? discovery.specification.recoveryCommand() : "";
1663
+ this.recoveryCommand = discovery instanceof MissingAdapter ? discovery.specification.recoveryCommand() : discovery.code === "authentication_required" ? discovery.specification.authenticationCommand() : "";
1305
1664
  this.status = discovery.status;
1306
1665
  }
1307
1666
  };
@@ -1341,7 +1700,9 @@ function formatAgentDiagnostics(inventory, json2) {
1341
1700
  humanLine(unavailable.label, unavailable.status, unavailable.message)
1342
1701
  );
1343
1702
  if (unavailable.recoveryCommand.length > 0) {
1344
- lines.push(` Install adapter: ${unavailable.recoveryCommand}`);
1703
+ lines.push(
1704
+ unavailable.code === "authentication_required" ? ` Authenticate locally: ${unavailable.recoveryCommand}` : ` Install adapter: ${unavailable.recoveryCommand}`
1705
+ );
1345
1706
  }
1346
1707
  }
1347
1708
  return `${lines.join("\n")}
@@ -1399,10 +1760,10 @@ var AuditStore = class {
1399
1760
  };
1400
1761
 
1401
1762
  // src/config.ts
1402
- import { join as join6 } from "node:path";
1763
+ import { join as join7 } from "node:path";
1403
1764
  function loadDefaultWorkspace() {
1404
1765
  const { MOBIUS_DEFAULT_WORKSPACE: configuredWorkspace } = process.env;
1405
- return configuredWorkspace ?? join6(readStateDirectory(), "workspace");
1766
+ return configuredWorkspace ?? join7(readStateDirectory(), "workspace");
1406
1767
  }
1407
1768
  function createDaemonConfig(connection, agents = [], installableAgents = []) {
1408
1769
  return {
@@ -1428,7 +1789,7 @@ import {
1428
1789
  writeFile
1429
1790
  } from "node:fs/promises";
1430
1791
  import { hostname } from "node:os";
1431
- import { basename, join as join7, resolve as resolve3 } from "node:path";
1792
+ import { basename, join as join8, resolve as resolve3 } from "node:path";
1432
1793
  import { fetch } from "undici";
1433
1794
 
1434
1795
  // src/network/environment-proxy.ts
@@ -1688,10 +2049,10 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
1688
2049
  return decodeClaimResponse(value);
1689
2050
  }
1690
2051
  async function persistConnection(channelId, relayOrigin, daemonToken, defaultWorkspace) {
1691
- const connectionsDirectory = join7(readStateDirectory(), "connections");
2052
+ const connectionsDirectory = join8(readStateDirectory(), "connections");
1692
2053
  await mkdir2(connectionsDirectory, { mode: 448, recursive: true });
1693
- const connectionPath = join7(connectionsDirectory, `${channelId}.json`);
1694
- const temporaryPath = join7(
2054
+ const connectionPath = join8(connectionsDirectory, `${channelId}.json`);
2055
+ const temporaryPath = join8(
1695
2056
  connectionsDirectory,
1696
2057
  `.${channelId}.${randomUUID()}.tmp`
1697
2058
  );
@@ -1738,7 +2099,7 @@ function decodeSavedConnection(value, expectedChannelId) {
1738
2099
  };
1739
2100
  }
1740
2101
  async function readSavedConnection(channelId) {
1741
- const connectionPath = join7(
2102
+ const connectionPath = join8(
1742
2103
  readStateDirectory(),
1743
2104
  "connections",
1744
2105
  `${channelId}.json`
@@ -1793,7 +2154,7 @@ function buildPairedDaemon(connection, connectionPath, inventory) {
1793
2154
  );
1794
2155
  relayUrl.protocol = relayUrl.protocol === "https:" ? "wss:" : "ws:";
1795
2156
  relayUrl.searchParams.set("token", connection.daemonToken);
1796
- const auditDatabasePath = join7(
2157
+ const auditDatabasePath = join8(
1797
2158
  readStateDirectory(),
1798
2159
  "audit",
1799
2160
  `${connection.channelId}.sqlite`
@@ -1852,7 +2213,7 @@ async function pairLocalMachine(arguments_, signal = new AbortController().signa
1852
2213
  }
1853
2214
  async function resumeLocalMachine(arguments_, signal = new AbortController().signal) {
1854
2215
  const channelId = parseChannelArgument(arguments_);
1855
- const connectionPath = join7(
2216
+ const connectionPath = join8(
1856
2217
  readStateDirectory(),
1857
2218
  "connections",
1858
2219
  `${channelId}.json`
@@ -24937,12 +25298,27 @@ var AcpSessionManager = class {
24937
25298
  "session_busy"
24938
25299
  );
24939
25300
  }
25301
+ const deadlineController = new AbortController();
25302
+ const deadlineError = new AcpExecutionError(
25303
+ "The local Agent prompt exceeded its deadline",
25304
+ {},
25305
+ "prompt_timeout"
25306
+ );
25307
+ const deadline = setTimeout(
25308
+ () => deadlineController.abort(deadlineError),
25309
+ options.agent.promptTimeoutMilliseconds
25310
+ );
25311
+ deadline.unref();
25312
+ const boundedOptions = {
25313
+ ...options,
25314
+ signal: AbortSignal.any([options.signal, deadlineController.signal])
25315
+ };
24940
25316
  this.#activeSessionIds.add(options.sessionId);
24941
25317
  try {
24942
25318
  if (this.#cancellationRequests.has(options.sessionId)) {
24943
25319
  return { stopReason: "cancelled" };
24944
25320
  }
24945
- const runtime = await this.ensureSession(options);
25321
+ const runtime = await this.ensureSession(boundedOptions);
24946
25322
  if (this.#nativeOperations.has(
24947
25323
  `${options.agent.id}:${runtime.nativeSessionId()}`
24948
25324
  ))
@@ -24954,7 +25330,7 @@ var AcpSessionManager = class {
24954
25330
  if (this.#cancellationRequests.has(options.sessionId)) {
24955
25331
  return { stopReason: "cancelled" };
24956
25332
  }
24957
- return await runtime.prompt(options);
25333
+ return await runtime.prompt(boundedOptions);
24958
25334
  } catch (error61) {
24959
25335
  const failedRuntime = this.#sessions.get(options.sessionId);
24960
25336
  if (failedRuntime !== void 0) {
@@ -24968,6 +25344,7 @@ var AcpSessionManager = class {
24968
25344
  cause: error61
24969
25345
  });
24970
25346
  } finally {
25347
+ clearTimeout(deadline);
24971
25348
  this.#activeSessionIds.delete(options.sessionId);
24972
25349
  this.#cancellationRequests.delete(options.sessionId);
24973
25350
  }
@@ -25777,7 +26154,7 @@ import { resolve as resolve4 } from "node:path";
25777
26154
 
25778
26155
  // src/agent-installation/fixed-agent-install-recipes.ts
25779
26156
  import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
25780
- import { join as join8 } from "node:path";
26157
+ import { join as join9 } from "node:path";
25781
26158
 
25782
26159
  // src/agent-installation/verified-download.ts
25783
26160
  import { createHash } from "node:crypto";
@@ -26001,18 +26378,18 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
26001
26378
  releaseChannel = "latest";
26002
26379
  install = async (options) => {
26003
26380
  this.assertSupportedPlatform();
26004
- const root = join8(options.stateDirectory, "agents", this.id);
26005
- const temporaryRoot = join8(options.stateDirectory, "temporary");
26381
+ const root = join9(options.stateDirectory, "agents", this.id);
26382
+ const temporaryRoot = join9(options.stateDirectory, "temporary");
26006
26383
  await Promise.all([
26007
26384
  mkdir3(root, { mode: 448, recursive: true }),
26008
26385
  mkdir3(temporaryRoot, { mode: 448, recursive: true })
26009
26386
  ]);
26010
26387
  const temporaryDirectory = await mkdtemp2(
26011
- join8(temporaryRoot, ".codex-install-")
26388
+ join9(temporaryRoot, ".codex-install-")
26012
26389
  );
26013
- const installerPath = join8(temporaryDirectory, "install.sh");
26014
- const installDirectory = join8(root, "bin");
26015
- const codexHome = join8(root, "home");
26390
+ const installerPath = join9(temporaryDirectory, "install.sh");
26391
+ const installDirectory = join9(root, "bin");
26392
+ const codexHome = join9(root, "home");
26016
26393
  try {
26017
26394
  await Promise.all([
26018
26395
  mkdir3(installDirectory, { mode: 448, recursive: true }),
@@ -26056,7 +26433,7 @@ var CodexInstallRecipe = class extends FixedAgentInstallRecipe {
26056
26433
  timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
26057
26434
  });
26058
26435
  return await this.requireInstalledExecutable(
26059
- join8(installDirectory, "codex")
26436
+ join9(installDirectory, "codex")
26060
26437
  );
26061
26438
  } catch (error61) {
26062
26439
  if (options.signal.aborted) {
@@ -26080,17 +26457,17 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26080
26457
  releaseChannel = "stable";
26081
26458
  install = async (options) => {
26082
26459
  this.assertSupportedPlatform();
26083
- const root = join8(options.stateDirectory, "agents", this.id);
26084
- const managedHome = join8(root, "home");
26085
- const temporaryRoot = join8(options.stateDirectory, "temporary");
26460
+ const root = join9(options.stateDirectory, "agents", this.id);
26461
+ const managedHome = join9(root, "home");
26462
+ const temporaryRoot = join9(options.stateDirectory, "temporary");
26086
26463
  await Promise.all([
26087
26464
  mkdir3(managedHome, { mode: 448, recursive: true }),
26088
26465
  mkdir3(temporaryRoot, { mode: 448, recursive: true })
26089
26466
  ]);
26090
26467
  const temporaryDirectory = await mkdtemp2(
26091
- join8(temporaryRoot, ".claude-install-")
26468
+ join9(temporaryRoot, ".claude-install-")
26092
26469
  );
26093
- const installerPath = join8(temporaryDirectory, "install.sh");
26470
+ const installerPath = join9(temporaryDirectory, "install.sh");
26094
26471
  try {
26095
26472
  await options.report("downloading");
26096
26473
  const download = await downloadVerifiedFile({
@@ -26127,7 +26504,7 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26127
26504
  timeoutMilliseconds: AGENT_INSTALL_TIMEOUT_MILLISECONDS
26128
26505
  });
26129
26506
  return await this.requireInstalledExecutable(
26130
- join8(managedHome, ".local", "bin", "claude")
26507
+ join9(managedHome, ".local", "bin", "claude")
26131
26508
  );
26132
26509
  } catch (error61) {
26133
26510
  if (options.signal.aborted) {
@@ -26145,10 +26522,82 @@ var ClaudeCodeInstallRecipe = class extends FixedAgentInstallRecipe {
26145
26522
  }
26146
26523
  };
26147
26524
  };
26525
+ var PiInstallRecipe = class extends FixedAgentInstallRecipe {
26526
+ #npmInstaller;
26527
+ id = "pi";
26528
+ label = "Pi";
26529
+ releaseChannel = PI_AGENT_PACKAGE.version;
26530
+ constructor(npmInstaller = new ManagedNpmPackageInstaller()) {
26531
+ super();
26532
+ this.#npmInstaller = npmInstaller;
26533
+ }
26534
+ install = async (options) => {
26535
+ this.assertSupportedPlatform();
26536
+ try {
26537
+ await options.report("downloading");
26538
+ await options.report("verifying");
26539
+ await options.report("installing_agent");
26540
+ const executable = await this.#npmInstaller.install(
26541
+ join9(
26542
+ options.stateDirectory,
26543
+ "agents",
26544
+ this.id,
26545
+ PI_AGENT_PACKAGE.version
26546
+ ),
26547
+ PI_AGENT_PACKAGE,
26548
+ options.environment,
26549
+ options.signal
26550
+ );
26551
+ options.audit.record(
26552
+ "agent_npm_package_verified",
26553
+ options.requestId,
26554
+ this.id,
26555
+ {
26556
+ integrity: PI_AGENT_PACKAGE.integrity,
26557
+ package: PI_AGENT_PACKAGE.packageName,
26558
+ version: PI_AGENT_PACKAGE.version
26559
+ }
26560
+ );
26561
+ return executable;
26562
+ } catch (error61) {
26563
+ if (options.signal.aborted) {
26564
+ options.signal.throwIfAborted();
26565
+ }
26566
+ if (error61 instanceof ManagedNpmPackageError) {
26567
+ if (error61.code === "managed_package_verification_failed") {
26568
+ throw new InstallationManagerError(
26569
+ "agent_verification_failed",
26570
+ "The managed Pi package did not match its pinned artifact",
26571
+ { cause: error61 }
26572
+ );
26573
+ }
26574
+ if (error61.code === "managed_package_publish_failed") {
26575
+ throw new InstallationManagerError(
26576
+ "agent_publish_failed",
26577
+ "The managed Pi package could not be published",
26578
+ { cause: error61 }
26579
+ );
26580
+ }
26581
+ throw new InstallationManagerError(error61.code, error61.message, {
26582
+ cause: error61
26583
+ });
26584
+ }
26585
+ if (error61 instanceof InstallationManagerError) {
26586
+ throw error61;
26587
+ }
26588
+ throw new InstallationManagerError(
26589
+ "agent_install_failed",
26590
+ "The fixed Pi installation recipe failed",
26591
+ { cause: error61 }
26592
+ );
26593
+ }
26594
+ };
26595
+ };
26148
26596
  function fixedAgentInstallRecipes() {
26149
26597
  return Object.freeze([
26150
26598
  new CodexInstallRecipe(),
26151
- new ClaudeCodeInstallRecipe()
26599
+ new ClaudeCodeInstallRecipe(),
26600
+ new PiInstallRecipe()
26152
26601
  ]);
26153
26602
  }
26154
26603
 
@@ -26278,6 +26727,7 @@ var FixedInstallationExecutor = class {
26278
26727
  if (agent === null) {
26279
26728
  agent = await recipe.install({
26280
26729
  audit: this.#audit,
26730
+ environment: this.#environment,
26281
26731
  report,
26282
26732
  requestId,
26283
26733
  signal,
@@ -26359,6 +26809,7 @@ var FixedInstallationExecutor = class {
26359
26809
  if (!installedAgent && (requiresAgentReplacement(unavailable.code) || unavailable.code === "acp_incompatible")) {
26360
26810
  agent = await recipe.install({
26361
26811
  audit: this.#audit,
26812
+ environment: this.#environment,
26362
26813
  report,
26363
26814
  requestId,
26364
26815
  signal,
@@ -26431,8 +26882,8 @@ import { homedir as homedir2 } from "node:os";
26431
26882
  import {
26432
26883
  basename as basename2,
26433
26884
  dirname as dirname4,
26434
- isAbsolute as isAbsolute2,
26435
- join as join9,
26885
+ isAbsolute as isAbsolute3,
26886
+ join as join10,
26436
26887
  normalize,
26437
26888
  relative as relative2
26438
26889
  } from "node:path";
@@ -26484,20 +26935,20 @@ var WorkspaceDirectoryService = class {
26484
26935
  if (id === "home") return homedir2();
26485
26936
  if (id.startsWith("path-")) {
26486
26937
  const path2 = decodePath(id, "path-");
26487
- if (isAbsolute2(path2)) return path2;
26938
+ if (isAbsolute3(path2)) return path2;
26488
26939
  }
26489
26940
  throw new LocalAuthorizationError("The directory identifier is invalid");
26490
26941
  };
26491
26942
  resolvePath = async (requested, signal) => {
26492
26943
  signal.throwIfAborted();
26493
- if (!isAbsolute2(requested) || requested.includes("\0") || normalize(requested) !== requested)
26944
+ if (!isAbsolute3(requested) || requested.includes("\0") || normalize(requested) !== requested)
26494
26945
  throw new LocalAuthorizationError("The local directory path is invalid");
26495
26946
  try {
26496
26947
  const target = await realpath4(requested);
26497
26948
  signal.throwIfAborted();
26498
26949
  const metadata = await stat4(target);
26499
26950
  signal.throwIfAborted();
26500
- if (!isAbsolute2(target) || normalize(target) !== target || !metadata.isDirectory())
26951
+ if (!isAbsolute3(target) || normalize(target) !== target || !metadata.isDirectory())
26501
26952
  throw new LocalAuthorizationError(
26502
26953
  "The selected location is not a directory"
26503
26954
  );
@@ -26513,7 +26964,7 @@ var WorkspaceDirectoryService = class {
26513
26964
  };
26514
26965
  label = (path2) => {
26515
26966
  const suffix = relative2(homedir2(), path2);
26516
- return suffix === "" ? "~" : !isAbsolute2(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
26967
+ return suffix === "" ? "~" : !isAbsolute3(suffix) && suffix !== ".." && !suffix.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ? `~/${suffix.replaceAll("\\", "/")}` : path2;
26517
26968
  };
26518
26969
  identify = async (bindingId, requestedPath, signal) => {
26519
26970
  this.authorizeMachine(bindingId);
@@ -26541,7 +26992,7 @@ var WorkspaceDirectoryService = class {
26541
26992
  continue;
26542
26993
  }
26543
26994
  try {
26544
- const path2 = await this.resolvePath(join9(target, child.name), signal);
26995
+ const path2 = await this.resolvePath(join10(target, child.name), signal);
26545
26996
  entries.push({ directoryId: encodePath(path2), name: child.name });
26546
26997
  } catch (cause) {
26547
26998
  signal.throwIfAborted();