acpx 0.15.0 → 0.16.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.
- package/dist/agent-registry-DU6mhZBL.js +785 -0
- package/dist/agent-registry-DU6mhZBL.js.map +1 -0
- package/dist/agent-registry.d.ts +70 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +2 -0
- package/dist/{cli-Cen2Rb6S.js → cli-BkYIxj_N.js} +6 -14
- package/dist/cli-BkYIxj_N.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +137 -333
- package/dist/cli.js.map +1 -1
- package/dist/{client-Cvz6msGc.d.ts → client-5levcBIs.d.ts} +11 -9
- package/dist/client-5levcBIs.d.ts.map +1 -0
- package/dist/{flows-D-F3Y2o9.js → flows-Bxs4udrs.js} +205 -277
- package/dist/flows-Bxs4udrs.js.map +1 -0
- package/dist/flows.d.ts +6 -17
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{flags-CRh8BJre.js → invocation-options-BY4lxkn3.js} +82 -58
- package/dist/invocation-options-BY4lxkn3.js.map +1 -0
- package/dist/{live-checkpoint-CYRs-D9t.js → live-checkpoint-BEfxBKCh.js} +1033 -1437
- package/dist/live-checkpoint-BEfxBKCh.js.map +1 -0
- package/dist/{output-CIVecnHU.js → output-uo5a27KB.js} +487 -490
- package/dist/output-uo5a27KB.js.map +1 -0
- package/dist/runtime.d.ts +105 -22
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +582 -366
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-DyIGRNfu.d.ts → session-options-t3d-F4dn.d.ts} +8 -6
- package/dist/session-options-t3d-F4dn.d.ts.map +1 -0
- package/package.json +27 -25
- package/skills/acpx/SKILL.md +19 -17
- package/dist/cli-Cen2Rb6S.js.map +0 -1
- package/dist/client-Cvz6msGc.d.ts.map +0 -1
- package/dist/flags-CRh8BJre.js.map +0 -1
- package/dist/flows-D-F3Y2o9.js.map +0 -1
- package/dist/live-checkpoint-CYRs-D9t.js.map +0 -1
- package/dist/output-CIVecnHU.js.map +0 -1
- package/dist/session-options-DyIGRNfu.d.ts.map +0 -1
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import { A as resolveAgentCommandParts, C as asAbsoluteCwd, E as isoNow$2, F as waitForSpawn, M as runTimedExecFile, N as splitCommandLine, P as waitForChildExit, S as PROCESS_HELPER_TIMEOUT_MS, T as isChildProcessRunning, _ as buildSpawnCommandOptions, b as readWindowsEnvValue, f as resolveBuiltInAgentLaunch, g as buildAgentSpawnCommand, j as resolveAgentSessionCwd, k as requireAgentStdio, n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY, v as buildTerminalShellSpawnCommand, w as basenameToken, x as resolveWindowsExecutablePath, y as buildTerminalSpawnCommand } from "./agent-registry-DU6mhZBL.js";
|
|
1
2
|
import fs, { readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
4
6
|
import fs$1 from "node:fs/promises";
|
|
5
7
|
import os from "node:os";
|
|
8
|
+
import { tempFile } from "@openclaw/fs-safe/advanced";
|
|
6
9
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
import { execFile, spawn } from "node:child_process";
|
|
8
10
|
import { Readable, Writable } from "node:stream";
|
|
9
|
-
import { PROTOCOL_VERSION, client, methods } from "@agentclientprotocol/sdk";
|
|
11
|
+
import { PROTOCOL_VERSION, RequestError, client, methods } from "@agentclientprotocol/sdk";
|
|
12
|
+
import { FsSafeError } from "@openclaw/fs-safe/errors";
|
|
13
|
+
import { isPathInside } from "@openclaw/fs-safe/path";
|
|
14
|
+
import { root } from "@openclaw/fs-safe/root";
|
|
10
15
|
import readline from "node:readline/promises";
|
|
16
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
11
17
|
//#region src/errors.ts
|
|
12
18
|
var AcpxOperationalError = class extends Error {
|
|
13
19
|
outputCode;
|
|
@@ -412,15 +418,11 @@ function isRetryablePromptError(error) {
|
|
|
412
418
|
if (isNonRetryablePromptError(error)) return false;
|
|
413
419
|
const acp = extractAcpError(error);
|
|
414
420
|
if (!acp) return false;
|
|
415
|
-
if (isPermanentPromptAcpError(acp)) return false;
|
|
416
421
|
return acp.code === -32603 || acp.code === -32700;
|
|
417
422
|
}
|
|
418
423
|
function isNonRetryablePromptError(error) {
|
|
419
424
|
return error instanceof PermissionDeniedError || error instanceof PermissionPromptUnavailableError || isTimeoutLike(error) || isNoSessionLike(error) || isUsageLike(error);
|
|
420
425
|
}
|
|
421
|
-
function isPermanentPromptAcpError(acp) {
|
|
422
|
-
return acp.code === -32001 || acp.code === -32002 || acp.code === -32601 || acp.code === -32602 || isAcpAuthRequiredPayload(acp);
|
|
423
|
-
}
|
|
424
426
|
function exitCodeForOutputErrorCode(code) {
|
|
425
427
|
switch (code) {
|
|
426
428
|
case "USAGE": return EXIT_CODES.USAGE;
|
|
@@ -432,254 +434,6 @@ function exitCodeForOutputErrorCode(code) {
|
|
|
432
434
|
}
|
|
433
435
|
}
|
|
434
436
|
//#endregion
|
|
435
|
-
//#region src/agent-registry.ts
|
|
436
|
-
const ACP_ADAPTER_PACKAGE_RANGES = {
|
|
437
|
-
pi: "^0.0.31",
|
|
438
|
-
codex: "^1.1.5",
|
|
439
|
-
claude: "^0.60.0",
|
|
440
|
-
mux: "^0.28.0"
|
|
441
|
-
};
|
|
442
|
-
const AGENT_REGISTRY = {
|
|
443
|
-
pi: `npx pi-acp@${ACP_ADAPTER_PACKAGE_RANGES.pi}`,
|
|
444
|
-
openclaw: "openclaw acp",
|
|
445
|
-
codex: `npx -y @agentclientprotocol/codex-acp@${ACP_ADAPTER_PACKAGE_RANGES.codex}`,
|
|
446
|
-
claude: `npx -y @agentclientprotocol/claude-agent-acp@${ACP_ADAPTER_PACKAGE_RANGES.claude}`,
|
|
447
|
-
gemini: "gemini --acp",
|
|
448
|
-
cursor: "cursor-agent acp",
|
|
449
|
-
copilot: "copilot --acp --stdio",
|
|
450
|
-
droid: "droid exec --output-format acp",
|
|
451
|
-
"fast-agent": "uvx fast-agent-mcp acp",
|
|
452
|
-
"grok-build": "grok agent stdio",
|
|
453
|
-
iflow: "iflow --experimental-acp",
|
|
454
|
-
kilocode: "npx -y @kilocode/cli acp",
|
|
455
|
-
kimi: "kimi acp",
|
|
456
|
-
kiro: "kiro-cli-chat acp",
|
|
457
|
-
mcode: "mcode acp",
|
|
458
|
-
mux: `npx -y mux@${ACP_ADAPTER_PACKAGE_RANGES.mux} acp`,
|
|
459
|
-
opencode: "npx -y opencode-ai acp",
|
|
460
|
-
pool: "pool acp",
|
|
461
|
-
qoder: "qodercli --acp",
|
|
462
|
-
qwen: "qwen --acp",
|
|
463
|
-
trae: "traecli acp serve",
|
|
464
|
-
zeroclaw: "zeroclaw acp"
|
|
465
|
-
};
|
|
466
|
-
const AGENT_ARGV_REGISTRY = {
|
|
467
|
-
pi: ["npx", `pi-acp@${ACP_ADAPTER_PACKAGE_RANGES.pi}`],
|
|
468
|
-
openclaw: ["openclaw", "acp"],
|
|
469
|
-
codex: [
|
|
470
|
-
"npx",
|
|
471
|
-
"-y",
|
|
472
|
-
`@agentclientprotocol/codex-acp@${ACP_ADAPTER_PACKAGE_RANGES.codex}`
|
|
473
|
-
],
|
|
474
|
-
claude: [
|
|
475
|
-
"npx",
|
|
476
|
-
"-y",
|
|
477
|
-
`@agentclientprotocol/claude-agent-acp@${ACP_ADAPTER_PACKAGE_RANGES.claude}`
|
|
478
|
-
],
|
|
479
|
-
gemini: ["gemini", "--acp"],
|
|
480
|
-
cursor: ["cursor-agent", "acp"],
|
|
481
|
-
copilot: [
|
|
482
|
-
"copilot",
|
|
483
|
-
"--acp",
|
|
484
|
-
"--stdio"
|
|
485
|
-
],
|
|
486
|
-
droid: [
|
|
487
|
-
"droid",
|
|
488
|
-
"exec",
|
|
489
|
-
"--output-format",
|
|
490
|
-
"acp"
|
|
491
|
-
],
|
|
492
|
-
"fast-agent": [
|
|
493
|
-
"uvx",
|
|
494
|
-
"fast-agent-mcp",
|
|
495
|
-
"acp"
|
|
496
|
-
],
|
|
497
|
-
"grok-build": [
|
|
498
|
-
"grok",
|
|
499
|
-
"agent",
|
|
500
|
-
"stdio"
|
|
501
|
-
],
|
|
502
|
-
iflow: ["iflow", "--experimental-acp"],
|
|
503
|
-
kilocode: [
|
|
504
|
-
"npx",
|
|
505
|
-
"-y",
|
|
506
|
-
"@kilocode/cli",
|
|
507
|
-
"acp"
|
|
508
|
-
],
|
|
509
|
-
kimi: ["kimi", "acp"],
|
|
510
|
-
kiro: ["kiro-cli-chat", "acp"],
|
|
511
|
-
mcode: ["mcode", "acp"],
|
|
512
|
-
mux: [
|
|
513
|
-
"npx",
|
|
514
|
-
"-y",
|
|
515
|
-
`mux@${ACP_ADAPTER_PACKAGE_RANGES.mux}`,
|
|
516
|
-
"acp"
|
|
517
|
-
],
|
|
518
|
-
opencode: [
|
|
519
|
-
"npx",
|
|
520
|
-
"-y",
|
|
521
|
-
"opencode-ai",
|
|
522
|
-
"acp"
|
|
523
|
-
],
|
|
524
|
-
pool: ["pool", "acp"],
|
|
525
|
-
qoder: ["qodercli", "--acp"],
|
|
526
|
-
qwen: ["qwen", "--acp"],
|
|
527
|
-
trae: [
|
|
528
|
-
"traecli",
|
|
529
|
-
"acp",
|
|
530
|
-
"serve"
|
|
531
|
-
],
|
|
532
|
-
zeroclaw: ["zeroclaw", "acp"]
|
|
533
|
-
};
|
|
534
|
-
const BUILT_IN_AGENT_PACKAGES = {
|
|
535
|
-
codex: {
|
|
536
|
-
packageName: "@agentclientprotocol/codex-acp",
|
|
537
|
-
packageRange: ACP_ADAPTER_PACKAGE_RANGES.codex,
|
|
538
|
-
preferredBinName: "codex-acp",
|
|
539
|
-
fallbackCommand: AGENT_REGISTRY.codex,
|
|
540
|
-
legacyFallbackCommands: []
|
|
541
|
-
},
|
|
542
|
-
claude: {
|
|
543
|
-
packageName: "@agentclientprotocol/claude-agent-acp",
|
|
544
|
-
packageRange: ACP_ADAPTER_PACKAGE_RANGES.claude,
|
|
545
|
-
preferredBinName: "claude-agent-acp",
|
|
546
|
-
fallbackCommand: AGENT_REGISTRY.claude,
|
|
547
|
-
legacyFallbackCommands: [`npm exec @agentclientprotocol/claude-agent-acp@${ACP_ADAPTER_PACKAGE_RANGES.claude}`]
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
const AGENT_ALIASES = {
|
|
551
|
-
"factory-droid": "droid",
|
|
552
|
-
factorydroid: "droid"
|
|
553
|
-
};
|
|
554
|
-
const DEFAULT_AGENT_NAME = "codex";
|
|
555
|
-
function normalizeAgentName$1(value) {
|
|
556
|
-
return value.trim().toLowerCase();
|
|
557
|
-
}
|
|
558
|
-
function resolveCanonicalAgentName(value) {
|
|
559
|
-
const normalized = normalizeAgentName$1(value);
|
|
560
|
-
return AGENT_ALIASES[normalized] ?? normalized;
|
|
561
|
-
}
|
|
562
|
-
function mergeAgentRegistry(overrides) {
|
|
563
|
-
if (!overrides) return { ...AGENT_REGISTRY };
|
|
564
|
-
const merged = { ...AGENT_REGISTRY };
|
|
565
|
-
for (const [name, command] of Object.entries(overrides)) {
|
|
566
|
-
const normalized = normalizeAgentName$1(name);
|
|
567
|
-
if (!normalized || !command.trim()) continue;
|
|
568
|
-
merged[normalized] = command.trim();
|
|
569
|
-
}
|
|
570
|
-
return merged;
|
|
571
|
-
}
|
|
572
|
-
function resolveAgentCommand(agentName, overrides) {
|
|
573
|
-
const normalized = normalizeAgentName$1(agentName);
|
|
574
|
-
const registry = mergeAgentRegistry(overrides);
|
|
575
|
-
return registry[normalized] ?? registry[AGENT_ALIASES[normalized] ?? normalized] ?? agentName;
|
|
576
|
-
}
|
|
577
|
-
function resolveAgentArgv(agentName) {
|
|
578
|
-
const normalized = normalizeAgentName$1(agentName);
|
|
579
|
-
const argv = AGENT_ARGV_REGISTRY[normalized] ?? AGENT_ARGV_REGISTRY[resolveCanonicalAgentName(agentName)];
|
|
580
|
-
return argv ? [...argv] : void 0;
|
|
581
|
-
}
|
|
582
|
-
function findBuiltInAgentPackage(agentCommand) {
|
|
583
|
-
const normalized = agentCommand.trim();
|
|
584
|
-
return Object.values(BUILT_IN_AGENT_PACKAGES).find((spec) => spec.fallbackCommand === normalized || spec.legacyFallbackCommands?.includes(normalized));
|
|
585
|
-
}
|
|
586
|
-
function defaultResolvePackageRoot(packageName) {
|
|
587
|
-
const segments = packageName.split("/");
|
|
588
|
-
let cursor = path.dirname(fileURLToPath(import.meta.url));
|
|
589
|
-
while (true) {
|
|
590
|
-
const candidateRoot = path.join(cursor, "node_modules", ...segments);
|
|
591
|
-
const manifestPath = path.join(candidateRoot, "package.json");
|
|
592
|
-
if (fs.existsSync(manifestPath)) try {
|
|
593
|
-
if (JSON.parse(fs.readFileSync(manifestPath, "utf8")).name === packageName) return candidateRoot;
|
|
594
|
-
} catch {}
|
|
595
|
-
const parent = path.dirname(cursor);
|
|
596
|
-
if (parent === cursor) throw new Error(`Built-in agent package not found: ${packageName}`);
|
|
597
|
-
cursor = parent;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
function resolvePackageBin(spec, manifest) {
|
|
601
|
-
if (typeof manifest.bin === "string") return manifest.bin;
|
|
602
|
-
if (!manifest.bin || typeof manifest.bin !== "object") return;
|
|
603
|
-
return manifest.bin[spec.preferredBinName] ?? (Object.keys(manifest.bin).length === 1 ? Object.values(manifest.bin)[0] : void 0);
|
|
604
|
-
}
|
|
605
|
-
function defaultResolveNpmCliPath(execPath) {
|
|
606
|
-
const candidate = path.resolve(path.dirname(execPath), "..", "lib", "node_modules", "npm", "bin", "npm-cli.js");
|
|
607
|
-
if (!fs.existsSync(candidate)) throw new Error(`npm CLI not found for execPath: ${execPath}`);
|
|
608
|
-
return candidate;
|
|
609
|
-
}
|
|
610
|
-
function resolveInstalledBuiltInAgentLaunch(agentCommand, options = {}) {
|
|
611
|
-
const spec = findBuiltInAgentPackage(agentCommand);
|
|
612
|
-
if (!spec) return;
|
|
613
|
-
const readFileSync = options.readFileSync ?? fs.readFileSync;
|
|
614
|
-
const existsSync = options.existsSync ?? fs.existsSync;
|
|
615
|
-
const resolvePackageRoot = options.resolvePackageRoot ?? defaultResolvePackageRoot;
|
|
616
|
-
try {
|
|
617
|
-
const resolved = resolveInstalledBuiltInAgentPackage(spec, {
|
|
618
|
-
readFileSync,
|
|
619
|
-
existsSync,
|
|
620
|
-
resolvePackageRoot
|
|
621
|
-
});
|
|
622
|
-
if (!resolved) return;
|
|
623
|
-
return {
|
|
624
|
-
source: "installed",
|
|
625
|
-
command: process.execPath,
|
|
626
|
-
args: [resolved.binPath],
|
|
627
|
-
packageName: spec.packageName,
|
|
628
|
-
packageRange: spec.packageRange,
|
|
629
|
-
packageVersion: resolved.packageVersion,
|
|
630
|
-
binPath: resolved.binPath
|
|
631
|
-
};
|
|
632
|
-
} catch {
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
function resolveInstalledBuiltInAgentPackage(spec, options) {
|
|
637
|
-
const packageRoot = options.resolvePackageRoot(spec.packageName);
|
|
638
|
-
const manifest = JSON.parse(options.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
639
|
-
if (manifest.name !== spec.packageName) return;
|
|
640
|
-
const relativeBinPath = resolvePackageBin(spec, manifest);
|
|
641
|
-
if (!relativeBinPath) return;
|
|
642
|
-
const binPath = path.resolve(packageRoot, relativeBinPath);
|
|
643
|
-
return options.existsSync(binPath) ? {
|
|
644
|
-
packageVersion: manifest.version,
|
|
645
|
-
binPath
|
|
646
|
-
} : void 0;
|
|
647
|
-
}
|
|
648
|
-
function resolvePackageExecBuiltInAgentLaunch(agentCommand, options = {}) {
|
|
649
|
-
const spec = findBuiltInAgentPackage(agentCommand);
|
|
650
|
-
if (!spec) return;
|
|
651
|
-
const existsSync = options.existsSync ?? fs.existsSync;
|
|
652
|
-
const execPath = options.execPath ?? process.execPath;
|
|
653
|
-
const resolveNpmCliPath = options.resolveNpmCliPath ?? defaultResolveNpmCliPath;
|
|
654
|
-
try {
|
|
655
|
-
const npmCliPath = resolveNpmCliPath(execPath);
|
|
656
|
-
if (!existsSync(npmCliPath)) return;
|
|
657
|
-
return {
|
|
658
|
-
source: "package-exec",
|
|
659
|
-
command: execPath,
|
|
660
|
-
args: [
|
|
661
|
-
npmCliPath,
|
|
662
|
-
"exec",
|
|
663
|
-
"--yes",
|
|
664
|
-
`--package=${spec.packageName}@${spec.packageRange}`,
|
|
665
|
-
"--",
|
|
666
|
-
spec.preferredBinName
|
|
667
|
-
],
|
|
668
|
-
packageName: spec.packageName,
|
|
669
|
-
packageRange: spec.packageRange,
|
|
670
|
-
npmCliPath
|
|
671
|
-
};
|
|
672
|
-
} catch {
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
function resolveBuiltInAgentLaunch(agentCommand, options = {}) {
|
|
677
|
-
return resolveInstalledBuiltInAgentLaunch(agentCommand, options) ?? resolvePackageExecBuiltInAgentLaunch(agentCommand, options);
|
|
678
|
-
}
|
|
679
|
-
function listBuiltInAgents(overrides) {
|
|
680
|
-
return [.../* @__PURE__ */ new Set([...Object.keys(AGENT_REGISTRY), ...Object.keys(overrides ?? {})])];
|
|
681
|
-
}
|
|
682
|
-
//#endregion
|
|
683
437
|
//#region src/async-control.ts
|
|
684
438
|
var TimeoutError = class extends Error {
|
|
685
439
|
constructor(timeoutMs) {
|
|
@@ -737,195 +491,38 @@ async function withInterrupt(run, onInterrupt) {
|
|
|
737
491
|
});
|
|
738
492
|
}
|
|
739
493
|
//#endregion
|
|
740
|
-
//#region src/
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
return
|
|
757
|
-
}
|
|
758
|
-
function isImageMimeType(value) {
|
|
759
|
-
return /^image\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
760
|
-
}
|
|
761
|
-
function isAudioMimeType(value) {
|
|
762
|
-
return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
763
|
-
}
|
|
764
|
-
function isTextBlock(value) {
|
|
765
|
-
const record = asRecord$6(value);
|
|
766
|
-
return record?.type === "text" && typeof record.text === "string";
|
|
767
|
-
}
|
|
768
|
-
function isImageBlock(value) {
|
|
769
|
-
const record = asRecord$6(value);
|
|
770
|
-
return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
771
|
-
}
|
|
772
|
-
function isAudioBlock(value) {
|
|
773
|
-
const record = asRecord$6(value);
|
|
774
|
-
return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
775
|
-
}
|
|
776
|
-
function isResourceLinkBlock(value) {
|
|
777
|
-
const record = asRecord$6(value);
|
|
778
|
-
return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title === void 0 || typeof record.title === "string") && (record.name === void 0 || typeof record.name === "string");
|
|
779
|
-
}
|
|
780
|
-
function isResourcePayload(value) {
|
|
781
|
-
const record = asRecord$6(value);
|
|
782
|
-
if (!record || !isNonEmptyString(record.uri)) return false;
|
|
783
|
-
return record.text === void 0 || typeof record.text === "string";
|
|
784
|
-
}
|
|
785
|
-
function isResourceBlock(value) {
|
|
786
|
-
const record = asRecord$6(value);
|
|
787
|
-
return record?.type === "resource" && isResourcePayload(record.resource);
|
|
788
|
-
}
|
|
789
|
-
const CONTENT_BLOCK_VALIDATORS = [
|
|
790
|
-
isTextBlock,
|
|
791
|
-
isImageBlock,
|
|
792
|
-
isAudioBlock,
|
|
793
|
-
isResourceLinkBlock,
|
|
794
|
-
isResourceBlock
|
|
795
|
-
];
|
|
796
|
-
function isContentBlock(value) {
|
|
797
|
-
return CONTENT_BLOCK_VALIDATORS.some((validator) => validator(value));
|
|
798
|
-
}
|
|
799
|
-
const CONTENT_BLOCK_ERROR_VALIDATORS = {
|
|
800
|
-
text: validateTextContentBlock,
|
|
801
|
-
image: validateImageContentBlock,
|
|
802
|
-
audio: validateAudioContentBlock,
|
|
803
|
-
resource_link: validateResourceLinkContentBlock,
|
|
804
|
-
resource: validateResourceContentBlock
|
|
805
|
-
};
|
|
806
|
-
function contentBlockErrorValidator(type) {
|
|
807
|
-
return Object.hasOwn(CONTENT_BLOCK_ERROR_VALIDATORS, type) ? CONTENT_BLOCK_ERROR_VALIDATORS[type] : void 0;
|
|
808
|
-
}
|
|
809
|
-
function validateTextContentBlock(record, index) {
|
|
810
|
-
return typeof record.text === "string" ? void 0 : `prompt[${index}] text block must include a string text field`;
|
|
811
|
-
}
|
|
812
|
-
function validateImageContentBlock(record, index) {
|
|
813
|
-
if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] image block must include a non-empty mimeType`;
|
|
814
|
-
if (!isImageMimeType(record.mimeType)) return `prompt[${index}] image block mimeType must start with image/`;
|
|
815
|
-
if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] image block must include non-empty base64 data`;
|
|
816
|
-
return isBase64Data(record.data) ? void 0 : `prompt[${index}] image block data must be valid base64`;
|
|
817
|
-
}
|
|
818
|
-
function validateAudioContentBlock(record, index) {
|
|
819
|
-
if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] audio block must include a non-empty mimeType`;
|
|
820
|
-
if (!isAudioMimeType(record.mimeType)) return `prompt[${index}] audio block mimeType must start with audio/`;
|
|
821
|
-
if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] audio block must include non-empty base64 data`;
|
|
822
|
-
return isBase64Data(record.data) ? void 0 : `prompt[${index}] audio block data must be valid base64`;
|
|
823
|
-
}
|
|
824
|
-
function validateResourceLinkContentBlock(record, index) {
|
|
825
|
-
if (!isNonEmptyString(record.uri)) return `prompt[${index}] resource_link block must include a non-empty uri`;
|
|
826
|
-
if (record.title !== void 0 && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string when present`;
|
|
827
|
-
if (record.name !== void 0 && typeof record.name !== "string") return `prompt[${index}] resource_link block name must be a string when present`;
|
|
828
|
-
}
|
|
829
|
-
function validateResourceContentBlock(record, index) {
|
|
830
|
-
if (!asRecord$6(record.resource)) return `prompt[${index}] resource block must include a resource object`;
|
|
831
|
-
return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and optional text`;
|
|
832
|
-
}
|
|
833
|
-
function getContentBlockValidationError(value, index) {
|
|
834
|
-
const record = asRecord$6(value);
|
|
835
|
-
if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
|
|
836
|
-
const validator = contentBlockErrorValidator(record.type);
|
|
837
|
-
return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
|
|
838
|
-
}
|
|
839
|
-
function isPromptInput(value) {
|
|
840
|
-
return Array.isArray(value) && value.every((entry) => isContentBlock(entry));
|
|
841
|
-
}
|
|
842
|
-
function promptCapabilityRequirement(block) {
|
|
843
|
-
switch (block.type) {
|
|
844
|
-
case "image": return {
|
|
845
|
-
blockType: "image",
|
|
846
|
-
capability: "image"
|
|
847
|
-
};
|
|
848
|
-
case "audio": return {
|
|
849
|
-
blockType: "audio",
|
|
850
|
-
capability: "audio"
|
|
851
|
-
};
|
|
852
|
-
case "resource": return {
|
|
853
|
-
blockType: "resource",
|
|
854
|
-
capability: "embeddedContext"
|
|
855
|
-
};
|
|
856
|
-
default: return;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
function getUnsupportedPromptContentMessage(prompt, agentCapabilities) {
|
|
860
|
-
for (const [index, block] of prompt.entries()) {
|
|
861
|
-
const requirement = promptCapabilityRequirement(block);
|
|
862
|
-
if (!requirement) continue;
|
|
863
|
-
if (agentCapabilities?.promptCapabilities?.[requirement.capability] === true) continue;
|
|
864
|
-
return `prompt[${index}] ${requirement.blockType} content requires agentCapabilities.promptCapabilities.${requirement.capability}`;
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
function textPrompt(text) {
|
|
868
|
-
return [{
|
|
869
|
-
type: "text",
|
|
870
|
-
text
|
|
871
|
-
}];
|
|
872
|
-
}
|
|
873
|
-
function parseStructuredPrompt(source) {
|
|
874
|
-
if (!source.startsWith("[")) return;
|
|
875
|
-
try {
|
|
876
|
-
const parsed = JSON.parse(source);
|
|
877
|
-
if (isPromptInput(parsed)) return parsed;
|
|
878
|
-
if (Array.isArray(parsed)) throw new PromptInputValidationError(parsed.map((entry, index) => getContentBlockValidationError(entry, index)).find((message) => message !== void 0) ?? "Structured prompt JSON must be an array of valid ACP content blocks");
|
|
879
|
-
return;
|
|
880
|
-
} catch (error) {
|
|
881
|
-
if (error instanceof PromptInputValidationError) throw error;
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
function parsePromptSource(source) {
|
|
886
|
-
const trimmed = source.trim();
|
|
887
|
-
const structured = parseStructuredPrompt(trimmed);
|
|
888
|
-
if (structured) return structured;
|
|
889
|
-
if (!trimmed) return [];
|
|
890
|
-
return textPrompt(trimmed);
|
|
891
|
-
}
|
|
892
|
-
function mergePromptSourceWithText(source, suffixText) {
|
|
893
|
-
const prompt = parsePromptSource(source);
|
|
894
|
-
const appended = suffixText.trim();
|
|
895
|
-
if (!appended) return prompt;
|
|
896
|
-
if (prompt.length === 0) return textPrompt(appended);
|
|
897
|
-
return [...prompt, ...textPrompt(appended)];
|
|
898
|
-
}
|
|
899
|
-
function promptToDisplayText(prompt) {
|
|
900
|
-
return prompt.map((block) => contentBlockDisplayText(block)).filter((entry) => entry.trim().length > 0).join("\n\n").trim();
|
|
901
|
-
}
|
|
902
|
-
function contentBlockDisplayText(block) {
|
|
903
|
-
switch (block.type) {
|
|
904
|
-
case "text": return block.text;
|
|
905
|
-
case "resource_link": return block.title ?? block.name ?? block.uri;
|
|
906
|
-
case "resource": return resourceBlockDisplayText(block);
|
|
907
|
-
case "image": return `[image] ${block.mimeType}`;
|
|
908
|
-
case "audio": return `[audio] ${block.mimeType}`;
|
|
909
|
-
default: return "";
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
function resourceBlockDisplayText(block) {
|
|
913
|
-
return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
|
|
494
|
+
//#region src/acp/codex-compat.ts
|
|
495
|
+
function isLegacyZedCodexAcpInvocation(agentCommand) {
|
|
496
|
+
return /@zed-industries\/codex-acp\b/u.test(agentCommand);
|
|
497
|
+
}
|
|
498
|
+
function preferCodexPermissionRefusal(request, agentName) {
|
|
499
|
+
if (agentName !== "@agentclientprotocol/codex-acp") return request;
|
|
500
|
+
const preferred = request.options.find((option) => option.kind === "reject_once" && ["decline", "reject_permissions"].includes(option.optionId));
|
|
501
|
+
return preferred ? {
|
|
502
|
+
...request,
|
|
503
|
+
options: [preferred, ...request.options.filter((option) => option !== preferred)]
|
|
504
|
+
} : request;
|
|
505
|
+
}
|
|
506
|
+
function codexPermissionNotice(request, response, agentName) {
|
|
507
|
+
if (agentName !== "@agentclientprotocol/codex-acp") return;
|
|
508
|
+
if (response.outcome.outcome === "cancelled") return "No matching permission option was available. The request was safely cancelled; Codex may end the current turn.";
|
|
509
|
+
const selectedId = response.outcome.optionId;
|
|
510
|
+
return request.options.find((option) => option.optionId === selectedId)?.kind === "reject_once" && selectedId === "cancel" ? "Permission refused using Codex's cancellation option; this can end the current turn. The operation was not approved." : void 0;
|
|
914
511
|
}
|
|
915
512
|
//#endregion
|
|
916
513
|
//#region src/acp/jsonrpc.ts
|
|
917
|
-
function asRecord$
|
|
514
|
+
function asRecord$6(value) {
|
|
918
515
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
919
516
|
return value;
|
|
920
517
|
}
|
|
921
518
|
function isAcpMessageObject(value) {
|
|
922
|
-
return asRecord$
|
|
519
|
+
return asRecord$6(value) !== null;
|
|
923
520
|
}
|
|
924
521
|
function hasValidId(value) {
|
|
925
522
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
926
523
|
}
|
|
927
524
|
function isErrorObject(value) {
|
|
928
|
-
const record = asRecord$
|
|
525
|
+
const record = asRecord$6(value);
|
|
929
526
|
return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
|
|
930
527
|
}
|
|
931
528
|
function hasResultOrError(value) {
|
|
@@ -950,7 +547,7 @@ function isJsonRpcResponse(value) {
|
|
|
950
547
|
return hasResultOrError(value);
|
|
951
548
|
}
|
|
952
549
|
function isAcpJsonRpcMessage(value) {
|
|
953
|
-
const record = asRecord$
|
|
550
|
+
const record = asRecord$6(value);
|
|
954
551
|
if (!record || record.jsonrpc !== "2.0") return false;
|
|
955
552
|
return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
|
|
956
553
|
}
|
|
@@ -962,11 +559,11 @@ function isSessionUpdateNotification(message) {
|
|
|
962
559
|
}
|
|
963
560
|
function extractSessionUpdateNotification(message) {
|
|
964
561
|
if (!isSessionUpdateNotification(message)) return;
|
|
965
|
-
const params = asRecord$
|
|
562
|
+
const params = asRecord$6(message.params);
|
|
966
563
|
if (!params) return;
|
|
967
564
|
const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
|
|
968
565
|
if (!sessionId) return;
|
|
969
|
-
const update = asRecord$
|
|
566
|
+
const update = asRecord$6(params.update);
|
|
970
567
|
if (!update || typeof update.sessionUpdate !== "string") return;
|
|
971
568
|
return {
|
|
972
569
|
sessionId,
|
|
@@ -975,33 +572,139 @@ function extractSessionUpdateNotification(message) {
|
|
|
975
572
|
}
|
|
976
573
|
function parsePromptStopReason(message) {
|
|
977
574
|
if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
|
|
978
|
-
const record = asRecord$
|
|
575
|
+
const record = asRecord$6(message.result);
|
|
979
576
|
if (!record) return;
|
|
980
577
|
return typeof record.stopReason === "string" ? record.stopReason : void 0;
|
|
981
578
|
}
|
|
579
|
+
function parsePermissionNotice(message) {
|
|
580
|
+
if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
|
|
581
|
+
const notice = asRecord$6(asRecord$6(asRecord$6(message.result)?._meta)?.acpx)?.permissionNotice;
|
|
582
|
+
return typeof notice === "string" ? notice : void 0;
|
|
583
|
+
}
|
|
982
584
|
function parseJsonRpcErrorMessage(message) {
|
|
983
585
|
if (!Object.hasOwn(message, "error")) return;
|
|
984
|
-
const errorRecord = asRecord$
|
|
586
|
+
const errorRecord = asRecord$6(message.error);
|
|
985
587
|
if (!errorRecord || typeof errorRecord.message !== "string") return;
|
|
986
588
|
return errorRecord.message;
|
|
987
589
|
}
|
|
988
590
|
//#endregion
|
|
591
|
+
//#region src/process-liveness.ts
|
|
592
|
+
function isProcessAlive(pid) {
|
|
593
|
+
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
594
|
+
try {
|
|
595
|
+
process.kill(pid, 0);
|
|
596
|
+
return true;
|
|
597
|
+
} catch {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
//#endregion
|
|
602
|
+
//#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
|
|
603
|
+
function _usingCtx() {
|
|
604
|
+
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
605
|
+
var n = Error();
|
|
606
|
+
return n.name = "SuppressedError", n.error = r, n.suppressed = e, n;
|
|
607
|
+
}, e = {}, n = [];
|
|
608
|
+
function using(r, e) {
|
|
609
|
+
if (null != e) {
|
|
610
|
+
if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
|
|
611
|
+
if (r) var o = e[Symbol.asyncDispose || Symbol["for"]("Symbol.asyncDispose")];
|
|
612
|
+
if (void 0 === o && (o = e[Symbol.dispose || Symbol["for"]("Symbol.dispose")], r)) var t = o;
|
|
613
|
+
if ("function" != typeof o) throw new TypeError("Object is not disposable.");
|
|
614
|
+
t && (o = function o() {
|
|
615
|
+
try {
|
|
616
|
+
t.call(e);
|
|
617
|
+
} catch (r) {
|
|
618
|
+
return Promise.reject(r);
|
|
619
|
+
}
|
|
620
|
+
}), n.push({
|
|
621
|
+
v: e,
|
|
622
|
+
d: o,
|
|
623
|
+
a: r
|
|
624
|
+
});
|
|
625
|
+
} else r && n.push({
|
|
626
|
+
d: e,
|
|
627
|
+
a: r
|
|
628
|
+
});
|
|
629
|
+
return e;
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
e,
|
|
633
|
+
u: using.bind(null, !1),
|
|
634
|
+
a: using.bind(null, !0),
|
|
635
|
+
d: function d() {
|
|
636
|
+
var o, t = this.e, s = 0;
|
|
637
|
+
function next() {
|
|
638
|
+
for (; o = n.pop();) try {
|
|
639
|
+
if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next);
|
|
640
|
+
if (o.d) {
|
|
641
|
+
var r = o.d.call(o.v);
|
|
642
|
+
if (o.a) return s |= 2, Promise.resolve(r).then(next, err);
|
|
643
|
+
} else s |= 1;
|
|
644
|
+
} catch (r) {
|
|
645
|
+
return err(r);
|
|
646
|
+
}
|
|
647
|
+
if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve();
|
|
648
|
+
if (t !== e) throw t;
|
|
649
|
+
}
|
|
650
|
+
function err(n) {
|
|
651
|
+
return t = t !== e ? new r(n, t) : n, next();
|
|
652
|
+
}
|
|
653
|
+
return next();
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
//#endregion
|
|
658
|
+
//#region src/state-files.ts
|
|
659
|
+
async function writePrivateFile(filePath, content, options = {}) {
|
|
660
|
+
try {
|
|
661
|
+
var _usingCtx$1 = _usingCtx();
|
|
662
|
+
const privateDirectory = options.privateDirectory !== false;
|
|
663
|
+
await fs$1.mkdir(path.dirname(filePath), {
|
|
664
|
+
recursive: true,
|
|
665
|
+
mode: privateDirectory ? 448 : void 0
|
|
666
|
+
});
|
|
667
|
+
const directory = await fs$1.realpath(path.dirname(filePath));
|
|
668
|
+
if (privateDirectory && process.platform !== "win32") await fs$1.chmod(directory, 448);
|
|
669
|
+
const temporary = _usingCtx$1.a(await tempFile({
|
|
670
|
+
rootDir: directory,
|
|
671
|
+
prefix: "acpx-write",
|
|
672
|
+
fileName: "record.json"
|
|
673
|
+
}));
|
|
674
|
+
const handle = await fs$1.open(temporary.path, "wx", 384);
|
|
675
|
+
try {
|
|
676
|
+
await handle.writeFile(content, "utf8");
|
|
677
|
+
await handle.chmod(384);
|
|
678
|
+
} finally {
|
|
679
|
+
await handle.close();
|
|
680
|
+
}
|
|
681
|
+
await fs$1.rename(temporary.path, path.join(directory, path.basename(filePath)));
|
|
682
|
+
} catch (_) {
|
|
683
|
+
_usingCtx$1.e = _;
|
|
684
|
+
} finally {
|
|
685
|
+
await _usingCtx$1.d();
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async function writePrivateJsonFile(filePath, value) {
|
|
689
|
+
await writePrivateFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
690
|
+
}
|
|
691
|
+
//#endregion
|
|
989
692
|
//#region src/session/event-log.ts
|
|
990
693
|
const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 67108864;
|
|
991
|
-
function sessionBaseDir
|
|
694
|
+
function sessionBaseDir() {
|
|
992
695
|
return path.join(os.homedir(), ".acpx", "sessions");
|
|
993
696
|
}
|
|
994
697
|
function safeSessionId(sessionId) {
|
|
995
698
|
return encodeURIComponent(sessionId);
|
|
996
699
|
}
|
|
997
700
|
function sessionEventActivePath(sessionId) {
|
|
998
|
-
return path.join(sessionBaseDir
|
|
701
|
+
return path.join(sessionBaseDir(), `${safeSessionId(sessionId)}.stream.ndjson`);
|
|
999
702
|
}
|
|
1000
703
|
function sessionEventSegmentPath(sessionId, segment) {
|
|
1001
|
-
return path.join(sessionBaseDir
|
|
704
|
+
return path.join(sessionBaseDir(), `${safeSessionId(sessionId)}.stream.${segment}.ndjson`);
|
|
1002
705
|
}
|
|
1003
706
|
function sessionEventLockPath(sessionId) {
|
|
1004
|
-
return path.join(sessionBaseDir
|
|
707
|
+
return path.join(sessionBaseDir(), `${safeSessionId(sessionId)}.stream.lock`);
|
|
1005
708
|
}
|
|
1006
709
|
function defaultSessionEventLog(sessionId) {
|
|
1007
710
|
return {
|
|
@@ -1034,57 +737,45 @@ function extractAgentSessionId(meta) {
|
|
|
1034
737
|
}
|
|
1035
738
|
}
|
|
1036
739
|
//#endregion
|
|
1037
|
-
//#region src/session/runtime-session-id.ts
|
|
1038
|
-
function normalizeRuntimeSessionId(value) {
|
|
1039
|
-
return normalizeAgentSessionId(value);
|
|
1040
|
-
}
|
|
1041
|
-
function extractRuntimeSessionId(meta) {
|
|
1042
|
-
return extractAgentSessionId(meta);
|
|
1043
|
-
}
|
|
1044
|
-
//#endregion
|
|
1045
740
|
//#region src/session/persistence/serialize.ts
|
|
1046
741
|
function serializeSessionRecordForDisk(record) {
|
|
1047
|
-
const canonical = {
|
|
1048
|
-
...record,
|
|
1049
|
-
schema: SESSION_RECORD_SCHEMA
|
|
1050
|
-
};
|
|
1051
742
|
return {
|
|
1052
|
-
schema:
|
|
1053
|
-
acpx_record_id:
|
|
1054
|
-
acp_session_id:
|
|
1055
|
-
agent_session_id:
|
|
1056
|
-
agent_command:
|
|
1057
|
-
agent_argv:
|
|
1058
|
-
cwd:
|
|
1059
|
-
name:
|
|
1060
|
-
created_at:
|
|
1061
|
-
last_used_at:
|
|
1062
|
-
last_seq:
|
|
1063
|
-
last_request_id:
|
|
1064
|
-
event_log:
|
|
1065
|
-
closed:
|
|
1066
|
-
closed_at:
|
|
1067
|
-
pid:
|
|
1068
|
-
agent_started_at:
|
|
1069
|
-
last_prompt_at:
|
|
1070
|
-
last_agent_exit_code:
|
|
1071
|
-
last_agent_exit_signal:
|
|
1072
|
-
last_agent_exit_at:
|
|
1073
|
-
last_agent_disconnect_reason:
|
|
1074
|
-
protocol_version:
|
|
1075
|
-
agent_capabilities:
|
|
1076
|
-
title:
|
|
1077
|
-
messages:
|
|
1078
|
-
updated_at:
|
|
1079
|
-
cumulative_token_usage:
|
|
1080
|
-
cumulative_cost:
|
|
1081
|
-
request_token_usage:
|
|
1082
|
-
acpx:
|
|
1083
|
-
imported_from:
|
|
1084
|
-
record_id:
|
|
1085
|
-
cwd_original:
|
|
1086
|
-
exported_by:
|
|
1087
|
-
exported_at:
|
|
743
|
+
schema: SESSION_RECORD_SCHEMA,
|
|
744
|
+
acpx_record_id: record.acpxRecordId,
|
|
745
|
+
acp_session_id: record.acpSessionId,
|
|
746
|
+
agent_session_id: normalizeAgentSessionId(record.agentSessionId),
|
|
747
|
+
agent_command: record.agentCommand,
|
|
748
|
+
agent_argv: record.agentArgv,
|
|
749
|
+
cwd: record.cwd,
|
|
750
|
+
name: record.name,
|
|
751
|
+
created_at: record.createdAt,
|
|
752
|
+
last_used_at: record.lastUsedAt,
|
|
753
|
+
last_seq: record.lastSeq,
|
|
754
|
+
last_request_id: record.lastRequestId,
|
|
755
|
+
event_log: record.eventLog,
|
|
756
|
+
closed: record.closed,
|
|
757
|
+
closed_at: record.closedAt,
|
|
758
|
+
pid: record.pid,
|
|
759
|
+
agent_started_at: record.agentStartedAt,
|
|
760
|
+
last_prompt_at: record.lastPromptAt,
|
|
761
|
+
last_agent_exit_code: record.lastAgentExitCode,
|
|
762
|
+
last_agent_exit_signal: record.lastAgentExitSignal,
|
|
763
|
+
last_agent_exit_at: record.lastAgentExitAt,
|
|
764
|
+
last_agent_disconnect_reason: record.lastAgentDisconnectReason,
|
|
765
|
+
protocol_version: record.protocolVersion,
|
|
766
|
+
agent_capabilities: record.agentCapabilities,
|
|
767
|
+
title: record.title,
|
|
768
|
+
messages: record.messages,
|
|
769
|
+
updated_at: record.updated_at,
|
|
770
|
+
cumulative_token_usage: record.cumulative_token_usage,
|
|
771
|
+
cumulative_cost: record.cumulative_cost,
|
|
772
|
+
request_token_usage: record.request_token_usage,
|
|
773
|
+
acpx: record.acpx,
|
|
774
|
+
imported_from: record.importedFrom ? {
|
|
775
|
+
record_id: record.importedFrom.recordId,
|
|
776
|
+
cwd_original: record.importedFrom.cwdOriginal,
|
|
777
|
+
exported_by: record.importedFrom.exportedBy,
|
|
778
|
+
exported_at: record.importedFrom.exportedAt
|
|
1088
779
|
} : void 0
|
|
1089
780
|
};
|
|
1090
781
|
}
|
|
@@ -1134,7 +825,7 @@ function resolveAgentArgvForCommand(agentCommand) {
|
|
|
1134
825
|
}
|
|
1135
826
|
//#endregion
|
|
1136
827
|
//#region src/session/persistence/parse.ts
|
|
1137
|
-
function asRecord$
|
|
828
|
+
function asRecord$5(value) {
|
|
1138
829
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1139
830
|
return value;
|
|
1140
831
|
}
|
|
@@ -1153,12 +844,12 @@ function parsePersistedAgentArgv(record) {
|
|
|
1153
844
|
function hasModelConfigOption(options) {
|
|
1154
845
|
if (!Array.isArray(options)) return false;
|
|
1155
846
|
return options.some((entry) => {
|
|
1156
|
-
const option = asRecord$
|
|
847
|
+
const option = asRecord$5(entry);
|
|
1157
848
|
return option?.category === "model" || option?.id === "model";
|
|
1158
849
|
});
|
|
1159
850
|
}
|
|
1160
851
|
function parseConfigOptions(raw) {
|
|
1161
|
-
if (!Array.isArray(raw) || !raw.every((entry) => asRecord$
|
|
852
|
+
if (!Array.isArray(raw) || !raw.every((entry) => asRecord$5(entry) !== void 0)) return;
|
|
1162
853
|
return raw;
|
|
1163
854
|
}
|
|
1164
855
|
function parseAvailableCommand(raw) {
|
|
@@ -1166,7 +857,7 @@ function parseAvailableCommand(raw) {
|
|
|
1166
857
|
const name = raw.trim();
|
|
1167
858
|
return name ? { name } : void 0;
|
|
1168
859
|
}
|
|
1169
|
-
const record = asRecord$
|
|
860
|
+
const record = asRecord$5(raw);
|
|
1170
861
|
if (!record) return;
|
|
1171
862
|
const name = parseNonEmptyString(record.name);
|
|
1172
863
|
if (!name) return;
|
|
@@ -1189,7 +880,7 @@ function parseNonEmptyString(value) {
|
|
|
1189
880
|
}
|
|
1190
881
|
function parseTokenUsage(raw) {
|
|
1191
882
|
if (raw === void 0 || raw === null) return;
|
|
1192
|
-
const record = asRecord$
|
|
883
|
+
const record = asRecord$5(raw);
|
|
1193
884
|
if (!record) return null;
|
|
1194
885
|
const usage = {};
|
|
1195
886
|
for (const field of [
|
|
@@ -1212,7 +903,7 @@ function isNonNegativeFiniteNumber(value) {
|
|
|
1212
903
|
}
|
|
1213
904
|
function parseUsageCost(raw) {
|
|
1214
905
|
if (raw === void 0 || raw === null) return;
|
|
1215
|
-
const record = asRecord$
|
|
906
|
+
const record = asRecord$5(raw);
|
|
1216
907
|
if (!record) return null;
|
|
1217
908
|
return parseUsageCostRecord(record);
|
|
1218
909
|
}
|
|
@@ -1238,7 +929,7 @@ function parseCostCurrency(value) {
|
|
|
1238
929
|
}
|
|
1239
930
|
function parseRequestTokenUsage(raw) {
|
|
1240
931
|
if (raw === void 0 || raw === null) return;
|
|
1241
|
-
const record = asRecord$
|
|
932
|
+
const record = asRecord$5(raw);
|
|
1242
933
|
if (!record) return null;
|
|
1243
934
|
const usage = {};
|
|
1244
935
|
for (const [key, value] of Object.entries(record)) {
|
|
@@ -1249,25 +940,25 @@ function parseRequestTokenUsage(raw) {
|
|
|
1249
940
|
return usage;
|
|
1250
941
|
}
|
|
1251
942
|
function isSessionMessageImage(raw) {
|
|
1252
|
-
const record = asRecord$
|
|
943
|
+
const record = asRecord$5(raw);
|
|
1253
944
|
if (!record || typeof record.source !== "string") return false;
|
|
1254
945
|
if (record.size === void 0 || record.size === null) return true;
|
|
1255
|
-
const size = asRecord$
|
|
946
|
+
const size = asRecord$5(record.size);
|
|
1256
947
|
return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
|
|
1257
948
|
}
|
|
1258
949
|
function isSessionMessageAudio(raw) {
|
|
1259
|
-
const record = asRecord$
|
|
950
|
+
const record = asRecord$5(raw);
|
|
1260
951
|
return !!record && typeof record.source === "string" && typeof record.mime_type === "string";
|
|
1261
952
|
}
|
|
1262
953
|
function isFiniteNumber(value) {
|
|
1263
954
|
return typeof value === "number" && Number.isFinite(value);
|
|
1264
955
|
}
|
|
1265
956
|
function isUserContent(raw) {
|
|
1266
|
-
const record = asRecord$
|
|
957
|
+
const record = asRecord$5(raw);
|
|
1267
958
|
if (!record) return false;
|
|
1268
959
|
if (typeof record.Text === "string") return true;
|
|
1269
960
|
if (record.Mention !== void 0) {
|
|
1270
|
-
const mention = asRecord$
|
|
961
|
+
const mention = asRecord$5(record.Mention);
|
|
1271
962
|
return !!mention && typeof mention.uri === "string" && typeof mention.content === "string";
|
|
1272
963
|
}
|
|
1273
964
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
@@ -1275,7 +966,7 @@ function isUserContent(raw) {
|
|
|
1275
966
|
return false;
|
|
1276
967
|
}
|
|
1277
968
|
function isToolUse(raw) {
|
|
1278
|
-
const record = asRecord$
|
|
969
|
+
const record = asRecord$5(raw);
|
|
1279
970
|
return !!record && hasStringFields(record, [
|
|
1280
971
|
"id",
|
|
1281
972
|
"name",
|
|
@@ -1289,18 +980,18 @@ function isOptionalString(value) {
|
|
|
1289
980
|
return value === void 0 || value === null || typeof value === "string";
|
|
1290
981
|
}
|
|
1291
982
|
function isToolResultContent(raw) {
|
|
1292
|
-
const record = asRecord$
|
|
983
|
+
const record = asRecord$5(raw);
|
|
1293
984
|
if (!record) return false;
|
|
1294
985
|
if (typeof record.Text === "string") return true;
|
|
1295
986
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
1296
987
|
return false;
|
|
1297
988
|
}
|
|
1298
989
|
function isToolResult(raw) {
|
|
1299
|
-
const record = asRecord$
|
|
990
|
+
const record = asRecord$5(raw);
|
|
1300
991
|
return !!record && typeof record.tool_use_id === "string" && typeof record.tool_name === "string" && typeof record.is_error === "boolean" && isToolResultContent(record.content);
|
|
1301
992
|
}
|
|
1302
993
|
function isAgentContent(raw) {
|
|
1303
|
-
const record = asRecord$
|
|
994
|
+
const record = asRecord$5(raw);
|
|
1304
995
|
if (!record) return false;
|
|
1305
996
|
if (typeof record.Text === "string") return true;
|
|
1306
997
|
if (record.Thinking !== void 0) return isThinkingContent(record.Thinking);
|
|
@@ -1309,21 +1000,21 @@ function isAgentContent(raw) {
|
|
|
1309
1000
|
return false;
|
|
1310
1001
|
}
|
|
1311
1002
|
function isThinkingContent(raw) {
|
|
1312
|
-
const thinking = asRecord$
|
|
1003
|
+
const thinking = asRecord$5(raw);
|
|
1313
1004
|
return !!thinking && typeof thinking.text === "string" && isOptionalString(thinking.signature);
|
|
1314
1005
|
}
|
|
1315
1006
|
function isUserMessage$1(raw) {
|
|
1316
|
-
const record = asRecord$
|
|
1007
|
+
const record = asRecord$5(raw);
|
|
1317
1008
|
if (!record || record.User === void 0) return false;
|
|
1318
|
-
const user = asRecord$
|
|
1009
|
+
const user = asRecord$5(record.User);
|
|
1319
1010
|
return !!user && typeof user.id === "string" && Array.isArray(user.content) && user.content.every((entry) => isUserContent(entry));
|
|
1320
1011
|
}
|
|
1321
1012
|
function isAgentMessage$1(raw) {
|
|
1322
|
-
const record = asRecord$
|
|
1013
|
+
const record = asRecord$5(raw);
|
|
1323
1014
|
if (!record || record.Agent === void 0) return false;
|
|
1324
|
-
const agent = asRecord$
|
|
1015
|
+
const agent = asRecord$5(record.Agent);
|
|
1325
1016
|
if (!agent || !Array.isArray(agent.content) || !agent.content.every(isAgentContent)) return false;
|
|
1326
|
-
const toolResults = asRecord$
|
|
1017
|
+
const toolResults = asRecord$5(agent.tool_results);
|
|
1327
1018
|
if (!toolResults) return false;
|
|
1328
1019
|
return Object.values(toolResults).every(isToolResult);
|
|
1329
1020
|
}
|
|
@@ -1356,13 +1047,16 @@ function hasValidConversationCore(record) {
|
|
|
1356
1047
|
return Array.isArray(record.messages) && record.messages.every(isConversationMessage) && typeof record.updated_at === "string";
|
|
1357
1048
|
}
|
|
1358
1049
|
function parseAcpxState(raw) {
|
|
1359
|
-
const record = asRecord$
|
|
1050
|
+
const record = asRecord$5(raw);
|
|
1360
1051
|
if (!record) return;
|
|
1361
1052
|
const state = {};
|
|
1362
|
-
|
|
1053
|
+
if (record.reset_on_next_ensure === true) state.reset_on_next_ensure = true;
|
|
1363
1054
|
assignStringState(state, "current_mode_id", record.current_mode_id);
|
|
1364
1055
|
assignStringState(state, "desired_mode_id", record.desired_mode_id);
|
|
1365
|
-
|
|
1056
|
+
const desiredConfigOptions = parseStringMap(record.desired_config_options);
|
|
1057
|
+
if (desiredConfigOptions) state.desired_config_options = desiredConfigOptions;
|
|
1058
|
+
const modelNames = parseStringMap(record.available_model_names, true);
|
|
1059
|
+
if (modelNames) state.available_model_names = modelNames;
|
|
1366
1060
|
assignParsedModelState(state, record);
|
|
1367
1061
|
const availableCommands = parseAvailableCommands(record.available_commands);
|
|
1368
1062
|
if (availableCommands) state.available_commands = availableCommands;
|
|
@@ -1377,60 +1071,37 @@ function assignParsedModelState(state, record) {
|
|
|
1377
1071
|
if (configOptions) state.config_options = configOptions;
|
|
1378
1072
|
if (state.model_control === void 0 && state.available_models !== void 0) state.model_control = hasModelConfigOption(state.config_options) ? "config_option" : "legacy_set_model";
|
|
1379
1073
|
}
|
|
1380
|
-
function assignBooleanTrue(state, key, value) {
|
|
1381
|
-
if (value === true) state[key] = true;
|
|
1382
|
-
}
|
|
1383
1074
|
function assignStringState(state, key, value) {
|
|
1384
1075
|
if (typeof value === "string") state[key] = value;
|
|
1385
1076
|
}
|
|
1386
|
-
function
|
|
1387
|
-
const
|
|
1388
|
-
if (!
|
|
1389
|
-
const parsed = Object.fromEntries(Object.entries(
|
|
1390
|
-
|
|
1391
|
-
return typeof value === "string";
|
|
1392
|
-
}));
|
|
1393
|
-
if (Object.keys(parsed).length > 0) state.desired_config_options = parsed;
|
|
1077
|
+
function parseStringMap(raw, preserveEmpty = false) {
|
|
1078
|
+
const record = asRecord$5(raw);
|
|
1079
|
+
if (!record) return;
|
|
1080
|
+
const parsed = Object.fromEntries(Object.entries(record).filter((entry) => typeof entry[1] === "string"));
|
|
1081
|
+
return Object.keys(parsed).length > 0 || preserveEmpty ? parsed : void 0;
|
|
1394
1082
|
}
|
|
1395
1083
|
function assignParsedSessionOptions(state, raw) {
|
|
1396
|
-
const sessionOptions = asRecord$
|
|
1084
|
+
const sessionOptions = asRecord$5(raw);
|
|
1397
1085
|
if (!sessionOptions) return;
|
|
1398
1086
|
const parsedSessionOptions = {};
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1087
|
+
if (typeof sessionOptions.model === "string") parsedSessionOptions.model = sessionOptions.model;
|
|
1088
|
+
if (isStringArray(sessionOptions.allowed_tools)) parsedSessionOptions.allowed_tools = [...sessionOptions.allowed_tools];
|
|
1089
|
+
if (isPositiveInteger(sessionOptions.max_turns)) parsedSessionOptions.max_turns = sessionOptions.max_turns;
|
|
1402
1090
|
assignSessionOptionSystemPrompt(parsedSessionOptions, sessionOptions.system_prompt);
|
|
1403
|
-
|
|
1091
|
+
const env = parseStringMap(sessionOptions.env);
|
|
1092
|
+
if (env) parsedSessionOptions.env = env;
|
|
1404
1093
|
if (Object.keys(parsedSessionOptions).length > 0) state.session_options = parsedSessionOptions;
|
|
1405
1094
|
}
|
|
1406
|
-
function assignSessionOptionModel(options, value) {
|
|
1407
|
-
if (typeof value === "string") options.model = value;
|
|
1408
|
-
}
|
|
1409
|
-
function assignSessionOptionAllowedTools(options, value) {
|
|
1410
|
-
if (isStringArray(value)) options.allowed_tools = [...value];
|
|
1411
|
-
}
|
|
1412
|
-
function assignSessionOptionMaxTurns(options, value) {
|
|
1413
|
-
if (typeof value === "number" && Number.isInteger(value) && value > 0) options.max_turns = value;
|
|
1414
|
-
}
|
|
1415
1095
|
function assignSessionOptionSystemPrompt(options, value) {
|
|
1416
1096
|
if (typeof value === "string" && value.length > 0) {
|
|
1417
1097
|
options.system_prompt = value;
|
|
1418
1098
|
return;
|
|
1419
1099
|
}
|
|
1420
|
-
const appendRecord = asRecord$
|
|
1100
|
+
const appendRecord = asRecord$5(value);
|
|
1421
1101
|
if (appendRecord && typeof appendRecord.append === "string" && appendRecord.append.length > 0) options.system_prompt = { append: appendRecord.append };
|
|
1422
1102
|
}
|
|
1423
|
-
function assignSessionOptionEnv(options, value) {
|
|
1424
|
-
const env = asRecord$4(value);
|
|
1425
|
-
if (!env) return;
|
|
1426
|
-
const parsed = Object.fromEntries(Object.entries(env).filter((entry) => {
|
|
1427
|
-
const [, raw] = entry;
|
|
1428
|
-
return typeof raw === "string";
|
|
1429
|
-
}));
|
|
1430
|
-
if (Object.keys(parsed).length > 0) options.env = parsed;
|
|
1431
|
-
}
|
|
1432
1103
|
function parseEventLog(raw, sessionId) {
|
|
1433
|
-
const record = asRecord$
|
|
1104
|
+
const record = asRecord$5(raw);
|
|
1434
1105
|
if (!record || !hasValidEventLogCore(record)) return defaultSessionEventLog(sessionId);
|
|
1435
1106
|
return {
|
|
1436
1107
|
active_path: record.active_path,
|
|
@@ -1449,7 +1120,7 @@ function isPositiveInteger(value) {
|
|
|
1449
1120
|
}
|
|
1450
1121
|
function parseImportedFrom(raw) {
|
|
1451
1122
|
if (raw == null) return;
|
|
1452
|
-
const record = asRecord$
|
|
1123
|
+
const record = asRecord$5(raw);
|
|
1453
1124
|
if (!record || typeof record.record_id !== "string" || typeof record.cwd_original !== "string" || typeof record.exported_by !== "string" || typeof record.exported_at !== "string") return null;
|
|
1454
1125
|
return {
|
|
1455
1126
|
recordId: record.record_id,
|
|
@@ -1476,8 +1147,7 @@ function normalizeOptionalName(value) {
|
|
|
1476
1147
|
}
|
|
1477
1148
|
function normalizeOptionalPid(value) {
|
|
1478
1149
|
if (value == null) return;
|
|
1479
|
-
|
|
1480
|
-
return value;
|
|
1150
|
+
return isPositiveInteger(value) ? value : null;
|
|
1481
1151
|
}
|
|
1482
1152
|
function normalizeOptionalBoolean(value, fallback = false) {
|
|
1483
1153
|
if (value == null) return fallback;
|
|
@@ -1500,7 +1170,7 @@ function normalizeOptionalSignal(value) {
|
|
|
1500
1170
|
return Symbol("invalid");
|
|
1501
1171
|
}
|
|
1502
1172
|
function parseSessionRecord(raw) {
|
|
1503
|
-
const record = asRecord$
|
|
1173
|
+
const record = asRecord$5(raw);
|
|
1504
1174
|
if (!record) return null;
|
|
1505
1175
|
if (record.schema !== "acpx.session.v1") return null;
|
|
1506
1176
|
const optionals = validSessionOptionals({
|
|
@@ -1525,7 +1195,7 @@ function parseSessionRecord(raw) {
|
|
|
1525
1195
|
schema: SESSION_RECORD_SCHEMA,
|
|
1526
1196
|
acpxRecordId: record.acpx_record_id,
|
|
1527
1197
|
acpSessionId: record.acp_session_id,
|
|
1528
|
-
agentSessionId:
|
|
1198
|
+
agentSessionId: normalizeAgentSessionId(record.agent_session_id),
|
|
1529
1199
|
agentCommand: record.agent_command,
|
|
1530
1200
|
agentArgv: parsePersistedAgentArgv(record),
|
|
1531
1201
|
cwd: record.cwd,
|
|
@@ -1545,7 +1215,7 @@ function parseSessionRecord(raw) {
|
|
|
1545
1215
|
lastAgentExitAt: optionals.lastAgentExitAt,
|
|
1546
1216
|
lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
|
|
1547
1217
|
protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
|
|
1548
|
-
agentCapabilities: asRecord$
|
|
1218
|
+
agentCapabilities: asRecord$5(record.agent_capabilities),
|
|
1549
1219
|
title: conversation.title,
|
|
1550
1220
|
messages: conversation.messages,
|
|
1551
1221
|
updated_at: conversation.updated_at,
|
|
@@ -1669,7 +1339,8 @@ const ZED_TAG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1669
1339
|
const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set([
|
|
1670
1340
|
"request_token_usage",
|
|
1671
1341
|
"messages.Agent.tool_results",
|
|
1672
|
-
"acpx.session_options.env"
|
|
1342
|
+
"acpx.session_options.env",
|
|
1343
|
+
"acpx.available_model_names"
|
|
1673
1344
|
]);
|
|
1674
1345
|
const OPAQUE_VALUE_PATHS = /* @__PURE__ */ new Set([
|
|
1675
1346
|
"agent_capabilities",
|
|
@@ -1683,10 +1354,6 @@ function isRecord(value) {
|
|
|
1683
1354
|
function joinPath(path) {
|
|
1684
1355
|
return path.join(".");
|
|
1685
1356
|
}
|
|
1686
|
-
function isAllowedKey(path, key) {
|
|
1687
|
-
if (ZED_TAG_KEYS.has(key)) return true;
|
|
1688
|
-
return false;
|
|
1689
|
-
}
|
|
1690
1357
|
function shouldSkipKeyRule(path) {
|
|
1691
1358
|
return MAP_OBJECT_PATHS.has(joinPath(path));
|
|
1692
1359
|
}
|
|
@@ -1712,7 +1379,7 @@ function collectViolations(value, path, violations) {
|
|
|
1712
1379
|
for (const [key, child] of Object.entries(value)) collectKeyViolation(child, key, path, skipKeyRule, violations);
|
|
1713
1380
|
}
|
|
1714
1381
|
function collectKeyViolation(child, key, path, skipKeyRule, violations) {
|
|
1715
|
-
if (!skipKeyRule && !SNAKE_CASE_KEY.test(key) && !
|
|
1382
|
+
if (!skipKeyRule && !SNAKE_CASE_KEY.test(key) && !ZED_TAG_KEYS.has(key)) violations.push(`${joinPath(path)}.${key}`.replace(/^\./, ""));
|
|
1716
1383
|
const childPath = [...path, key];
|
|
1717
1384
|
if (!shouldSkipDescend(childPath)) collectViolations(child, childPath, violations);
|
|
1718
1385
|
}
|
|
@@ -1727,20 +1394,14 @@ function assertPersistedKeyPolicy(value) {
|
|
|
1727
1394
|
throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
|
|
1728
1395
|
}
|
|
1729
1396
|
//#endregion
|
|
1730
|
-
//#region src/session/persistence/atomic-write.ts
|
|
1731
|
-
function createAtomicWriteTempPath(filePath, createUniqueId = randomUUID) {
|
|
1732
|
-
const tempName = `.acpx-write.${process.pid}.${Date.now()}.${createUniqueId()}.tmp`;
|
|
1733
|
-
return path.join(path.dirname(filePath), tempName);
|
|
1734
|
-
}
|
|
1735
|
-
//#endregion
|
|
1736
1397
|
//#region src/session/persistence/index.ts
|
|
1737
1398
|
const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
|
|
1738
|
-
function asRecord$
|
|
1399
|
+
function asRecord$4(value) {
|
|
1739
1400
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1740
1401
|
return value;
|
|
1741
1402
|
}
|
|
1742
1403
|
function parseIndexEntry(raw) {
|
|
1743
|
-
const record = asRecord$
|
|
1404
|
+
const record = asRecord$4(raw);
|
|
1744
1405
|
if (!record) return;
|
|
1745
1406
|
if (!hasRequiredIndexEntryFields(record)) return;
|
|
1746
1407
|
if (record.name !== void 0 && typeof record.name !== "string") return;
|
|
@@ -1784,7 +1445,7 @@ async function readSessionIndex(sessionDir) {
|
|
|
1784
1445
|
const filePath = sessionIndexPath(sessionDir);
|
|
1785
1446
|
try {
|
|
1786
1447
|
const payload = await fs$1.readFile(filePath, "utf8");
|
|
1787
|
-
const record = asRecord$
|
|
1448
|
+
const record = asRecord$4(JSON.parse(payload));
|
|
1788
1449
|
if (!record || record.schema !== SESSION_INDEX_SCHEMA || !Array.isArray(record.files)) return;
|
|
1789
1450
|
const files = record.files.filter((entry) => typeof entry === "string");
|
|
1790
1451
|
if (files.length !== record.files.length || !Array.isArray(record.entries)) return;
|
|
@@ -1800,18 +1461,14 @@ async function readSessionIndex(sessionDir) {
|
|
|
1800
1461
|
}
|
|
1801
1462
|
}
|
|
1802
1463
|
async function writeSessionIndex(sessionDir, index) {
|
|
1803
|
-
|
|
1804
|
-
const tempFile = createAtomicWriteTempPath(filePath);
|
|
1805
|
-
const payload = JSON.stringify({
|
|
1464
|
+
await writePrivateJsonFile(sessionIndexPath(sessionDir), {
|
|
1806
1465
|
schema: SESSION_INDEX_SCHEMA,
|
|
1807
|
-
files:
|
|
1808
|
-
entries:
|
|
1809
|
-
}
|
|
1810
|
-
await fs$1.writeFile(tempFile, `${payload}\n`, "utf8");
|
|
1811
|
-
await fs$1.rename(tempFile, filePath);
|
|
1466
|
+
files: index.files.toSorted(),
|
|
1467
|
+
entries: index.entries.toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt))
|
|
1468
|
+
});
|
|
1812
1469
|
}
|
|
1813
1470
|
async function rebuildSessionIndex(sessionDir) {
|
|
1814
|
-
const files =
|
|
1471
|
+
const files = await listSessionFiles(sessionDir);
|
|
1815
1472
|
const indexEntries = [];
|
|
1816
1473
|
for (const file of files) try {
|
|
1817
1474
|
const payload = await fs$1.readFile(path.join(sessionDir, file), "utf8");
|
|
@@ -1828,23 +1485,26 @@ async function rebuildSessionIndex(sessionDir) {
|
|
|
1828
1485
|
return index;
|
|
1829
1486
|
}
|
|
1830
1487
|
async function loadOrRebuildSessionIndex(sessionDir) {
|
|
1831
|
-
const files =
|
|
1488
|
+
const files = await listSessionFiles(sessionDir);
|
|
1832
1489
|
const existing = await readSessionIndex(sessionDir);
|
|
1833
1490
|
if (existing && existing.files.length === files.length && existing.files.every((file, index) => file === files[index])) return existing;
|
|
1834
1491
|
return await rebuildSessionIndex(sessionDir);
|
|
1835
1492
|
}
|
|
1493
|
+
async function listSessionFiles(sessionDir) {
|
|
1494
|
+
return (await fs$1.readdir(sessionDir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && entry.name !== "index.json").map((entry) => entry.name).toSorted();
|
|
1495
|
+
}
|
|
1836
1496
|
//#endregion
|
|
1837
1497
|
//#region src/session/persistence/repository.ts
|
|
1838
1498
|
const DEFAULT_HISTORY_LIMIT = 20;
|
|
1839
1499
|
function sessionFilePath(acpxRecordId) {
|
|
1840
|
-
const safeId =
|
|
1500
|
+
const safeId = safeSessionId(acpxRecordId);
|
|
1841
1501
|
return path.join(sessionBaseDir(), `${safeId}.json`);
|
|
1842
1502
|
}
|
|
1843
|
-
function sessionBaseDir() {
|
|
1844
|
-
return path.join(os.homedir(), ".acpx", "sessions");
|
|
1845
|
-
}
|
|
1846
1503
|
async function ensureSessionDir() {
|
|
1847
|
-
await fs$1.mkdir(sessionBaseDir(), {
|
|
1504
|
+
await fs$1.mkdir(sessionBaseDir(), {
|
|
1505
|
+
recursive: true,
|
|
1506
|
+
mode: 448
|
|
1507
|
+
});
|
|
1848
1508
|
}
|
|
1849
1509
|
async function loadRecordFromIndexEntry(entry) {
|
|
1850
1510
|
try {
|
|
@@ -1868,14 +1528,10 @@ function matchesSessionEntry(session, normalizedCwd, normalizedName, includeClos
|
|
|
1868
1528
|
}
|
|
1869
1529
|
async function writeSessionRecord(record) {
|
|
1870
1530
|
await measurePerf("session.write_record", async () => {
|
|
1871
|
-
await ensureSessionDir();
|
|
1872
1531
|
const persisted = serializeSessionRecordForDisk(record);
|
|
1873
1532
|
assertPersistedKeyPolicy(persisted);
|
|
1874
1533
|
const file = sessionFilePath(record.acpxRecordId);
|
|
1875
|
-
|
|
1876
|
-
const payload = JSON.stringify(persisted, null, 2);
|
|
1877
|
-
await fs$1.writeFile(tempFile, `${payload}\n`, "utf8");
|
|
1878
|
-
await fs$1.rename(tempFile, file);
|
|
1534
|
+
await writePrivateJsonFile(file, persisted);
|
|
1879
1535
|
const sessionDir = sessionBaseDir();
|
|
1880
1536
|
const index = await loadOrRebuildSessionIndex(sessionDir);
|
|
1881
1537
|
const fileName = path.basename(file);
|
|
@@ -1940,7 +1596,7 @@ function normalizeName(value) {
|
|
|
1940
1596
|
const trimmed = value.trim();
|
|
1941
1597
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
1942
1598
|
}
|
|
1943
|
-
function isoNow$
|
|
1599
|
+
function isoNow$1() {
|
|
1944
1600
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1945
1601
|
}
|
|
1946
1602
|
async function listSessions() {
|
|
@@ -2031,7 +1687,7 @@ function isBeforeCutoff(record, cutoffIso) {
|
|
|
2031
1687
|
return !cutoffIso || closedAtOrLastUsedAt(record) < cutoffIso;
|
|
2032
1688
|
}
|
|
2033
1689
|
async function pruneSessionFiles(record, sessionDir, dirEntries, includeHistory) {
|
|
2034
|
-
const safeId =
|
|
1690
|
+
const safeId = safeSessionId(record.acpxRecordId);
|
|
2035
1691
|
let bytesFreed = await unlinkCountingBytes(path.join(sessionDir, `${safeId}.json`));
|
|
2036
1692
|
if (includeHistory) for (const name of dirEntries.filter((entry) => isSessionStreamFile(entry, safeId))) bytesFreed += await unlinkCountingBytes(path.join(sessionDir, name));
|
|
2037
1693
|
return bytesFreed;
|
|
@@ -2068,10 +1724,6 @@ const WRITE_PREVIEW_MAX_CHARS = 1200;
|
|
|
2068
1724
|
function nowIso$1() {
|
|
2069
1725
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2070
1726
|
}
|
|
2071
|
-
function isWithinRoot(rootDir, targetPath) {
|
|
2072
|
-
const relative = path.relative(rootDir, targetPath);
|
|
2073
|
-
return relative.length === 0 || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
2074
|
-
}
|
|
2075
1727
|
function toWritePreview(content) {
|
|
2076
1728
|
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
|
2077
1729
|
const visibleLines = lines.slice(0, WRITE_PREVIEW_MAX_LINES);
|
|
@@ -2092,6 +1744,7 @@ function canPromptForPermission$2() {
|
|
|
2092
1744
|
}
|
|
2093
1745
|
var FileSystemHandlers = class {
|
|
2094
1746
|
rootDir;
|
|
1747
|
+
workspace;
|
|
2095
1748
|
permissionMode;
|
|
2096
1749
|
nonInteractivePermissions;
|
|
2097
1750
|
onOperation;
|
|
@@ -2121,7 +1774,7 @@ var FileSystemHandlers = class {
|
|
|
2121
1774
|
});
|
|
2122
1775
|
try {
|
|
2123
1776
|
if (this.permissionMode === "deny-all") throw new PermissionDeniedError("Permission denied for fs/read_text_file (--deny-all)");
|
|
2124
|
-
const content = await
|
|
1777
|
+
const content = await (await this.getWorkspace()).readText(`.${path.sep}${path.relative(this.rootDir, filePath)}`);
|
|
2125
1778
|
const sliced = this.sliceContent(content, params.line, params.limit);
|
|
2126
1779
|
this.emitOperation({
|
|
2127
1780
|
method: "fs/read_text_file",
|
|
@@ -2156,8 +1809,12 @@ var FileSystemHandlers = class {
|
|
|
2156
1809
|
});
|
|
2157
1810
|
try {
|
|
2158
1811
|
if (!await this.isWriteApproved(filePath, preview)) throw new PermissionDeniedError("Permission denied for fs/write_text_file");
|
|
2159
|
-
await
|
|
2160
|
-
|
|
1812
|
+
const file = await (await this.getWorkspace()).openWritable(`.${path.sep}${path.relative(this.rootDir, filePath)}`, { mode: 438 });
|
|
1813
|
+
try {
|
|
1814
|
+
await file.handle.writeFile(params.content, "utf8");
|
|
1815
|
+
} finally {
|
|
1816
|
+
await file.handle.close();
|
|
1817
|
+
}
|
|
2161
1818
|
this.emitOperation({
|
|
2162
1819
|
method: "fs/write_text_file",
|
|
2163
1820
|
status: "completed",
|
|
@@ -2187,9 +1844,16 @@ var FileSystemHandlers = class {
|
|
|
2187
1844
|
resolvePathWithinRoot(rawPath) {
|
|
2188
1845
|
if (!path.isAbsolute(rawPath)) throw new Error(`Path must be absolute: ${rawPath}`);
|
|
2189
1846
|
const resolved = path.resolve(rawPath);
|
|
2190
|
-
if (!
|
|
1847
|
+
if (!isPathInside(this.rootDir, resolved)) throw new Error(`Path is outside allowed cwd subtree: ${resolved}`);
|
|
2191
1848
|
return resolved;
|
|
2192
1849
|
}
|
|
1850
|
+
getWorkspace() {
|
|
1851
|
+
return this.workspace ??= root(this.rootDir, {
|
|
1852
|
+
symlinks: "follow-within-root",
|
|
1853
|
+
hardlinks: "allow",
|
|
1854
|
+
maxBytes: Infinity
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
2193
1857
|
sliceContent(content, line, limit) {
|
|
2194
1858
|
if (line == null && limit == null) return content;
|
|
2195
1859
|
const lines = content.split("\n");
|
|
@@ -2223,14 +1887,14 @@ function selected(optionId) {
|
|
|
2223
1887
|
function cancelled() {
|
|
2224
1888
|
return { outcome: { outcome: "cancelled" } };
|
|
2225
1889
|
}
|
|
2226
|
-
function
|
|
1890
|
+
function withPermissionMetadata(response, metadata) {
|
|
2227
1891
|
return {
|
|
2228
1892
|
...response,
|
|
2229
1893
|
_meta: {
|
|
2230
1894
|
...response._meta,
|
|
2231
1895
|
acpx: {
|
|
2232
1896
|
...response._meta?.acpx && typeof response._meta.acpx === "object" && !Array.isArray(response._meta.acpx) ? response._meta.acpx : {},
|
|
2233
|
-
|
|
1897
|
+
...metadata
|
|
2234
1898
|
}
|
|
2235
1899
|
}
|
|
2236
1900
|
};
|
|
@@ -2405,7 +2069,7 @@ async function resolveEscalatingPermissionRequest(params, policyMatch, allowOpti
|
|
|
2405
2069
|
if (canPromptForPermission$1()) return resolveInteractivePromptResult(params, allowOption, rejectOption);
|
|
2406
2070
|
const escalation = buildEscalationEvent(params, policyMatch.matchedRule);
|
|
2407
2071
|
return {
|
|
2408
|
-
response:
|
|
2072
|
+
response: withPermissionMetadata(rejectOption ? selected(rejectOption.optionId) : cancelled(), { permissionEscalation: escalation }),
|
|
2409
2073
|
escalation
|
|
2410
2074
|
};
|
|
2411
2075
|
}
|
|
@@ -2467,419 +2131,180 @@ function classifyPermissionDecision(params, response) {
|
|
|
2467
2131
|
return "denied";
|
|
2468
2132
|
}
|
|
2469
2133
|
//#endregion
|
|
2470
|
-
//#region src/
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
function windowsExecutableExtensions(env) {
|
|
2476
|
-
return (readWindowsEnvValue(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0);
|
|
2477
|
-
}
|
|
2478
|
-
function commandCandidates(command, env) {
|
|
2479
|
-
if (path.extname(command).length > 0) return [command];
|
|
2480
|
-
return windowsExecutableExtensions(env).map((extension) => `${command}${extension}`);
|
|
2481
|
-
}
|
|
2482
|
-
function commandHasPath(command) {
|
|
2483
|
-
return command.includes("/") || command.includes("\\") || path.isAbsolute(command);
|
|
2484
|
-
}
|
|
2485
|
-
function resolveWindowsPathCommand(command, env) {
|
|
2486
|
-
const candidates = commandCandidates(command, env);
|
|
2487
|
-
const pathValue = readWindowsEnvValue(env, "PATH");
|
|
2488
|
-
if (!pathValue) return;
|
|
2489
|
-
for (const directory of pathValue.split(";")) {
|
|
2490
|
-
const resolved = findExistingCommandInDirectory(directory, candidates);
|
|
2491
|
-
if (resolved) return resolved;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
function findExistingCommandInDirectory(directory, candidates) {
|
|
2495
|
-
const trimmedDirectory = directory.trim();
|
|
2496
|
-
if (trimmedDirectory.length === 0) return;
|
|
2497
|
-
return candidates.map((candidate) => path.join(trimmedDirectory, candidate)).find((resolved) => fs.existsSync(resolved));
|
|
2498
|
-
}
|
|
2499
|
-
function resolveWindowsWrapperToken(token, wrapperPath) {
|
|
2500
|
-
const relative = token.match(/%~?dp0%?\s*[\\/]*(.*)$/i)?.[1]?.trim();
|
|
2501
|
-
if (!relative) return;
|
|
2502
|
-
const candidate = path.resolve(path.dirname(wrapperPath), relative.replace(/[\\/]+/g, path.sep).replace(/^[\\/]+/, ""));
|
|
2503
|
-
return path.extname(candidate).toLowerCase() === ".exe" && fs.existsSync(candidate) ? candidate : void 0;
|
|
2504
|
-
}
|
|
2505
|
-
function resolveWindowsWrapperExecutable(wrapperPath) {
|
|
2506
|
-
if (!fs.existsSync(wrapperPath)) return;
|
|
2507
|
-
try {
|
|
2508
|
-
return [...fs.readFileSync(wrapperPath, "utf8").matchAll(/"([^"\r\n]*)"/g)].map((match) => resolveWindowsWrapperToken(match[1] ?? "", wrapperPath)).find((candidate) => candidate !== void 0);
|
|
2509
|
-
} catch {
|
|
2510
|
-
return;
|
|
2134
|
+
//#region src/prompt-content.ts
|
|
2135
|
+
var PromptInputValidationError = class extends Error {
|
|
2136
|
+
constructor(message) {
|
|
2137
|
+
super(message);
|
|
2138
|
+
this.name = "PromptInputValidationError";
|
|
2511
2139
|
}
|
|
2140
|
+
};
|
|
2141
|
+
function asRecord$3(value) {
|
|
2142
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2143
|
+
return value;
|
|
2512
2144
|
}
|
|
2513
|
-
function
|
|
2514
|
-
|
|
2515
|
-
if (commandHasPath(command)) return candidates.find((candidate) => fs.existsSync(candidate));
|
|
2516
|
-
return resolveWindowsPathCommand(command, env);
|
|
2517
|
-
}
|
|
2518
|
-
/**
|
|
2519
|
-
* Resolve a Windows command to a native executable suitable for direct spawn.
|
|
2520
|
-
*
|
|
2521
|
-
* Batch and PowerShell shims are intentionally rejected unless they point at a
|
|
2522
|
-
* real `.exe` entrypoint. Callers that need shell execution should use the
|
|
2523
|
-
* command-specific shell policy instead.
|
|
2524
|
-
*/
|
|
2525
|
-
function resolveWindowsExecutablePath(command, env = process.env) {
|
|
2526
|
-
const resolved = resolveWindowsCommand(command, env);
|
|
2527
|
-
if (!resolved) return;
|
|
2528
|
-
const absolute = path.resolve(resolved);
|
|
2529
|
-
const extension = path.extname(absolute).toLowerCase();
|
|
2530
|
-
if (extension === ".exe") return absolute;
|
|
2531
|
-
if (extension !== ".cmd" && extension !== ".bat" && extension !== ".ps1") return;
|
|
2532
|
-
const siblingExecutable = `${absolute.slice(0, -extension.length)}.exe`;
|
|
2533
|
-
return fs.existsSync(siblingExecutable) ? siblingExecutable : resolveWindowsWrapperExecutable(absolute);
|
|
2534
|
-
}
|
|
2535
|
-
function shouldUseWindowsBatchShell(command, platform = process.platform, env = process.env) {
|
|
2536
|
-
if (platform !== "win32") return false;
|
|
2537
|
-
const resolvedCommand = resolveWindowsCommand(command, env) ?? command;
|
|
2538
|
-
const ext = path.extname(resolvedCommand).toLowerCase();
|
|
2539
|
-
return ext === ".cmd" || ext === ".bat";
|
|
2540
|
-
}
|
|
2541
|
-
const CMD_META_CHAR_RE = /([()\][%!^"`<>&|;, *?])/gu;
|
|
2542
|
-
const CMD_BACKSLASH_QUOTE_RE = /(?=(\\+?)?)\1"/gu;
|
|
2543
|
-
const CMD_TRAILING_BACKSLASH_RE = /(?=(\\+?)?)\1$/gu;
|
|
2544
|
-
const CMD_SHIM_RE = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/iu;
|
|
2545
|
-
function escapeCmdCommand(value) {
|
|
2546
|
-
return value.replace(CMD_META_CHAR_RE, "^$1");
|
|
2547
|
-
}
|
|
2548
|
-
function escapeCmdArgument(value, doubleEscapeMeta) {
|
|
2549
|
-
const escaped = `"${value.replace(CMD_BACKSLASH_QUOTE_RE, "$1$1\\\"").replace(CMD_TRAILING_BACKSLASH_RE, "$1$1")}"`.replace(CMD_META_CHAR_RE, "^$1");
|
|
2550
|
-
return doubleEscapeMeta ? escaped.replace(CMD_META_CHAR_RE, "^$1") : escaped;
|
|
2551
|
-
}
|
|
2552
|
-
function buildAgentSpawnCommand(command, args, platform = process.platform, env = process.env) {
|
|
2553
|
-
if (!shouldUseWindowsBatchShell(command, platform, env)) return {
|
|
2554
|
-
command,
|
|
2555
|
-
args: [...args]
|
|
2556
|
-
};
|
|
2557
|
-
const resolvedCommand = path.win32.normalize(resolveWindowsCommand(command, env) ?? command);
|
|
2558
|
-
const doubleEscapeMeta = CMD_SHIM_RE.test(resolvedCommand);
|
|
2559
|
-
const shellCommand = [escapeCmdCommand(resolvedCommand), ...args.map((arg) => escapeCmdArgument(arg, doubleEscapeMeta))].join(" ");
|
|
2560
|
-
return {
|
|
2561
|
-
command: readWindowsEnvValue(env, "COMSPEC") ?? "cmd.exe",
|
|
2562
|
-
args: [
|
|
2563
|
-
"/d",
|
|
2564
|
-
"/s",
|
|
2565
|
-
"/c",
|
|
2566
|
-
`"${shellCommand}"`
|
|
2567
|
-
],
|
|
2568
|
-
windowsVerbatimArguments: true
|
|
2569
|
-
};
|
|
2570
|
-
}
|
|
2571
|
-
function buildSpawnCommandOptions(command, options, platform = process.platform, env = process.env) {
|
|
2572
|
-
if (!shouldUseWindowsBatchShell(command, platform, env)) return options;
|
|
2573
|
-
return {
|
|
2574
|
-
...options,
|
|
2575
|
-
shell: true
|
|
2576
|
-
};
|
|
2577
|
-
}
|
|
2578
|
-
function buildTerminalSpawnCommand(command, args) {
|
|
2579
|
-
return {
|
|
2580
|
-
command,
|
|
2581
|
-
args: args ?? [],
|
|
2582
|
-
killProcessGroup: false
|
|
2583
|
-
};
|
|
2145
|
+
function isNonEmptyString(value) {
|
|
2146
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2584
2147
|
}
|
|
2585
|
-
function
|
|
2586
|
-
if (
|
|
2587
|
-
|
|
2588
|
-
args: [
|
|
2589
|
-
"/d",
|
|
2590
|
-
"/s",
|
|
2591
|
-
"/c",
|
|
2592
|
-
command
|
|
2593
|
-
],
|
|
2594
|
-
killProcessGroup: true
|
|
2595
|
-
};
|
|
2596
|
-
return {
|
|
2597
|
-
command: "/bin/sh",
|
|
2598
|
-
args: ["-c", command],
|
|
2599
|
-
killProcessGroup: true
|
|
2600
|
-
};
|
|
2148
|
+
function isBase64Data(value) {
|
|
2149
|
+
if (value.length === 0 || value.length % 4 !== 0) return false;
|
|
2150
|
+
return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
|
|
2601
2151
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2605
|
-
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
2606
|
-
let cachedVersion = null;
|
|
2607
|
-
function parseVersion(value) {
|
|
2608
|
-
if (typeof value !== "string") return null;
|
|
2609
|
-
const trimmed = value.trim();
|
|
2610
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
2152
|
+
function isImageMimeType(value) {
|
|
2153
|
+
return /^image\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
2611
2154
|
}
|
|
2612
|
-
function
|
|
2613
|
-
|
|
2614
|
-
return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
|
|
2615
|
-
} catch {
|
|
2616
|
-
return null;
|
|
2617
|
-
}
|
|
2155
|
+
function isAudioMimeType(value) {
|
|
2156
|
+
return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
2618
2157
|
}
|
|
2619
|
-
function
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
const packageVersion = readPackageVersion(path.join(current, "package.json"));
|
|
2623
|
-
if (packageVersion) return packageVersion;
|
|
2624
|
-
const parent = path.dirname(current);
|
|
2625
|
-
if (parent === current) return null;
|
|
2626
|
-
current = parent;
|
|
2627
|
-
}
|
|
2158
|
+
function isTextBlock(value) {
|
|
2159
|
+
const record = asRecord$3(value);
|
|
2160
|
+
return record?.type === "text" && typeof record.text === "string";
|
|
2628
2161
|
}
|
|
2629
|
-
function
|
|
2630
|
-
const
|
|
2631
|
-
|
|
2632
|
-
if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2633
|
-
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
2162
|
+
function isImageBlock(value) {
|
|
2163
|
+
const record = asRecord$3(value);
|
|
2164
|
+
return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
2634
2165
|
}
|
|
2635
|
-
function
|
|
2636
|
-
const
|
|
2637
|
-
|
|
2638
|
-
return envPackageName === "acpx" ? envVersion : null;
|
|
2166
|
+
function isAudioBlock(value) {
|
|
2167
|
+
const record = asRecord$3(value);
|
|
2168
|
+
return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
2639
2169
|
}
|
|
2640
|
-
function
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
return cachedVersion;
|
|
2170
|
+
function isResourceLinkBlock(value) {
|
|
2171
|
+
const record = asRecord$3(value);
|
|
2172
|
+
return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title === void 0 || typeof record.title === "string") && (record.name === void 0 || typeof record.name === "string");
|
|
2644
2173
|
}
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
const timeoutMs = Math.max(1, Math.round(options.timeoutMs ?? 8e3));
|
|
2650
|
-
return await new Promise((resolve, reject) => {
|
|
2651
|
-
let settled = false;
|
|
2652
|
-
let timer;
|
|
2653
|
-
const child = execFile(command, [...args], {
|
|
2654
|
-
encoding: "utf8",
|
|
2655
|
-
maxBuffer: options.maxBufferBytes ?? 33554432,
|
|
2656
|
-
killSignal: "SIGKILL",
|
|
2657
|
-
windowsHide: options.windowsHide
|
|
2658
|
-
}, (error, stdout) => {
|
|
2659
|
-
if (settled) return;
|
|
2660
|
-
settled = true;
|
|
2661
|
-
if (timer) clearTimeout(timer);
|
|
2662
|
-
if (error) {
|
|
2663
|
-
reject(error);
|
|
2664
|
-
return;
|
|
2665
|
-
}
|
|
2666
|
-
resolve(stdout);
|
|
2667
|
-
});
|
|
2668
|
-
timer = setTimeout(() => {
|
|
2669
|
-
if (settled) return;
|
|
2670
|
-
settled = true;
|
|
2671
|
-
const killed = child.kill("SIGKILL");
|
|
2672
|
-
child.stdout?.destroy();
|
|
2673
|
-
child.stderr?.destroy();
|
|
2674
|
-
child.unref();
|
|
2675
|
-
reject(Object.assign(/* @__PURE__ */ new Error(`${command} timed out after ${timeoutMs}ms`), {
|
|
2676
|
-
code: "ETIMEDOUT",
|
|
2677
|
-
killed,
|
|
2678
|
-
signal: "SIGKILL"
|
|
2679
|
-
}));
|
|
2680
|
-
}, timeoutMs);
|
|
2681
|
-
});
|
|
2174
|
+
function isResourcePayload(value) {
|
|
2175
|
+
const record = asRecord$3(value);
|
|
2176
|
+
if (!record || !isNonEmptyString(record.uri)) return false;
|
|
2177
|
+
return record.text === void 0 || typeof record.text === "string";
|
|
2682
2178
|
}
|
|
2683
|
-
function
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
const argv = [parts.command, ...parts.args];
|
|
2687
|
-
return {
|
|
2688
|
-
agentCommand: renderArgvIdentity(argv),
|
|
2689
|
-
agentArgv: argv
|
|
2690
|
-
};
|
|
2179
|
+
function isResourceBlock(value) {
|
|
2180
|
+
const record = asRecord$3(value);
|
|
2181
|
+
return record?.type === "resource" && isResourcePayload(record.resource);
|
|
2691
2182
|
}
|
|
2692
|
-
const
|
|
2693
|
-
|
|
2694
|
-
|
|
2183
|
+
const CONTENT_BLOCK_VALIDATORS = [
|
|
2184
|
+
isTextBlock,
|
|
2185
|
+
isImageBlock,
|
|
2186
|
+
isAudioBlock,
|
|
2187
|
+
isResourceLinkBlock,
|
|
2188
|
+
isResourceBlock
|
|
2189
|
+
];
|
|
2190
|
+
function isContentBlock(value) {
|
|
2191
|
+
return CONTENT_BLOCK_VALIDATORS.some((validator) => validator(value));
|
|
2695
2192
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2193
|
+
const CONTENT_BLOCK_ERROR_VALIDATORS = {
|
|
2194
|
+
text: validateTextContentBlock,
|
|
2195
|
+
image: validateImageContentBlock,
|
|
2196
|
+
audio: validateAudioContentBlock,
|
|
2197
|
+
resource_link: validateResourceLinkContentBlock,
|
|
2198
|
+
resource: validateResourceContentBlock
|
|
2199
|
+
};
|
|
2200
|
+
function contentBlockErrorValidator(type) {
|
|
2201
|
+
return Object.hasOwn(CONTENT_BLOCK_ERROR_VALIDATORS, type) ? CONTENT_BLOCK_ERROR_VALIDATORS[type] : void 0;
|
|
2698
2202
|
}
|
|
2699
|
-
function
|
|
2700
|
-
return
|
|
2701
|
-
const onSpawn = () => {
|
|
2702
|
-
child.off("error", onError);
|
|
2703
|
-
resolve();
|
|
2704
|
-
};
|
|
2705
|
-
const onError = (error) => {
|
|
2706
|
-
child.off("spawn", onSpawn);
|
|
2707
|
-
reject(error);
|
|
2708
|
-
};
|
|
2709
|
-
child.once("spawn", onSpawn);
|
|
2710
|
-
child.once("error", onError);
|
|
2711
|
-
});
|
|
2203
|
+
function validateTextContentBlock(record, index) {
|
|
2204
|
+
return typeof record.text === "string" ? void 0 : `prompt[${index}] text block must include a string text field`;
|
|
2712
2205
|
}
|
|
2713
|
-
function
|
|
2714
|
-
|
|
2206
|
+
function validateImageContentBlock(record, index) {
|
|
2207
|
+
if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] image block must include a non-empty mimeType`;
|
|
2208
|
+
if (!isImageMimeType(record.mimeType)) return `prompt[${index}] image block mimeType must start with image/`;
|
|
2209
|
+
if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] image block must include non-empty base64 data`;
|
|
2210
|
+
return isBase64Data(record.data) ? void 0 : `prompt[${index}] image block data must be valid base64`;
|
|
2715
2211
|
}
|
|
2716
|
-
function
|
|
2717
|
-
if (!
|
|
2718
|
-
return
|
|
2212
|
+
function validateAudioContentBlock(record, index) {
|
|
2213
|
+
if (!isNonEmptyString(record.mimeType)) return `prompt[${index}] audio block must include a non-empty mimeType`;
|
|
2214
|
+
if (!isAudioMimeType(record.mimeType)) return `prompt[${index}] audio block mimeType must start with audio/`;
|
|
2215
|
+
if (typeof record.data !== "string" || record.data.length === 0) return `prompt[${index}] audio block must include non-empty base64 data`;
|
|
2216
|
+
return isBase64Data(record.data) ? void 0 : `prompt[${index}] audio block data must be valid base64`;
|
|
2719
2217
|
}
|
|
2720
|
-
function
|
|
2721
|
-
if (!
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
const timer = setTimeout(() => {
|
|
2725
|
-
finish(false);
|
|
2726
|
-
}, Math.max(0, timeoutMs));
|
|
2727
|
-
const finish = (value) => {
|
|
2728
|
-
if (settled) return;
|
|
2729
|
-
settled = true;
|
|
2730
|
-
child.off("close", onExitLike);
|
|
2731
|
-
child.off("exit", onExitLike);
|
|
2732
|
-
clearTimeout(timer);
|
|
2733
|
-
resolve(value);
|
|
2734
|
-
};
|
|
2735
|
-
const onExitLike = () => {
|
|
2736
|
-
finish(true);
|
|
2737
|
-
};
|
|
2738
|
-
child.once("close", onExitLike);
|
|
2739
|
-
child.once("exit", onExitLike);
|
|
2740
|
-
});
|
|
2218
|
+
function validateResourceLinkContentBlock(record, index) {
|
|
2219
|
+
if (!isNonEmptyString(record.uri)) return `prompt[${index}] resource_link block must include a non-empty uri`;
|
|
2220
|
+
if (record.title !== void 0 && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string when present`;
|
|
2221
|
+
if (record.name !== void 0 && typeof record.name !== "string") return `prompt[${index}] resource_link block name must be a string when present`;
|
|
2741
2222
|
}
|
|
2742
|
-
function
|
|
2743
|
-
if (
|
|
2744
|
-
|
|
2745
|
-
assertWindowsLaunchableCommand(parts.command, platform);
|
|
2746
|
-
return parts;
|
|
2747
|
-
}
|
|
2748
|
-
if (platform === "win32") throw new Error("Raw agent command strings are not supported on Windows. Configure the agent with an argv array, for example: \"argv\": [\"agent.exe\", \"--acp\"]. Legacy agents.<name>.args arrays are migrated automatically. Existing sessions without saved argv must be closed and recreated.");
|
|
2749
|
-
return splitCommandLine(value);
|
|
2750
|
-
}
|
|
2751
|
-
function assertWindowsLaunchableCommand(command, platform) {
|
|
2752
|
-
if (platform === "win32" && path.extname(command).toLowerCase() === ".sh") throw new Error(`Windows cannot launch shell script executable "${command}" directly. Configure an explicit interpreter argv, for example: "argv": ["bash", "${command}"]. acpx does not infer interpreters.`);
|
|
2753
|
-
}
|
|
2754
|
-
function splitCommandLine(value) {
|
|
2755
|
-
const parts = [];
|
|
2756
|
-
let current = "";
|
|
2757
|
-
let quote = null;
|
|
2758
|
-
let escaping = false;
|
|
2759
|
-
let hasPart = false;
|
|
2760
|
-
for (const ch of value) {
|
|
2761
|
-
const next = readCommandLineChar({
|
|
2762
|
-
ch,
|
|
2763
|
-
current,
|
|
2764
|
-
quote,
|
|
2765
|
-
escaping,
|
|
2766
|
-
parts,
|
|
2767
|
-
hasPart
|
|
2768
|
-
});
|
|
2769
|
-
current = next.current;
|
|
2770
|
-
quote = next.quote;
|
|
2771
|
-
escaping = next.escaping;
|
|
2772
|
-
hasPart = next.hasPart;
|
|
2773
|
-
}
|
|
2774
|
-
if (escaping) {
|
|
2775
|
-
current += "\\";
|
|
2776
|
-
hasPart = true;
|
|
2777
|
-
}
|
|
2778
|
-
if (quote) throw new Error("Invalid --agent command: unterminated quote");
|
|
2779
|
-
if (hasPart) parts.push(current);
|
|
2780
|
-
return toCommandParts(parts);
|
|
2223
|
+
function validateResourceContentBlock(record, index) {
|
|
2224
|
+
if (!asRecord$3(record.resource)) return `prompt[${index}] resource block must include a resource object`;
|
|
2225
|
+
return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and optional text`;
|
|
2781
2226
|
}
|
|
2782
|
-
function
|
|
2783
|
-
|
|
2784
|
-
return {
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
};
|
|
2227
|
+
function getContentBlockValidationError(value, index) {
|
|
2228
|
+
const record = asRecord$3(value);
|
|
2229
|
+
if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
|
|
2230
|
+
const validator = contentBlockErrorValidator(record.type);
|
|
2231
|
+
return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
|
|
2788
2232
|
}
|
|
2789
|
-
function
|
|
2790
|
-
|
|
2791
|
-
current: state.current + state.ch,
|
|
2792
|
-
quote: state.quote,
|
|
2793
|
-
escaping: false,
|
|
2794
|
-
hasPart: true
|
|
2795
|
-
};
|
|
2796
|
-
if (state.ch === "\\" && state.quote !== "'") return {
|
|
2797
|
-
current: state.current,
|
|
2798
|
-
quote: state.quote,
|
|
2799
|
-
escaping: true,
|
|
2800
|
-
hasPart: state.hasPart
|
|
2801
|
-
};
|
|
2802
|
-
if (state.quote) return readQuotedCommandLineChar({
|
|
2803
|
-
ch: state.ch,
|
|
2804
|
-
current: state.current,
|
|
2805
|
-
quote: state.quote,
|
|
2806
|
-
hasPart: state.hasPart
|
|
2807
|
-
});
|
|
2808
|
-
return readUnquotedCommandLineChar(state);
|
|
2809
|
-
}
|
|
2810
|
-
function readQuotedCommandLineChar(state) {
|
|
2811
|
-
if (state.ch === state.quote) return {
|
|
2812
|
-
current: state.current,
|
|
2813
|
-
quote: null,
|
|
2814
|
-
escaping: false,
|
|
2815
|
-
hasPart: true
|
|
2816
|
-
};
|
|
2817
|
-
return {
|
|
2818
|
-
current: state.current + state.ch,
|
|
2819
|
-
quote: state.quote,
|
|
2820
|
-
escaping: false,
|
|
2821
|
-
hasPart: true
|
|
2822
|
-
};
|
|
2233
|
+
function isPromptInput(value) {
|
|
2234
|
+
return Array.isArray(value) && value.every((entry) => isContentBlock(entry));
|
|
2823
2235
|
}
|
|
2824
|
-
function
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
hasPart: false
|
|
2236
|
+
function promptCapabilityRequirement(block) {
|
|
2237
|
+
switch (block.type) {
|
|
2238
|
+
case "image": return {
|
|
2239
|
+
blockType: "image",
|
|
2240
|
+
capability: "image"
|
|
2241
|
+
};
|
|
2242
|
+
case "audio": return {
|
|
2243
|
+
blockType: "audio",
|
|
2244
|
+
capability: "audio"
|
|
2245
|
+
};
|
|
2246
|
+
case "resource": return {
|
|
2247
|
+
blockType: "resource",
|
|
2248
|
+
capability: "embeddedContext"
|
|
2838
2249
|
};
|
|
2250
|
+
default: return;
|
|
2839
2251
|
}
|
|
2840
|
-
return {
|
|
2841
|
-
current: state.current + state.ch,
|
|
2842
|
-
quote: null,
|
|
2843
|
-
escaping: false,
|
|
2844
|
-
hasPart: true
|
|
2845
|
-
};
|
|
2846
|
-
}
|
|
2847
|
-
function flushCommandLinePart(parts, current, hasPart) {
|
|
2848
|
-
if (hasPart) parts.push(current);
|
|
2849
2252
|
}
|
|
2850
|
-
function
|
|
2851
|
-
|
|
2253
|
+
function getUnsupportedPromptContentMessage(prompt, agentCapabilities) {
|
|
2254
|
+
for (const [index, block] of prompt.entries()) {
|
|
2255
|
+
const requirement = promptCapabilityRequirement(block);
|
|
2256
|
+
if (!requirement) continue;
|
|
2257
|
+
if (agentCapabilities?.promptCapabilities?.[requirement.capability] === true) continue;
|
|
2258
|
+
return `prompt[${index}] ${requirement.blockType} content requires agentCapabilities.promptCapabilities.${requirement.capability}`;
|
|
2259
|
+
}
|
|
2852
2260
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
return translated;
|
|
2261
|
+
function textPrompt(text) {
|
|
2262
|
+
return [{
|
|
2263
|
+
type: "text",
|
|
2264
|
+
text
|
|
2265
|
+
}];
|
|
2859
2266
|
}
|
|
2860
|
-
function
|
|
2861
|
-
if (!
|
|
2267
|
+
function parseStructuredPrompt(source) {
|
|
2268
|
+
if (!source.startsWith("[")) return;
|
|
2862
2269
|
try {
|
|
2863
|
-
const
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
return
|
|
2270
|
+
const parsed = JSON.parse(source);
|
|
2271
|
+
if (isPromptInput(parsed)) return parsed;
|
|
2272
|
+
if (Array.isArray(parsed)) throw new PromptInputValidationError(parsed.map((entry, index) => getContentBlockValidationError(entry, index)).find((message) => message !== void 0) ?? "Structured prompt JSON must be an array of valid ACP content blocks");
|
|
2273
|
+
return;
|
|
2274
|
+
} catch (error) {
|
|
2275
|
+
if (error instanceof PromptInputValidationError) throw error;
|
|
2276
|
+
return;
|
|
2867
2277
|
}
|
|
2868
2278
|
}
|
|
2869
|
-
function
|
|
2870
|
-
|
|
2871
|
-
|
|
2279
|
+
function parsePromptSource(source) {
|
|
2280
|
+
const trimmed = source.trim();
|
|
2281
|
+
const structured = parseStructuredPrompt(trimmed);
|
|
2282
|
+
if (structured) return structured;
|
|
2283
|
+
if (!trimmed) return [];
|
|
2284
|
+
return textPrompt(trimmed);
|
|
2285
|
+
}
|
|
2286
|
+
function mergePromptSourceWithText(source, suffixText) {
|
|
2287
|
+
const prompt = parsePromptSource(source);
|
|
2288
|
+
const appended = suffixText.trim();
|
|
2289
|
+
if (!appended) return prompt;
|
|
2290
|
+
if (prompt.length === 0) return textPrompt(appended);
|
|
2291
|
+
return [...prompt, ...textPrompt(appended)];
|
|
2872
2292
|
}
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
const normalized = command.toLowerCase();
|
|
2876
|
-
return WINDOWS_EXECUTABLE_EXTENSION_RE.test(normalized);
|
|
2293
|
+
function promptToDisplayText(prompt) {
|
|
2294
|
+
return prompt.map((block) => contentBlockDisplayText(block)).filter((entry) => entry.trim().length > 0).join("\n\n").trim();
|
|
2877
2295
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2296
|
+
function contentBlockDisplayText(block) {
|
|
2297
|
+
switch (block.type) {
|
|
2298
|
+
case "text": return block.text;
|
|
2299
|
+
case "resource_link": return block.title ?? block.name ?? block.uri;
|
|
2300
|
+
case "resource": return resourceBlockDisplayText(block);
|
|
2301
|
+
case "image": return `[image] ${block.mimeType}`;
|
|
2302
|
+
case "audio": return `[audio] ${block.mimeType}`;
|
|
2303
|
+
default: return "";
|
|
2304
|
+
}
|
|
2880
2305
|
}
|
|
2881
|
-
function
|
|
2882
|
-
return
|
|
2306
|
+
function resourceBlockDisplayText(block) {
|
|
2307
|
+
return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
|
|
2883
2308
|
}
|
|
2884
2309
|
//#endregion
|
|
2885
2310
|
//#region src/acp/agent-command.ts
|
|
@@ -3178,32 +2603,108 @@ function addAuthCredentialEnvKeys(protectedKeys, methodId, credential) {
|
|
|
3178
2603
|
protectEnvKey(protectedKeys, normalized);
|
|
3179
2604
|
}
|
|
3180
2605
|
}
|
|
3181
|
-
function assignAuthCredentialEnv(env, methodId, credential) {
|
|
3182
|
-
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
3183
|
-
if (!methodId.includes("=") && !methodId.includes("\0") && env[methodId] == null) env[methodId] = credential;
|
|
3184
|
-
const normalized = toEnvToken(methodId);
|
|
3185
|
-
if (normalized) {
|
|
3186
|
-
assignIfMissing(env, `${AUTH_ENV_PREFIX}${normalized}`, credential);
|
|
3187
|
-
assignIfMissing(env, normalized, credential);
|
|
2606
|
+
function assignAuthCredentialEnv(env, methodId, credential) {
|
|
2607
|
+
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
2608
|
+
if (!methodId.includes("=") && !methodId.includes("\0") && env[methodId] == null) env[methodId] = credential;
|
|
2609
|
+
const normalized = toEnvToken(methodId);
|
|
2610
|
+
if (normalized) {
|
|
2611
|
+
assignIfMissing(env, `${AUTH_ENV_PREFIX}${normalized}`, credential);
|
|
2612
|
+
assignIfMissing(env, normalized, credential);
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
function assignIfMissing(env, key, value) {
|
|
2616
|
+
if (env[key] == null) env[key] = value;
|
|
2617
|
+
}
|
|
2618
|
+
function resolveConfiguredAuthCredential(methodId, authCredentials) {
|
|
2619
|
+
const configCredentials = authCredentials ?? {};
|
|
2620
|
+
return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)];
|
|
2621
|
+
}
|
|
2622
|
+
function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv, agentProcessEnv) {
|
|
2623
|
+
return {
|
|
2624
|
+
cwd,
|
|
2625
|
+
env: buildAgentEnvironment(authCredentials, sessionEnv, agentProcessEnv),
|
|
2626
|
+
stdio: [
|
|
2627
|
+
"pipe",
|
|
2628
|
+
"pipe",
|
|
2629
|
+
"pipe"
|
|
2630
|
+
],
|
|
2631
|
+
windowsHide: true
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
//#endregion
|
|
2635
|
+
//#region src/version.ts
|
|
2636
|
+
const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2637
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
2638
|
+
let cachedVersion = null;
|
|
2639
|
+
function parseVersion(value) {
|
|
2640
|
+
if (typeof value !== "string") return null;
|
|
2641
|
+
const trimmed = value.trim();
|
|
2642
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2643
|
+
}
|
|
2644
|
+
function readPackageVersion(packageJsonPath) {
|
|
2645
|
+
try {
|
|
2646
|
+
return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
|
|
2647
|
+
} catch {
|
|
2648
|
+
return null;
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
function resolveVersionFromAncestors(startDir) {
|
|
2652
|
+
let current = startDir;
|
|
2653
|
+
while (true) {
|
|
2654
|
+
const packageVersion = readPackageVersion(path.join(current, "package.json"));
|
|
2655
|
+
if (packageVersion) return packageVersion;
|
|
2656
|
+
const parent = path.dirname(current);
|
|
2657
|
+
if (parent === current) return null;
|
|
2658
|
+
current = parent;
|
|
3188
2659
|
}
|
|
3189
2660
|
}
|
|
3190
|
-
function
|
|
3191
|
-
|
|
2661
|
+
function resolveAcpxVersion(params) {
|
|
2662
|
+
const envVersion = resolvePackageEnvVersion(params?.env ?? process.env);
|
|
2663
|
+
if (envVersion) return envVersion;
|
|
2664
|
+
if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2665
|
+
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
3192
2666
|
}
|
|
3193
|
-
function
|
|
3194
|
-
const
|
|
3195
|
-
|
|
2667
|
+
function resolvePackageEnvVersion(env) {
|
|
2668
|
+
const envPackageName = parseVersion(env.npm_package_name);
|
|
2669
|
+
const envVersion = parseVersion(env.npm_package_version);
|
|
2670
|
+
return envPackageName === "acpx" ? envVersion : null;
|
|
3196
2671
|
}
|
|
3197
|
-
function
|
|
2672
|
+
function getAcpxVersion() {
|
|
2673
|
+
if (cachedVersion) return cachedVersion;
|
|
2674
|
+
cachedVersion = resolveAcpxVersion();
|
|
2675
|
+
return cachedVersion;
|
|
2676
|
+
}
|
|
2677
|
+
//#endregion
|
|
2678
|
+
//#region src/acp/client-protocol.ts
|
|
2679
|
+
const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
|
|
2680
|
+
const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
|
|
2681
|
+
const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
|
|
2682
|
+
function resolveClientInfo(devinAcp) {
|
|
2683
|
+
if (!devinAcp) return {
|
|
2684
|
+
name: "acpx",
|
|
2685
|
+
version: getAcpxVersion()
|
|
2686
|
+
};
|
|
3198
2687
|
return {
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
2688
|
+
name: DEVIN_COMPATIBILITY_CLIENT_NAME,
|
|
2689
|
+
version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
function resolveClientCapabilities(params) {
|
|
2693
|
+
const baseCapabilities = {
|
|
2694
|
+
fs: {
|
|
2695
|
+
readTextFile: params.fs,
|
|
2696
|
+
writeTextFile: params.fs
|
|
2697
|
+
},
|
|
2698
|
+
terminal: params.terminal,
|
|
2699
|
+
...params.elicitationModes.length > 0 ? { elicitation: {
|
|
2700
|
+
...params.elicitationModes.includes("form") ? { form: {} } : {},
|
|
2701
|
+
...params.elicitationModes.includes("url") ? { url: {} } : {}
|
|
2702
|
+
} } : {}
|
|
2703
|
+
};
|
|
2704
|
+
if (!params.devinAcp) return baseCapabilities;
|
|
2705
|
+
return {
|
|
2706
|
+
...baseCapabilities,
|
|
2707
|
+
_meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
|
|
3207
2708
|
};
|
|
3208
2709
|
}
|
|
3209
2710
|
//#endregion
|
|
@@ -3261,9 +2762,8 @@ function parseAvailableModels(value) {
|
|
|
3261
2762
|
function isModelSelectOption(option) {
|
|
3262
2763
|
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
3263
2764
|
}
|
|
3264
|
-
function parseModelConfigOption(
|
|
3265
|
-
|
|
3266
|
-
if (!option || !isModelSelectOption(option) || typeof option.id !== "string" || typeof option.currentValue !== "string") return;
|
|
2765
|
+
function parseModelConfigOption(option) {
|
|
2766
|
+
if (!isModelSelectOption(option) || typeof option.id !== "string" || typeof option.currentValue !== "string") return;
|
|
3267
2767
|
const availableModels = parseAvailableModels(option.options);
|
|
3268
2768
|
return availableModels ? {
|
|
3269
2769
|
configId: option.id,
|
|
@@ -3271,12 +2771,26 @@ function parseModelConfigOption(value) {
|
|
|
3271
2771
|
availableModels
|
|
3272
2772
|
} : void 0;
|
|
3273
2773
|
}
|
|
2774
|
+
function modelConfigPriority(option) {
|
|
2775
|
+
if (option.category !== "model") return 0;
|
|
2776
|
+
return option.id === "model" ? 2 : 1;
|
|
2777
|
+
}
|
|
3274
2778
|
function modelStateFromConfigOptions(configOptions) {
|
|
3275
2779
|
if (!Array.isArray(configOptions)) return;
|
|
2780
|
+
let selected;
|
|
2781
|
+
let selectedPriority = -1;
|
|
3276
2782
|
for (const value of configOptions) {
|
|
3277
|
-
const
|
|
3278
|
-
if (
|
|
2783
|
+
const option = asRecord$2(value);
|
|
2784
|
+
if (!option) continue;
|
|
2785
|
+
const models = parseModelConfigOption(option);
|
|
2786
|
+
if (!models) continue;
|
|
2787
|
+
const priority = modelConfigPriority(option);
|
|
2788
|
+
if (priority > selectedPriority) {
|
|
2789
|
+
selected = models;
|
|
2790
|
+
selectedPriority = priority;
|
|
2791
|
+
}
|
|
3279
2792
|
}
|
|
2793
|
+
return selected;
|
|
3280
2794
|
}
|
|
3281
2795
|
function modelStateFromLegacyResponse(response) {
|
|
3282
2796
|
if (!response || typeof response !== "object") return;
|
|
@@ -3327,9 +2841,10 @@ function assertRequestedModelSupported(params) {
|
|
|
3327
2841
|
}
|
|
3328
2842
|
//#endregion
|
|
3329
2843
|
//#region src/acp/ndjson-stream.ts
|
|
2844
|
+
const DEFAULT_MAX_ACP_MESSAGE_BYTES = 67108864;
|
|
3330
2845
|
var AcpMessageLimitError = class extends AcpxOperationalError {
|
|
3331
2846
|
constructor(limit) {
|
|
3332
|
-
super(`ACP message exceeded ACPX_MAX_ACP_MESSAGE_BYTES (${limit} bytes)
|
|
2847
|
+
super(`ACP message exceeded ACPX_MAX_ACP_MESSAGE_BYTES (${limit} bytes). Increase the limit or set it to 0 for unlimited input.`, {
|
|
3333
2848
|
outputCode: "RUNTIME",
|
|
3334
2849
|
detailCode: "ACP_MESSAGE_TOO_LARGE",
|
|
3335
2850
|
origin: "acp",
|
|
@@ -3339,7 +2854,7 @@ var AcpMessageLimitError = class extends AcpxOperationalError {
|
|
|
3339
2854
|
};
|
|
3340
2855
|
function readMaxAcpMessageBytes(raw = process.env.ACPX_MAX_ACP_MESSAGE_BYTES) {
|
|
3341
2856
|
const value = raw?.trim();
|
|
3342
|
-
if (!value) return;
|
|
2857
|
+
if (!value) return DEFAULT_MAX_ACP_MESSAGE_BYTES;
|
|
3343
2858
|
const bytes = Number(value);
|
|
3344
2859
|
if (!/^\d+$/.test(value) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_MAX_ACP_MESSAGE_BYTES must be a non-negative safe integer; zero is unlimited");
|
|
3345
2860
|
return bytes === 0 ? void 0 : bytes;
|
|
@@ -3413,6 +2928,156 @@ function createNdJsonMessageStream(agentCommand, output, input, maxMessageBytes,
|
|
|
3413
2928
|
};
|
|
3414
2929
|
}
|
|
3415
2930
|
//#endregion
|
|
2931
|
+
//#region src/acp/observed-stream.ts
|
|
2932
|
+
function observeAcpStream(base, options) {
|
|
2933
|
+
const elicitationRequestIds = /* @__PURE__ */ new Set();
|
|
2934
|
+
const shouldSuppressInboundReplaySessionUpdate = (message) => {
|
|
2935
|
+
return options.suppressReplaySessionUpdates() && isSessionUpdateNotification(message);
|
|
2936
|
+
};
|
|
2937
|
+
const observeInbound = (message) => {
|
|
2938
|
+
const requestId = elicitationRequestId(message);
|
|
2939
|
+
if (requestId !== void 0) {
|
|
2940
|
+
elicitationRequestIds.add(requestId);
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
if (!shouldSuppressInboundReplaySessionUpdate(message)) options.onMessage("inbound", message);
|
|
2944
|
+
};
|
|
2945
|
+
return {
|
|
2946
|
+
readable: new ReadableStream({ async start(controller) {
|
|
2947
|
+
const reader = base.readable.getReader();
|
|
2948
|
+
try {
|
|
2949
|
+
while (true) {
|
|
2950
|
+
const { value, done } = await reader.read();
|
|
2951
|
+
if (done) break;
|
|
2952
|
+
if (!value) continue;
|
|
2953
|
+
observeInbound(value);
|
|
2954
|
+
if (isExtensionNotification(value)) continue;
|
|
2955
|
+
controller.enqueue(value);
|
|
2956
|
+
}
|
|
2957
|
+
} finally {
|
|
2958
|
+
reader.releaseLock();
|
|
2959
|
+
controller.close();
|
|
2960
|
+
}
|
|
2961
|
+
} }),
|
|
2962
|
+
writable: new WritableStream({ async write(message) {
|
|
2963
|
+
const promptOwner = promptRequestOwner(message);
|
|
2964
|
+
const activePrompt = promptOwner ? options.bindPromptOwner(promptOwner) : void 0;
|
|
2965
|
+
const id = responseId(message);
|
|
2966
|
+
if (!(id !== void 0 && elicitationRequestIds.delete(id))) options.onMessage("outbound", message);
|
|
2967
|
+
const writer = base.writable.getWriter();
|
|
2968
|
+
try {
|
|
2969
|
+
await writer.write(message);
|
|
2970
|
+
} finally {
|
|
2971
|
+
writer.releaseLock();
|
|
2972
|
+
}
|
|
2973
|
+
if (activePrompt && promptOwner) options.onPromptRequestWritten(activePrompt, promptOwner);
|
|
2974
|
+
} })
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
function isExtensionNotification(message) {
|
|
2978
|
+
if (!("method" in message) || "id" in message || typeof message.method !== "string") return false;
|
|
2979
|
+
return message.method !== methods.client.session.update && message.method !== methods.client.elicitation.complete && message.method !== methods.protocol.cancelRequest;
|
|
2980
|
+
}
|
|
2981
|
+
function elicitationRequestId(message) {
|
|
2982
|
+
if (!("method" in message) || message.method !== methods.client.elicitation.create || !("id" in message)) return;
|
|
2983
|
+
return message.id;
|
|
2984
|
+
}
|
|
2985
|
+
function responseId(message) {
|
|
2986
|
+
if (!("id" in message) || "method" in message) return;
|
|
2987
|
+
return message.id;
|
|
2988
|
+
}
|
|
2989
|
+
function promptRequestOwner(message) {
|
|
2990
|
+
if (!("method" in message) || message.method !== methods.agent.session.prompt || !("id" in message)) return;
|
|
2991
|
+
const sessionId = message.params?.sessionId;
|
|
2992
|
+
return typeof sessionId === "string" ? {
|
|
2993
|
+
requestId: message.id,
|
|
2994
|
+
sessionId
|
|
2995
|
+
} : void 0;
|
|
2996
|
+
}
|
|
2997
|
+
//#endregion
|
|
2998
|
+
//#region src/acp/process-descendants.ts
|
|
2999
|
+
function parseProcessTable(output) {
|
|
3000
|
+
const table = /* @__PURE__ */ new Map();
|
|
3001
|
+
for (const line of output.split("\n")) {
|
|
3002
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
|
|
3003
|
+
if (match && !match[3].startsWith("Z")) {
|
|
3004
|
+
const pid = Number(match[1]);
|
|
3005
|
+
if (Number.isSafeInteger(pid) && pid > 1 && pid !== process.pid) table.set(pid, {
|
|
3006
|
+
pid,
|
|
3007
|
+
parentPid: Number(match[2]),
|
|
3008
|
+
birth: match[4]
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
return table;
|
|
3013
|
+
}
|
|
3014
|
+
function includeDescendants(table, owned) {
|
|
3015
|
+
let expanded;
|
|
3016
|
+
do {
|
|
3017
|
+
expanded = false;
|
|
3018
|
+
for (const identity of table.values()) if (owned.has(identity.parentPid) && !owned.has(identity.pid)) {
|
|
3019
|
+
owned.add(identity.pid);
|
|
3020
|
+
expanded = true;
|
|
3021
|
+
}
|
|
3022
|
+
} while (expanded);
|
|
3023
|
+
}
|
|
3024
|
+
/** Best-effort POSIX cleanup for descendants witnessed during this child launch. */
|
|
3025
|
+
var ProcessDescendants = class {
|
|
3026
|
+
child;
|
|
3027
|
+
identities = /* @__PURE__ */ new Map();
|
|
3028
|
+
pending;
|
|
3029
|
+
retired = false;
|
|
3030
|
+
constructor(child) {
|
|
3031
|
+
this.child = child;
|
|
3032
|
+
}
|
|
3033
|
+
capture(timeoutMs = PROCESS_HELPER_TIMEOUT_MS) {
|
|
3034
|
+
if (this.retired || process.platform === "win32") return Promise.resolve(true);
|
|
3035
|
+
this.pending ??= this.readSnapshot(timeoutMs).finally(() => {
|
|
3036
|
+
this.pending = void 0;
|
|
3037
|
+
});
|
|
3038
|
+
return this.pending;
|
|
3039
|
+
}
|
|
3040
|
+
async readSnapshot(timeoutMs) {
|
|
3041
|
+
try {
|
|
3042
|
+
const output = await runTimedExecFile("ps", ["-eo", "pid=,ppid=,stat=,lstart="], { timeoutMs });
|
|
3043
|
+
if (!this.retired) this.refresh(parseProcessTable(output));
|
|
3044
|
+
return true;
|
|
3045
|
+
} catch {
|
|
3046
|
+
return false;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
refresh(table) {
|
|
3050
|
+
const owned = /* @__PURE__ */ new Set();
|
|
3051
|
+
for (const [pid, identity] of this.identities) if (table.get(pid)?.birth === identity.birth) owned.add(pid);
|
|
3052
|
+
const root = this.child.pid;
|
|
3053
|
+
if (root && isChildProcessRunning(this.child)) owned.add(root);
|
|
3054
|
+
includeDescendants(table, owned);
|
|
3055
|
+
owned.delete(root ?? 0);
|
|
3056
|
+
this.identities = new Map([...table].filter(([pid]) => owned.has(pid)));
|
|
3057
|
+
}
|
|
3058
|
+
async signal(signal, timeoutMs) {
|
|
3059
|
+
if (!await this.capture(timeoutMs) || this.retired) return;
|
|
3060
|
+
for (const pid of this.identities.keys()) try {
|
|
3061
|
+
process.kill(pid, signal);
|
|
3062
|
+
} catch {
|
|
3063
|
+
this.identities.delete(pid);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
async waitForExit(timeoutMs) {
|
|
3067
|
+
const deadline = Date.now() + timeoutMs;
|
|
3068
|
+
do {
|
|
3069
|
+
if (!await this.capture(Math.max(1, deadline - Date.now()))) return false;
|
|
3070
|
+
if (this.identities.size === 0) return true;
|
|
3071
|
+
await setTimeout$1(Math.min(100, Math.max(0, deadline - Date.now())));
|
|
3072
|
+
} while (Date.now() < deadline);
|
|
3073
|
+
return false;
|
|
3074
|
+
}
|
|
3075
|
+
retire() {
|
|
3076
|
+
this.retired = true;
|
|
3077
|
+
this.identities.clear();
|
|
3078
|
+
}
|
|
3079
|
+
};
|
|
3080
|
+
//#endregion
|
|
3416
3081
|
//#region src/acp/session-control-errors.ts
|
|
3417
3082
|
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
3418
3083
|
function asRecord$1(value) {
|
|
@@ -3469,6 +3134,13 @@ function buildTerminalSpawnOptions(command, cwd, env, platform = process.platfor
|
|
|
3469
3134
|
windowsHide: true
|
|
3470
3135
|
}, platform, resolvedEnv ?? process.env);
|
|
3471
3136
|
}
|
|
3137
|
+
function readTerminalOutputCeiling() {
|
|
3138
|
+
const raw = process.env.ACPX_TERMINAL_MAX_OUTPUT_BYTES?.trim();
|
|
3139
|
+
if (!raw) return;
|
|
3140
|
+
const bytes = Number(raw);
|
|
3141
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(bytes)) throw new Error("ACPX_TERMINAL_MAX_OUTPUT_BYTES must be a non-negative safe integer; zero disables the host ceiling");
|
|
3142
|
+
return bytes === 0 ? void 0 : bytes;
|
|
3143
|
+
}
|
|
3472
3144
|
function trimToUtf8Boundary(buffer, limit) {
|
|
3473
3145
|
if (limit <= 0) return Buffer.alloc(0);
|
|
3474
3146
|
if (buffer.length <= limit) return buffer;
|
|
@@ -3477,20 +3149,6 @@ function trimToUtf8Boundary(buffer, limit) {
|
|
|
3477
3149
|
if (start >= buffer.length) start = buffer.length - limit;
|
|
3478
3150
|
return buffer.subarray(start);
|
|
3479
3151
|
}
|
|
3480
|
-
function waitForSpawn(process) {
|
|
3481
|
-
return new Promise((resolve, reject) => {
|
|
3482
|
-
const onSpawn = () => {
|
|
3483
|
-
process.off("error", onError);
|
|
3484
|
-
resolve();
|
|
3485
|
-
};
|
|
3486
|
-
const onError = (error) => {
|
|
3487
|
-
process.off("spawn", onSpawn);
|
|
3488
|
-
reject(error);
|
|
3489
|
-
};
|
|
3490
|
-
process.once("spawn", onSpawn);
|
|
3491
|
-
process.once("error", onError);
|
|
3492
|
-
});
|
|
3493
|
-
}
|
|
3494
3152
|
async function defaultConfirmExecute(commandLine) {
|
|
3495
3153
|
return await promptForPermission({ prompt: `\n[permission] Allow terminal command "${commandLine}"? (y/N) ` });
|
|
3496
3154
|
}
|
|
@@ -3512,8 +3170,10 @@ var TerminalManager = class {
|
|
|
3512
3170
|
confirmExecute;
|
|
3513
3171
|
killGraceMs;
|
|
3514
3172
|
processHelperTimeoutMs;
|
|
3173
|
+
outputCeilingBytes;
|
|
3515
3174
|
terminals = /* @__PURE__ */ new Map();
|
|
3516
3175
|
constructor(options) {
|
|
3176
|
+
this.outputCeilingBytes = readTerminalOutputCeiling();
|
|
3517
3177
|
this.cwd = options.cwd;
|
|
3518
3178
|
this.permissionMode = options.permissionMode;
|
|
3519
3179
|
this.nonInteractivePermissions = options.nonInteractivePermissions ?? "deny";
|
|
@@ -3538,7 +3198,8 @@ var TerminalManager = class {
|
|
|
3538
3198
|
});
|
|
3539
3199
|
try {
|
|
3540
3200
|
if (!await this.isExecuteApproved(commandLine)) throw new PermissionDeniedError("Permission denied for terminal/create");
|
|
3541
|
-
const
|
|
3201
|
+
const requestedLimit = Math.max(0, Math.round(params.outputByteLimit ?? DEFAULT_TERMINAL_OUTPUT_LIMIT_BYTES));
|
|
3202
|
+
const outputByteLimit = Math.min(requestedLimit, this.outputCeilingBytes ?? Number.POSITIVE_INFINITY);
|
|
3542
3203
|
const { proc, spawnCommand } = await spawnTerminalProcess(params, this.cwd);
|
|
3543
3204
|
let resolveExit = () => {};
|
|
3544
3205
|
const exitPromise = new Promise((resolve) => {
|
|
@@ -3902,11 +3563,12 @@ async function runProcessGroupListCommand(timeoutMs) {
|
|
|
3902
3563
|
return await runTimedExecFile("ps", ["-eo", "pid=,pgid="], { timeoutMs });
|
|
3903
3564
|
}
|
|
3904
3565
|
async function runWindowsProcessListCommand(timeoutMs) {
|
|
3566
|
+
const command = ["Get-CimInstance Win32_Process |", "ForEach-Object { \"$($_.ProcessId) $($_.ParentProcessId)\" }"].join(" ");
|
|
3905
3567
|
return await runTimedExecFile("powershell.exe", [
|
|
3906
3568
|
"-NoProfile",
|
|
3907
3569
|
"-NonInteractive",
|
|
3908
3570
|
"-Command",
|
|
3909
|
-
|
|
3571
|
+
command
|
|
3910
3572
|
], {
|
|
3911
3573
|
timeoutMs,
|
|
3912
3574
|
windowsHide: true
|
|
@@ -3959,10 +3621,8 @@ const REPLAY_DRAIN_TIMEOUT_MS = 5e3;
|
|
|
3959
3621
|
const DRAIN_POLL_INTERVAL_MS = 20;
|
|
3960
3622
|
const AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
3961
3623
|
const AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
3624
|
+
const AGENT_CLEANUP_BUDGET_MS = 8e3;
|
|
3962
3625
|
const STARTUP_STDERR_MAX_CHARS = 8192;
|
|
3963
|
-
const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
|
|
3964
|
-
const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
|
|
3965
|
-
const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
|
|
3966
3626
|
const ELICITATION_CANCEL_MESSAGES = {
|
|
3967
3627
|
inactive: "elicitation owner is no longer active",
|
|
3968
3628
|
unavailable: "elicitation handler is unavailable",
|
|
@@ -3971,16 +3631,6 @@ const ELICITATION_CANCEL_MESSAGES = {
|
|
|
3971
3631
|
cancelled: "elicitation request was cancelled",
|
|
3972
3632
|
requestScoped: "request-scoped elicitation has no owner"
|
|
3973
3633
|
};
|
|
3974
|
-
function resolveClientInfo(devinAcp) {
|
|
3975
|
-
if (!devinAcp) return {
|
|
3976
|
-
name: "acpx",
|
|
3977
|
-
version: getAcpxVersion()
|
|
3978
|
-
};
|
|
3979
|
-
return {
|
|
3980
|
-
name: DEVIN_COMPATIBILITY_CLIENT_NAME,
|
|
3981
|
-
version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
|
|
3982
|
-
};
|
|
3983
|
-
}
|
|
3984
3634
|
function normalizeElicitationModes(modes) {
|
|
3985
3635
|
return [...new Set((modes ?? []).filter((mode) => mode === "form" || mode === "url"))];
|
|
3986
3636
|
}
|
|
@@ -4003,72 +3653,18 @@ function elicitationRequestScopeId(request) {
|
|
|
4003
3653
|
const value = request.requestId;
|
|
4004
3654
|
return value === null || typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
4005
3655
|
}
|
|
4006
|
-
function
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
3656
|
+
async function raceWithAbort(signal, pending) {
|
|
3657
|
+
let onAbort;
|
|
3658
|
+
const aborted = new Promise((resolve) => {
|
|
3659
|
+
onAbort = () => resolve(void 0);
|
|
3660
|
+
if (signal.aborted) onAbort();
|
|
3661
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
4010
3662
|
});
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
}
|
|
4016
|
-
function elicitationRequestId(message) {
|
|
4017
|
-
if (!("method" in message) || message.method !== methods.client.elicitation.create || !("id" in message)) return;
|
|
4018
|
-
return message.id;
|
|
4019
|
-
}
|
|
4020
|
-
function responseId(message) {
|
|
4021
|
-
if (!("id" in message) || "method" in message) return;
|
|
4022
|
-
return message.id;
|
|
4023
|
-
}
|
|
4024
|
-
function promptRequestOwner(message) {
|
|
4025
|
-
if (!("method" in message) || message.method !== methods.agent.session.prompt || !("id" in message)) return;
|
|
4026
|
-
const sessionId = message.params?.sessionId;
|
|
4027
|
-
return typeof sessionId === "string" ? {
|
|
4028
|
-
requestId: message.id,
|
|
4029
|
-
sessionId
|
|
4030
|
-
} : void 0;
|
|
4031
|
-
}
|
|
4032
|
-
function createAgentConnectionFacade(connection) {
|
|
4033
|
-
const agent = connection.agent;
|
|
4034
|
-
return {
|
|
4035
|
-
signal: connection.signal,
|
|
4036
|
-
close: (error) => connection.close(error),
|
|
4037
|
-
initialize: async (params) => await agent.request(methods.agent.initialize, params),
|
|
4038
|
-
authenticate: async (params) => {
|
|
4039
|
-
await agent.request(methods.agent.authenticate, params);
|
|
4040
|
-
},
|
|
4041
|
-
newSession: async (params) => await agent.request(methods.agent.session.new, params),
|
|
4042
|
-
loadSession: async (params) => await agent.request(methods.agent.session.load, params),
|
|
4043
|
-
resumeSession: async (params) => await agent.request(methods.agent.session.resume, params),
|
|
4044
|
-
prompt: async (params) => await agent.request(methods.agent.session.prompt, params),
|
|
4045
|
-
setSessionMode: async (params) => await agent.request(methods.agent.session.setMode, params),
|
|
4046
|
-
setSessionConfigOption: async (params) => await agent.request(methods.agent.session.setConfigOption, params),
|
|
4047
|
-
extMethod: async (method, params) => await agent.request(method, params),
|
|
4048
|
-
cancel: async (params) => await agent.notify(methods.agent.session.cancel, params),
|
|
4049
|
-
closeSession: async (params) => {
|
|
4050
|
-
await agent.request(methods.agent.session.close, params);
|
|
4051
|
-
},
|
|
4052
|
-
listSessions: async (params) => await agent.request(methods.agent.session.list, params)
|
|
4053
|
-
};
|
|
4054
|
-
}
|
|
4055
|
-
function resolveClientCapabilities(params) {
|
|
4056
|
-
const baseCapabilities = {
|
|
4057
|
-
fs: {
|
|
4058
|
-
readTextFile: params.fs,
|
|
4059
|
-
writeTextFile: params.fs
|
|
4060
|
-
},
|
|
4061
|
-
terminal: params.terminal,
|
|
4062
|
-
...params.elicitationModes.length > 0 ? { elicitation: {
|
|
4063
|
-
...params.elicitationModes.includes("form") ? { form: {} } : {},
|
|
4064
|
-
...params.elicitationModes.includes("url") ? { url: {} } : {}
|
|
4065
|
-
} } : {}
|
|
4066
|
-
};
|
|
4067
|
-
if (!params.devinAcp) return baseCapabilities;
|
|
4068
|
-
return {
|
|
4069
|
-
...baseCapabilities,
|
|
4070
|
-
_meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
|
|
4071
|
-
};
|
|
3663
|
+
try {
|
|
3664
|
+
return await Promise.race([pending, aborted]);
|
|
3665
|
+
} finally {
|
|
3666
|
+
signal.removeEventListener("abort", onAbort);
|
|
3667
|
+
}
|
|
4072
3668
|
}
|
|
4073
3669
|
function hasResponseField(response, field) {
|
|
4074
3670
|
return !!response && typeof response === "object" && field in response;
|
|
@@ -4080,7 +3676,7 @@ function normalizeResponseConfigOptions(response) {
|
|
|
4080
3676
|
function toReconnectedSessionResult(response) {
|
|
4081
3677
|
const configOptions = normalizeResponseConfigOptions(response);
|
|
4082
3678
|
return {
|
|
4083
|
-
agentSessionId:
|
|
3679
|
+
agentSessionId: extractAgentSessionId(response?._meta),
|
|
4084
3680
|
configOptions,
|
|
4085
3681
|
models: modelStateFromSessionResponse({
|
|
4086
3682
|
configOptions,
|
|
@@ -4135,6 +3731,8 @@ var AcpClient = class {
|
|
|
4135
3731
|
options;
|
|
4136
3732
|
connection;
|
|
4137
3733
|
agent;
|
|
3734
|
+
agentDescendants = /* @__PURE__ */ new WeakMap();
|
|
3735
|
+
agentCleanups = /* @__PURE__ */ new WeakMap();
|
|
4138
3736
|
initResult;
|
|
4139
3737
|
loadedSessionId;
|
|
4140
3738
|
eventHandlers;
|
|
@@ -4277,6 +3875,7 @@ var AcpClient = class {
|
|
|
4277
3875
|
this.logAgentLaunch(launch);
|
|
4278
3876
|
await this.ensureLaunchSupport(launch);
|
|
4279
3877
|
const { child, process: startedProcess } = await this.spawnAgentProcess(launch);
|
|
3878
|
+
this.agent = child;
|
|
4280
3879
|
this.closing = false;
|
|
4281
3880
|
this.agentStartedAt = startedProcess.startedAt;
|
|
4282
3881
|
this.lastAgentExit = void 0;
|
|
@@ -4301,11 +3900,11 @@ var AcpClient = class {
|
|
|
4301
3900
|
let connection;
|
|
4302
3901
|
const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output, maxMessageBytes, (error) => {
|
|
4303
3902
|
this.rejectPendingConnectionRequests(error);
|
|
4304
|
-
connection?.close
|
|
3903
|
+
connection?.close(error);
|
|
4305
3904
|
}));
|
|
4306
3905
|
connection = this.createConnection(stream, launch);
|
|
4307
3906
|
connection.signal.addEventListener("abort", () => {
|
|
4308
|
-
this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3907
|
+
this.recordAgentExit(child, "connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
4309
3908
|
}, { once: true });
|
|
4310
3909
|
await this.initializeAgentConnection({
|
|
4311
3910
|
child,
|
|
@@ -4370,13 +3969,14 @@ var AcpClient = class {
|
|
|
4370
3969
|
...plan.spawnOptions,
|
|
4371
3970
|
windowsVerbatimArguments: spawnCommand.windowsVerbatimArguments
|
|
4372
3971
|
});
|
|
4373
|
-
await waitForSpawn
|
|
3972
|
+
await waitForSpawn(spawnedChild);
|
|
4374
3973
|
} catch (error) {
|
|
4375
3974
|
const spawnError = new AgentSpawnError(this.options.agentCommand, error);
|
|
4376
3975
|
this.notifyProcessSpawnFailure(launch, spawnError);
|
|
4377
3976
|
throw spawnError;
|
|
4378
3977
|
}
|
|
4379
3978
|
const child = requireAgentStdio(spawnedChild);
|
|
3979
|
+
this.agentDescendants.set(child, new ProcessDescendants(child));
|
|
4380
3980
|
const pid = child.pid;
|
|
4381
3981
|
if (pid === void 0) {
|
|
4382
3982
|
const spawnError = new AgentSpawnError(this.options.agentCommand, /* @__PURE__ */ new Error("spawned agent process did not expose a PID"));
|
|
@@ -4389,7 +3989,7 @@ var AcpClient = class {
|
|
|
4389
3989
|
process: Object.freeze({
|
|
4390
3990
|
...launch,
|
|
4391
3991
|
pid,
|
|
4392
|
-
startedAt: isoNow$
|
|
3992
|
+
startedAt: isoNow$2()
|
|
4393
3993
|
})
|
|
4394
3994
|
};
|
|
4395
3995
|
}
|
|
@@ -4422,34 +4022,34 @@ var AcpClient = class {
|
|
|
4422
4022
|
}).onRequest(methods.client.terminal.create, async ({ params }) => {
|
|
4423
4023
|
return await this.handleCreateTerminal(params);
|
|
4424
4024
|
}).onRequest(methods.client.terminal.output, async ({ params }) => {
|
|
4425
|
-
return await this.
|
|
4025
|
+
return await this.terminalManager.terminalOutput(params);
|
|
4426
4026
|
}).onRequest(methods.client.terminal.waitForExit, async ({ params }) => {
|
|
4427
|
-
return await this.
|
|
4027
|
+
return await this.terminalManager.waitForTerminalExit(params);
|
|
4428
4028
|
}).onRequest(methods.client.terminal.kill, async ({ params }) => {
|
|
4429
|
-
return await this.
|
|
4029
|
+
return await this.terminalManager.killTerminal(params);
|
|
4430
4030
|
}).onRequest(methods.client.terminal.release, async ({ params }) => {
|
|
4431
|
-
return await this.
|
|
4031
|
+
return await this.terminalManager.releaseTerminal(params);
|
|
4432
4032
|
});
|
|
4433
4033
|
if (launch.devinAcp) app.onRequest("_cognition.ai/request_diagnostics", (params) => {
|
|
4434
4034
|
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
|
4435
4035
|
}, async () => ({}));
|
|
4436
|
-
return
|
|
4036
|
+
return app.connect(stream);
|
|
4437
4037
|
}
|
|
4438
4038
|
async initializeAgentConnection(params) {
|
|
4439
4039
|
try {
|
|
4440
4040
|
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch), params.startupFailure.promise]);
|
|
4441
4041
|
params.startupFailure.dispose();
|
|
4442
4042
|
this.connection = params.connection;
|
|
4443
|
-
this.agent = params.child;
|
|
4444
4043
|
this.initResult = initResult;
|
|
4044
|
+
await this.captureAgentDescendants(params.child);
|
|
4445
4045
|
this.log(`initialized protocol version ${initResult.protocolVersion}`);
|
|
4446
4046
|
} catch (error) {
|
|
4447
|
-
params.connection.close
|
|
4047
|
+
params.connection.close(error);
|
|
4448
4048
|
await this.handleInitializeFailure(params, error);
|
|
4449
4049
|
}
|
|
4450
4050
|
}
|
|
4451
4051
|
async initializeProtocolConnection(connection, launch) {
|
|
4452
|
-
const initializePromise = connection.initialize
|
|
4052
|
+
const initializePromise = connection.agent.request(methods.agent.initialize, {
|
|
4453
4053
|
protocolVersion: PROTOCOL_VERSION,
|
|
4454
4054
|
clientCapabilities: resolveClientCapabilities({
|
|
4455
4055
|
devinAcp: launch.devinAcp,
|
|
@@ -4465,10 +4065,9 @@ var AcpClient = class {
|
|
|
4465
4065
|
}
|
|
4466
4066
|
async handleInitializeFailure(params, error) {
|
|
4467
4067
|
params.startupFailure.dispose();
|
|
4068
|
+
await this.captureAgentDescendants(params.child);
|
|
4468
4069
|
const normalizedError = error instanceof AcpMessageLimitError ? error : await this.normalizeInitializeError(error, params.child, params.startupStderr);
|
|
4469
|
-
|
|
4470
|
-
params.child.kill();
|
|
4471
|
-
} catch {}
|
|
4070
|
+
await this.terminateAgentProcess(params.child);
|
|
4472
4071
|
if (params.launch.geminiAcp && error instanceof TimeoutError) throw new GeminiAcpStartupTimeoutError(await buildGeminiAcpStartupTimeoutMessage(params.launch.spawnCommand), {
|
|
4473
4072
|
cause: error,
|
|
4474
4073
|
retryable: true
|
|
@@ -4476,59 +4075,15 @@ var AcpClient = class {
|
|
|
4476
4075
|
throw normalizedError;
|
|
4477
4076
|
}
|
|
4478
4077
|
createTappedStream(base) {
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
const requestId = elicitationRequestId(message);
|
|
4489
|
-
if (requestId !== void 0) {
|
|
4490
|
-
elicitationRequestIds.add(requestId);
|
|
4491
|
-
return;
|
|
4492
|
-
}
|
|
4493
|
-
if (!shouldSuppressInboundReplaySessionUpdate(message)) {
|
|
4494
|
-
onAcpOutputMessage()?.("inbound", message);
|
|
4495
|
-
onAcpMessage()?.("inbound", message);
|
|
4496
|
-
}
|
|
4497
|
-
};
|
|
4498
|
-
return {
|
|
4499
|
-
readable: new ReadableStream({ async start(controller) {
|
|
4500
|
-
const reader = base.readable.getReader();
|
|
4501
|
-
try {
|
|
4502
|
-
while (true) {
|
|
4503
|
-
const { value, done } = await reader.read();
|
|
4504
|
-
if (done) break;
|
|
4505
|
-
if (!value) continue;
|
|
4506
|
-
observeInbound(value);
|
|
4507
|
-
if (isExtensionNotification(value)) continue;
|
|
4508
|
-
controller.enqueue(value);
|
|
4509
|
-
}
|
|
4510
|
-
} finally {
|
|
4511
|
-
reader.releaseLock();
|
|
4512
|
-
controller.close();
|
|
4513
|
-
}
|
|
4514
|
-
} }),
|
|
4515
|
-
writable: new WritableStream({ async write(message) {
|
|
4516
|
-
const promptOwner = promptRequestOwner(message);
|
|
4517
|
-
const activePrompt = promptOwner ? bindPromptOwner(promptOwner) : void 0;
|
|
4518
|
-
const id = responseId(message);
|
|
4519
|
-
if (!(id !== void 0 && elicitationRequestIds.delete(id))) {
|
|
4520
|
-
onAcpOutputMessage()?.("outbound", message);
|
|
4521
|
-
onAcpMessage()?.("outbound", message);
|
|
4522
|
-
}
|
|
4523
|
-
const writer = base.writable.getWriter();
|
|
4524
|
-
try {
|
|
4525
|
-
await writer.write(message);
|
|
4526
|
-
} finally {
|
|
4527
|
-
writer.releaseLock();
|
|
4528
|
-
}
|
|
4529
|
-
if (activePrompt && promptOwner) onPromptRequestWritten(activePrompt, promptOwner);
|
|
4530
|
-
} })
|
|
4531
|
-
};
|
|
4078
|
+
return observeAcpStream(base, {
|
|
4079
|
+
onMessage: (direction, message) => {
|
|
4080
|
+
this.eventHandlers.onAcpOutputMessage?.(direction, message);
|
|
4081
|
+
this.eventHandlers.onAcpMessage?.(direction, message);
|
|
4082
|
+
},
|
|
4083
|
+
suppressReplaySessionUpdates: () => this.suppressReplaySessionUpdateMessages,
|
|
4084
|
+
bindPromptOwner: (owner) => this.bindPromptOwner(owner),
|
|
4085
|
+
onPromptRequestWritten: (active, owner) => this.onPromptRequestWritten(active, owner)
|
|
4086
|
+
});
|
|
4532
4087
|
}
|
|
4533
4088
|
async createSession(cwd = this.options.cwd) {
|
|
4534
4089
|
const connection = this.getConnection();
|
|
@@ -4537,7 +4092,7 @@ var AcpClient = class {
|
|
|
4537
4092
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
4538
4093
|
let result;
|
|
4539
4094
|
try {
|
|
4540
|
-
const createPromise = this.runConnectionRequest(() => connection.
|
|
4095
|
+
const createPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.new, {
|
|
4541
4096
|
cwd: sessionCwd,
|
|
4542
4097
|
mcpServers: this.options.mcpServers ?? [],
|
|
4543
4098
|
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
@@ -4557,9 +4112,10 @@ var AcpClient = class {
|
|
|
4557
4112
|
response: result
|
|
4558
4113
|
});
|
|
4559
4114
|
this.rememberSessionModels(result.sessionId, models);
|
|
4115
|
+
await this.captureAgentDescendants(this.agent);
|
|
4560
4116
|
return {
|
|
4561
4117
|
sessionId: result.sessionId,
|
|
4562
|
-
agentSessionId:
|
|
4118
|
+
agentSessionId: extractAgentSessionId(result._meta),
|
|
4563
4119
|
configOptions,
|
|
4564
4120
|
models,
|
|
4565
4121
|
configOptionsPresent: hasResponseField(result, "configOptions"),
|
|
@@ -4576,7 +4132,7 @@ var AcpClient = class {
|
|
|
4576
4132
|
const previousSuppression = this.applySessionUpdateSuppression(Boolean(options.suppressReplayUpdates));
|
|
4577
4133
|
let response;
|
|
4578
4134
|
try {
|
|
4579
|
-
response = await this.runConnectionRequest(() => connection.
|
|
4135
|
+
response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.load, {
|
|
4580
4136
|
sessionId,
|
|
4581
4137
|
cwd: sessionCwd,
|
|
4582
4138
|
mcpServers: this.options.mcpServers ?? []
|
|
@@ -4588,12 +4144,13 @@ var AcpClient = class {
|
|
|
4588
4144
|
this.loadedSessionId = sessionId;
|
|
4589
4145
|
const result = toReconnectedSessionResult(response);
|
|
4590
4146
|
this.updateRememberedSessionModels(sessionId, result);
|
|
4147
|
+
await this.captureAgentDescendants(this.agent);
|
|
4591
4148
|
return result;
|
|
4592
4149
|
}
|
|
4593
4150
|
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
4594
4151
|
const connection = this.getConnection();
|
|
4595
4152
|
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
|
4596
|
-
const response = await this.runConnectionRequest(() => connection.
|
|
4153
|
+
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.resume, {
|
|
4597
4154
|
sessionId,
|
|
4598
4155
|
cwd: sessionCwd,
|
|
4599
4156
|
mcpServers: this.options.mcpServers ?? []
|
|
@@ -4601,6 +4158,7 @@ var AcpClient = class {
|
|
|
4601
4158
|
this.loadedSessionId = sessionId;
|
|
4602
4159
|
const result = toReconnectedSessionResult(response);
|
|
4603
4160
|
this.updateRememberedSessionModels(sessionId, result);
|
|
4161
|
+
await this.captureAgentDescendants(this.agent);
|
|
4604
4162
|
return result;
|
|
4605
4163
|
}
|
|
4606
4164
|
applySessionUpdateSuppression(enabled) {
|
|
@@ -4616,14 +4174,15 @@ var AcpClient = class {
|
|
|
4616
4174
|
this.suppressSessionUpdates = previous.suppressSessionUpdates;
|
|
4617
4175
|
this.suppressReplaySessionUpdateMessages = previous.suppressReplaySessionUpdateMessages;
|
|
4618
4176
|
}
|
|
4619
|
-
async prompt(sessionId, prompt, onRequestWritten, onElicitation) {
|
|
4177
|
+
async prompt(sessionId, prompt, onRequestWritten, onElicitation, onPermissionRequest) {
|
|
4620
4178
|
const connection = this.getConnection();
|
|
4621
4179
|
const normalizedPrompt = this.normalizePromptForAgent(prompt);
|
|
4622
4180
|
const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
|
|
4623
|
-
const
|
|
4181
|
+
const previousActivePrompt = this.activePrompt;
|
|
4182
|
+
const activePrompt = this.beginActivePrompt(sessionId, onRequestWritten, onElicitation, onPermissionRequest);
|
|
4624
4183
|
let promptPromise;
|
|
4625
4184
|
try {
|
|
4626
|
-
promptPromise = this.runConnectionRequest(() => connection.prompt
|
|
4185
|
+
promptPromise = this.runConnectionRequest(() => connection.agent.request(methods.agent.session.prompt, {
|
|
4627
4186
|
sessionId,
|
|
4628
4187
|
prompt: normalizedPrompt
|
|
4629
4188
|
}));
|
|
@@ -4634,6 +4193,7 @@ var AcpClient = class {
|
|
|
4634
4193
|
}
|
|
4635
4194
|
activePrompt.promise = promptPromise;
|
|
4636
4195
|
try {
|
|
4196
|
+
previousActivePrompt?.elicitationController?.abort();
|
|
4637
4197
|
return this.returnPromptResponseOrPermissionFailure(sessionId, await promptPromise);
|
|
4638
4198
|
} catch (error) {
|
|
4639
4199
|
this.throwPromptPermissionFailureIfPresent(sessionId);
|
|
@@ -4641,23 +4201,19 @@ var AcpClient = class {
|
|
|
4641
4201
|
} finally {
|
|
4642
4202
|
restoreConsoleError?.();
|
|
4643
4203
|
this.clearActivePrompt(activePrompt);
|
|
4644
|
-
this.cancellingSessionIds.delete(sessionId);
|
|
4645
|
-
this.abortAndDropPermissionSignal(sessionId);
|
|
4646
|
-
this.promptPermissionFailures.delete(sessionId);
|
|
4647
4204
|
}
|
|
4648
4205
|
}
|
|
4649
|
-
beginActivePrompt(sessionId, onRequestWritten, elicitationHandler) {
|
|
4650
|
-
const previous = this.activePrompt;
|
|
4206
|
+
beginActivePrompt(sessionId, onRequestWritten, elicitationHandler, permissionHandler) {
|
|
4651
4207
|
this.cancellingSessionIds.delete(sessionId);
|
|
4652
4208
|
const active = {
|
|
4653
4209
|
sessionId,
|
|
4654
4210
|
onRequestWritten,
|
|
4655
4211
|
elicitationHandler,
|
|
4212
|
+
permissionHandler,
|
|
4656
4213
|
elicitationController: new AbortController()
|
|
4657
4214
|
};
|
|
4658
4215
|
this.activePrompt = active;
|
|
4659
4216
|
this.pendingPromptOwners.push(active);
|
|
4660
|
-
previous?.elicitationController?.abort();
|
|
4661
4217
|
return active;
|
|
4662
4218
|
}
|
|
4663
4219
|
bindPromptOwner(owner) {
|
|
@@ -4674,10 +4230,18 @@ var AcpClient = class {
|
|
|
4674
4230
|
} catch {}
|
|
4675
4231
|
}
|
|
4676
4232
|
clearActivePrompt(active) {
|
|
4677
|
-
if (this.activePrompt === active) this.activePrompt = void 0;
|
|
4678
4233
|
const pendingIndex = this.pendingPromptOwners.indexOf(active);
|
|
4234
|
+
const clearSession = !this.pendingPromptOwners.some((candidate, index) => index > pendingIndex && candidate.sessionId === active.sessionId);
|
|
4235
|
+
if (this.activePrompt === active) this.activePrompt = void 0;
|
|
4679
4236
|
if (pendingIndex >= 0) this.pendingPromptOwners.splice(pendingIndex, 1);
|
|
4237
|
+
let permissionController;
|
|
4238
|
+
if (clearSession) {
|
|
4239
|
+
this.cancellingSessionIds.delete(active.sessionId);
|
|
4240
|
+
permissionController = this.takePermissionAbortController(active.sessionId);
|
|
4241
|
+
this.promptPermissionFailures.delete(active.sessionId);
|
|
4242
|
+
}
|
|
4680
4243
|
active.elicitationController.abort();
|
|
4244
|
+
permissionController?.abort();
|
|
4681
4245
|
}
|
|
4682
4246
|
normalizePromptForAgent(prompt) {
|
|
4683
4247
|
const normalizedPrompt = typeof prompt === "string" ? textPrompt(prompt) : prompt;
|
|
@@ -4696,7 +4260,7 @@ var AcpClient = class {
|
|
|
4696
4260
|
async setSessionMode(sessionId, modeId) {
|
|
4697
4261
|
const connection = this.getConnection();
|
|
4698
4262
|
try {
|
|
4699
|
-
await this.runConnectionRequest(() => connection.
|
|
4263
|
+
await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setMode, {
|
|
4700
4264
|
sessionId,
|
|
4701
4265
|
modeId
|
|
4702
4266
|
}));
|
|
@@ -4707,7 +4271,7 @@ var AcpClient = class {
|
|
|
4707
4271
|
async setSessionConfigOption(sessionId, configId, value) {
|
|
4708
4272
|
const connection = this.getConnection();
|
|
4709
4273
|
try {
|
|
4710
|
-
return await this.runConnectionRequest(() => connection.
|
|
4274
|
+
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
4711
4275
|
sessionId,
|
|
4712
4276
|
configId,
|
|
4713
4277
|
value
|
|
@@ -4729,7 +4293,7 @@ var AcpClient = class {
|
|
|
4729
4293
|
async setSessionModelThroughConfig(sessionId, modelId, configId) {
|
|
4730
4294
|
const connection = this.getConnection();
|
|
4731
4295
|
try {
|
|
4732
|
-
const response = await this.runConnectionRequest(() => connection.
|
|
4296
|
+
const response = await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.setConfigOption, {
|
|
4733
4297
|
sessionId,
|
|
4734
4298
|
configId,
|
|
4735
4299
|
value: modelId
|
|
@@ -4743,7 +4307,7 @@ var AcpClient = class {
|
|
|
4743
4307
|
async setSessionModelThroughLegacyMethod(sessionId, modelId) {
|
|
4744
4308
|
const connection = this.getConnection();
|
|
4745
4309
|
try {
|
|
4746
|
-
await this.runConnectionRequest(() => connection.
|
|
4310
|
+
await this.runConnectionRequest(() => connection.agent.request("session/set_model", {
|
|
4747
4311
|
sessionId,
|
|
4748
4312
|
modelId
|
|
4749
4313
|
}));
|
|
@@ -4791,26 +4355,32 @@ var AcpClient = class {
|
|
|
4791
4355
|
}
|
|
4792
4356
|
async cancel(sessionId) {
|
|
4793
4357
|
const connection = this.getConnection();
|
|
4794
|
-
const active = this.activePrompt;
|
|
4795
|
-
|
|
4358
|
+
const active = this.activePrompt?.sessionId === sessionId ? this.activePrompt : void 0;
|
|
4359
|
+
const cancellation = active?.cancelPromise ?? this.runConnectionRequest(() => connection.agent.notify(methods.agent.session.cancel, { sessionId })).catch((error) => {
|
|
4360
|
+
if (active?.cancelPromise === cancellation) active.cancelPromise = void 0;
|
|
4361
|
+
throw error;
|
|
4362
|
+
});
|
|
4363
|
+
const permissionController = this.takePermissionAbortController(sessionId);
|
|
4364
|
+
if (active) {
|
|
4365
|
+
active.cancelPromise = cancellation;
|
|
4796
4366
|
this.cancellingSessionIds.add(sessionId);
|
|
4797
4367
|
active.elicitationController?.abort();
|
|
4798
4368
|
} else this.cancellingSessionIds.delete(sessionId);
|
|
4799
|
-
|
|
4800
|
-
await
|
|
4369
|
+
permissionController?.abort();
|
|
4370
|
+
await cancellation;
|
|
4801
4371
|
}
|
|
4802
4372
|
async closeSession(sessionId) {
|
|
4803
4373
|
const connection = this.getConnection();
|
|
4804
4374
|
this.cancellingSessionIds.add(sessionId);
|
|
4805
4375
|
if (this.activePrompt?.sessionId === sessionId) this.activePrompt.elicitationController?.abort();
|
|
4806
|
-
await this.runConnectionRequest(() => connection.
|
|
4376
|
+
await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.close, { sessionId }));
|
|
4807
4377
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
4808
4378
|
this.modelConfigIds.delete(sessionId);
|
|
4809
4379
|
this.legacyModelSessionIds.delete(sessionId);
|
|
4810
4380
|
}
|
|
4811
4381
|
async listSessions(params = {}) {
|
|
4812
4382
|
const connection = this.getConnection();
|
|
4813
|
-
return await this.runConnectionRequest(() => connection.
|
|
4383
|
+
return await this.runConnectionRequest(() => connection.agent.request(methods.agent.session.list, params));
|
|
4814
4384
|
}
|
|
4815
4385
|
async requestCancelActivePrompt() {
|
|
4816
4386
|
const active = this.activePrompt;
|
|
@@ -4870,18 +4440,36 @@ var AcpClient = class {
|
|
|
4870
4440
|
this.activePrompt?.elicitationController?.abort();
|
|
4871
4441
|
}
|
|
4872
4442
|
closeConnection() {
|
|
4873
|
-
this.connection?.close
|
|
4443
|
+
this.connection?.close();
|
|
4444
|
+
}
|
|
4445
|
+
async captureAgentDescendants(child) {
|
|
4446
|
+
const descendants = child && this.agentDescendants.get(child);
|
|
4447
|
+
if (descendants && !await descendants.capture()) this.log("could not verify agent descendants; skipping unverified process cleanup");
|
|
4448
|
+
}
|
|
4449
|
+
terminateAgentProcess(child) {
|
|
4450
|
+
let cleanup = this.agentCleanups.get(child);
|
|
4451
|
+
if (!cleanup) {
|
|
4452
|
+
cleanup = Promise.resolve().then(() => this.cleanupAgentProcess(child));
|
|
4453
|
+
this.agentCleanups.set(child, cleanup);
|
|
4454
|
+
}
|
|
4455
|
+
return cleanup;
|
|
4874
4456
|
}
|
|
4875
|
-
async
|
|
4457
|
+
async cleanupAgentProcess(child) {
|
|
4458
|
+
const descendants = this.agentDescendants.get(child);
|
|
4459
|
+
const deadline = Date.now() + AGENT_CLEANUP_BUDGET_MS;
|
|
4876
4460
|
const stdinCloseGraceMs = resolveAgentCloseAfterStdinEndMs(this.options.agentCommand);
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
this.
|
|
4882
|
-
|
|
4461
|
+
try {
|
|
4462
|
+
if (descendants) await descendants.capture(Math.max(1, deadline - Date.now()));
|
|
4463
|
+
this.endAgentStdin(child);
|
|
4464
|
+
await waitForChildExit(child, Math.min(stdinCloseGraceMs, Math.max(0, deadline - Date.now())));
|
|
4465
|
+
if (!await this.signalAgentAndDescendants(child, "SIGTERM", AGENT_CLOSE_TERM_GRACE_MS, deadline)) {
|
|
4466
|
+
this.log("agent processes did not exit after SIGTERM; forcing SIGKILL");
|
|
4467
|
+
await this.signalAgentAndDescendants(child, "SIGKILL", AGENT_CLOSE_KILL_GRACE_MS, deadline);
|
|
4468
|
+
}
|
|
4469
|
+
} finally {
|
|
4470
|
+
descendants?.retire();
|
|
4471
|
+
this.detachAgentHandles(child, isChildProcessRunning(child));
|
|
4883
4472
|
}
|
|
4884
|
-
this.detachAgentHandles(child, !exited);
|
|
4885
4473
|
}
|
|
4886
4474
|
endAgentStdin(child) {
|
|
4887
4475
|
if (child.stdin.destroyed) return;
|
|
@@ -4889,12 +4477,15 @@ var AcpClient = class {
|
|
|
4889
4477
|
child.stdin.end();
|
|
4890
4478
|
} catch {}
|
|
4891
4479
|
}
|
|
4892
|
-
async
|
|
4893
|
-
|
|
4894
|
-
|
|
4480
|
+
async signalAgentAndDescendants(child, signal, waitMs, deadline) {
|
|
4481
|
+
const descendants = this.agentDescendants.get(child);
|
|
4482
|
+
if (descendants && Date.now() < deadline) await descendants.signal(signal, deadline - Date.now());
|
|
4483
|
+
if (isChildProcessRunning(child)) try {
|
|
4895
4484
|
child.kill(signal);
|
|
4896
4485
|
} catch {}
|
|
4897
|
-
|
|
4486
|
+
const remaining = Math.min(waitMs, Math.max(0, deadline - Date.now()));
|
|
4487
|
+
const [exited, descendantsExited] = await Promise.all([waitForChildExit(child, remaining), descendants ? descendants.waitForExit(remaining) : Promise.resolve(true)]);
|
|
4488
|
+
return exited && descendantsExited;
|
|
4898
4489
|
}
|
|
4899
4490
|
detachAgentHandles(agent, unref) {
|
|
4900
4491
|
const stdin = agent.stdin;
|
|
@@ -5046,7 +4637,7 @@ var AcpClient = class {
|
|
|
5046
4637
|
this.log(`agent advertised auth methods [${authMethods.map((m) => m.id).join(", ")}] but no matching credentials found — skipping (agent may handle auth internally)`);
|
|
5047
4638
|
return;
|
|
5048
4639
|
}
|
|
5049
|
-
await connection.authenticate
|
|
4640
|
+
await connection.agent.request(methods.agent.authenticate, { methodId: selected.methodId });
|
|
5050
4641
|
this.log(`authenticated with method ${selected.methodId} (${selected.source})`);
|
|
5051
4642
|
}
|
|
5052
4643
|
async handlePermissionRequest(params) {
|
|
@@ -5065,7 +4656,7 @@ var AcpClient = class {
|
|
|
5065
4656
|
if ("response" in resolved) return resolved.response;
|
|
5066
4657
|
const { active, handler } = resolved.owner;
|
|
5067
4658
|
const signal = AbortSignal.any([requestSignal, active.elicitationController.signal]);
|
|
5068
|
-
const
|
|
4659
|
+
const outcome = await raceWithAbort(signal, Promise.resolve().then(async () => await handler(request, {
|
|
5069
4660
|
requestId,
|
|
5070
4661
|
signal
|
|
5071
4662
|
})).then((response) => ({
|
|
@@ -5074,10 +4665,9 @@ var AcpClient = class {
|
|
|
5074
4665
|
}), (error) => ({
|
|
5075
4666
|
kind: "error",
|
|
5076
4667
|
error
|
|
5077
|
-
}));
|
|
5078
|
-
const outcome = await Promise.race([handlerAttempt, waitForAbort(signal)]);
|
|
4668
|
+
})));
|
|
5079
4669
|
if (this.activePrompt !== active) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.inactive);
|
|
5080
|
-
if (outcome
|
|
4670
|
+
if (!outcome || signal.aborted) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.cancelled);
|
|
5081
4671
|
if (outcome.kind === "error" || !isKnownElicitationResponse(outcome.response)) return cancelledElicitationResponse(ELICITATION_CANCEL_MESSAGES.unavailable);
|
|
5082
4672
|
return outcome.response;
|
|
5083
4673
|
}
|
|
@@ -5104,15 +4694,31 @@ var AcpClient = class {
|
|
|
5104
4694
|
isElicitationSessionCancelling(sessionId) {
|
|
5105
4695
|
return this.closing || this.cancellingSessionIds.has(sessionId);
|
|
5106
4696
|
}
|
|
4697
|
+
resolvePermissionOwner(sessionId) {
|
|
4698
|
+
const active = this.activePrompt;
|
|
4699
|
+
const handler = active?.permissionHandler ?? this.options.onPermissionRequest;
|
|
4700
|
+
if (!handler) return;
|
|
4701
|
+
const signal = this.cancellationSignalForSession(sessionId);
|
|
4702
|
+
if (!active?.permissionHandler) return {
|
|
4703
|
+
handler,
|
|
4704
|
+
signal
|
|
4705
|
+
};
|
|
4706
|
+
return {
|
|
4707
|
+
handler,
|
|
4708
|
+
signal: active.sessionId === sessionId ? AbortSignal.any([signal, active.elicitationController.signal]) : AbortSignal.abort()
|
|
4709
|
+
};
|
|
4710
|
+
}
|
|
5107
4711
|
async tryHandlePermissionRequestWithHost(params) {
|
|
5108
|
-
|
|
5109
|
-
|
|
4712
|
+
const owner = this.resolvePermissionOwner(params.sessionId);
|
|
4713
|
+
if (!owner) return;
|
|
4714
|
+
const { handler, signal } = owner;
|
|
4715
|
+
if (signal.aborted) return this.hostPermissionDecisionResponse(params, signal, void 0);
|
|
5110
4716
|
try {
|
|
5111
|
-
const decision = await
|
|
4717
|
+
const decision = await raceWithAbort(signal, handler({
|
|
5112
4718
|
sessionId: params.sessionId,
|
|
5113
4719
|
raw: params,
|
|
5114
4720
|
inferredKind: inferToolKind(params)
|
|
5115
|
-
}, { signal });
|
|
4721
|
+
}, { signal }));
|
|
5116
4722
|
return this.hostPermissionDecisionResponse(params, signal, decision);
|
|
5117
4723
|
} catch (error) {
|
|
5118
4724
|
return this.hostPermissionErrorResponse(params, signal, error);
|
|
@@ -5124,9 +4730,9 @@ var AcpClient = class {
|
|
|
5124
4730
|
return cancelledPermissionResponse();
|
|
5125
4731
|
}
|
|
5126
4732
|
if (!decision) return;
|
|
5127
|
-
const response = decisionToResponse(params, decision);
|
|
4733
|
+
const response = decisionToResponse(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), decision);
|
|
5128
4734
|
this.recordPermissionDecision(classifyPermissionDecision(params, response));
|
|
5129
|
-
return response;
|
|
4735
|
+
return decision.outcome === "cancel" ? response : this.explainPermissionRefusal(params, response);
|
|
5130
4736
|
}
|
|
5131
4737
|
hostPermissionErrorResponse(params, signal, error) {
|
|
5132
4738
|
if (signal.aborted || this.cancellingSessionIds.has(params.sessionId)) {
|
|
@@ -5137,16 +4743,29 @@ var AcpClient = class {
|
|
|
5137
4743
|
}
|
|
5138
4744
|
async resolvePermissionRequestFromMode(params) {
|
|
5139
4745
|
try {
|
|
5140
|
-
const result = await resolvePermissionRequestWithDetails(params, this.options.permissionMode, this.options.nonInteractivePermissions ?? "deny", this.options.permissionPolicy);
|
|
4746
|
+
const result = await resolvePermissionRequestWithDetails(preferCodexPermissionRefusal(params, this.initResult?.agentInfo?.name), this.options.permissionMode, this.options.nonInteractivePermissions ?? "deny", this.options.permissionPolicy);
|
|
5141
4747
|
this.emitPermissionEscalation(result.escalation);
|
|
5142
4748
|
return {
|
|
5143
|
-
response: result.response,
|
|
4749
|
+
response: this.explainPermissionRefusal(params, result.response),
|
|
5144
4750
|
recorded: false
|
|
5145
4751
|
};
|
|
5146
4752
|
} catch (error) {
|
|
5147
4753
|
return this.handleModePermissionError(params.sessionId, error);
|
|
5148
4754
|
}
|
|
5149
4755
|
}
|
|
4756
|
+
explainPermissionRefusal(params, response) {
|
|
4757
|
+
const notice = codexPermissionNotice(params, response, this.initResult?.agentInfo?.name);
|
|
4758
|
+
if (!notice || this.cancellingSessionIds.has(params.sessionId)) return response;
|
|
4759
|
+
try {
|
|
4760
|
+
this.eventHandlers.onClientOperation?.({
|
|
4761
|
+
method: "session/request_permission",
|
|
4762
|
+
status: "completed",
|
|
4763
|
+
summary: notice,
|
|
4764
|
+
timestamp: isoNow$2()
|
|
4765
|
+
});
|
|
4766
|
+
} catch {}
|
|
4767
|
+
return withPermissionMetadata(response, { permissionNotice: notice });
|
|
4768
|
+
}
|
|
5150
4769
|
emitPermissionEscalation(escalation) {
|
|
5151
4770
|
if (escalation) this.eventHandlers.onPermissionEscalation?.(escalation);
|
|
5152
4771
|
}
|
|
@@ -5161,8 +4780,9 @@ var AcpClient = class {
|
|
|
5161
4780
|
}
|
|
5162
4781
|
attachAgentLifecycleObservers(child, startedProcess, exitNotificationBarrier) {
|
|
5163
4782
|
const onExit = (exitCode, signal) => {
|
|
5164
|
-
const exitedAt = isoNow$
|
|
5165
|
-
this.recordAgentExit("process_exit", exitCode, signal);
|
|
4783
|
+
const exitedAt = isoNow$2();
|
|
4784
|
+
this.recordAgentExit(child, "process_exit", exitCode, signal);
|
|
4785
|
+
this.terminateAgentProcess(child);
|
|
5166
4786
|
exitNotificationBarrier.then(() => {
|
|
5167
4787
|
this.notifyProcessExit(startedProcess, exitCode, signal, exitedAt);
|
|
5168
4788
|
});
|
|
@@ -5173,10 +4793,10 @@ var AcpClient = class {
|
|
|
5173
4793
|
onExit(child.exitCode, child.signalCode);
|
|
5174
4794
|
}
|
|
5175
4795
|
child.once("close", (exitCode, signal) => {
|
|
5176
|
-
this.recordAgentExit("process_close", exitCode, signal);
|
|
4796
|
+
this.recordAgentExit(child, "process_close", exitCode, signal);
|
|
5177
4797
|
});
|
|
5178
4798
|
child.stdout.once("close", () => {
|
|
5179
|
-
this.recordAgentExit("pipe_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
4799
|
+
this.recordAgentExit(child, "pipe_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
5180
4800
|
});
|
|
5181
4801
|
}
|
|
5182
4802
|
notifyProcessSpawnFailure(launch, error) {
|
|
@@ -5185,7 +4805,7 @@ var AcpClient = class {
|
|
|
5185
4805
|
const event = Object.freeze({
|
|
5186
4806
|
...launch,
|
|
5187
4807
|
error,
|
|
5188
|
-
failedAt: isoNow$
|
|
4808
|
+
failedAt: isoNow$2()
|
|
5189
4809
|
});
|
|
5190
4810
|
try {
|
|
5191
4811
|
Promise.resolve(handler(event)).catch((observerError) => {
|
|
@@ -5216,12 +4836,12 @@ var AcpClient = class {
|
|
|
5216
4836
|
const message = error instanceof Error ? error.message : String(error);
|
|
5217
4837
|
this.log(`process lifecycle ${hook} hook failed: ${message}`);
|
|
5218
4838
|
}
|
|
5219
|
-
recordAgentExit(reason, exitCode, signal) {
|
|
5220
|
-
if (this.lastAgentExit) return;
|
|
4839
|
+
recordAgentExit(child, reason, exitCode, signal) {
|
|
4840
|
+
if (this.agent !== child || this.lastAgentExit) return;
|
|
5221
4841
|
this.lastAgentExit = {
|
|
5222
4842
|
exitCode,
|
|
5223
4843
|
signal,
|
|
5224
|
-
exitedAt: isoNow$
|
|
4844
|
+
exitedAt: isoNow$2(),
|
|
5225
4845
|
reason,
|
|
5226
4846
|
unexpectedDuringPrompt: !this.closing && Boolean(this.activePrompt)
|
|
5227
4847
|
};
|
|
@@ -5275,6 +4895,7 @@ var AcpClient = class {
|
|
|
5275
4895
|
return await this.filesystem.readTextFile(params);
|
|
5276
4896
|
} catch (error) {
|
|
5277
4897
|
this.recordPermissionError(params.sessionId, error);
|
|
4898
|
+
if (error instanceof FsSafeError && error.code === "not-found" || error instanceof Error && "code" in error && error.code === "ENOENT") throw RequestError.resourceNotFound(pathToFileURL(params.path).href);
|
|
5278
4899
|
throw error;
|
|
5279
4900
|
}
|
|
5280
4901
|
}
|
|
@@ -5294,18 +4915,6 @@ var AcpClient = class {
|
|
|
5294
4915
|
throw error;
|
|
5295
4916
|
}
|
|
5296
4917
|
}
|
|
5297
|
-
async handleTerminalOutput(params) {
|
|
5298
|
-
return await this.terminalManager.terminalOutput(params);
|
|
5299
|
-
}
|
|
5300
|
-
async handleWaitForTerminalExit(params) {
|
|
5301
|
-
return await this.terminalManager.waitForTerminalExit(params);
|
|
5302
|
-
}
|
|
5303
|
-
async handleKillTerminal(params) {
|
|
5304
|
-
return await this.terminalManager.killTerminal(params);
|
|
5305
|
-
}
|
|
5306
|
-
async handleReleaseTerminal(params) {
|
|
5307
|
-
return await this.terminalManager.releaseTerminal(params);
|
|
5308
|
-
}
|
|
5309
4918
|
cancellationSignalForSession(sessionId) {
|
|
5310
4919
|
let controller = this.permissionAbortControllers.get(sessionId);
|
|
5311
4920
|
if (!controller) {
|
|
@@ -5314,12 +4923,10 @@ var AcpClient = class {
|
|
|
5314
4923
|
}
|
|
5315
4924
|
return controller.signal;
|
|
5316
4925
|
}
|
|
5317
|
-
|
|
4926
|
+
takePermissionAbortController(sessionId) {
|
|
5318
4927
|
const controller = this.permissionAbortControllers.get(sessionId);
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
this.permissionAbortControllers.delete(sessionId);
|
|
5322
|
-
}
|
|
4928
|
+
this.permissionAbortControllers.delete(sessionId);
|
|
4929
|
+
return controller;
|
|
5323
4930
|
}
|
|
5324
4931
|
recordPermissionDecision(decision) {
|
|
5325
4932
|
this.permissionStats.requested += 1;
|
|
@@ -5382,132 +4989,6 @@ var AcpClient = class {
|
|
|
5382
4989
|
}
|
|
5383
4990
|
};
|
|
5384
4991
|
//#endregion
|
|
5385
|
-
//#region src/runtime/engine/lifecycle.ts
|
|
5386
|
-
function applyLifecycleSnapshotToRecord(record, snapshot) {
|
|
5387
|
-
if (!snapshot) return;
|
|
5388
|
-
record.pid = snapshot.running ? snapshot.pid : void 0;
|
|
5389
|
-
record.agentStartedAt = snapshot.startedAt;
|
|
5390
|
-
if (snapshot.lastExit) {
|
|
5391
|
-
record.lastAgentExitCode = snapshot.lastExit.exitCode;
|
|
5392
|
-
record.lastAgentExitSignal = snapshot.lastExit.signal;
|
|
5393
|
-
record.lastAgentExitAt = snapshot.lastExit.exitedAt;
|
|
5394
|
-
record.lastAgentDisconnectReason = snapshot.lastExit.reason;
|
|
5395
|
-
return;
|
|
5396
|
-
}
|
|
5397
|
-
record.lastAgentExitCode = void 0;
|
|
5398
|
-
record.lastAgentExitSignal = void 0;
|
|
5399
|
-
record.lastAgentExitAt = void 0;
|
|
5400
|
-
record.lastAgentDisconnectReason = void 0;
|
|
5401
|
-
}
|
|
5402
|
-
function reconcileAgentSessionId(record, agentSessionId) {
|
|
5403
|
-
const normalized = normalizeRuntimeSessionId(agentSessionId);
|
|
5404
|
-
if (!normalized) return;
|
|
5405
|
-
record.agentSessionId = normalized;
|
|
5406
|
-
}
|
|
5407
|
-
function sessionHasAgentMessages(recordOrConversation) {
|
|
5408
|
-
return recordOrConversation.messages.some((message) => typeof message === "object" && message !== null && "Agent" in message);
|
|
5409
|
-
}
|
|
5410
|
-
function applyConversation(record, conversation) {
|
|
5411
|
-
record.title = conversation.title;
|
|
5412
|
-
record.updated_at = conversation.updated_at;
|
|
5413
|
-
record.messages = conversation.messages;
|
|
5414
|
-
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
|
5415
|
-
record.cumulative_cost = conversation.cumulative_cost;
|
|
5416
|
-
record.request_token_usage = conversation.request_token_usage;
|
|
5417
|
-
}
|
|
5418
|
-
//#endregion
|
|
5419
|
-
//#region src/runtime/engine/session-options.ts
|
|
5420
|
-
function mergeSessionOptions(preferred, fallback) {
|
|
5421
|
-
const merged = { ...fallback };
|
|
5422
|
-
assignDefinedOption(merged, "model", preferred?.model);
|
|
5423
|
-
assignDefinedOption(merged, "allowedTools", preferred?.allowedTools);
|
|
5424
|
-
assignDefinedOption(merged, "maxTurns", preferred?.maxTurns);
|
|
5425
|
-
assignDefinedOption(merged, "systemPrompt", preferred?.systemPrompt);
|
|
5426
|
-
assignDefinedOption(merged, "env", mergeEnvRecords(fallback?.env, preferred?.env));
|
|
5427
|
-
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
5428
|
-
}
|
|
5429
|
-
function mergeEnvRecords(fallback, preferred) {
|
|
5430
|
-
if (!fallback && !preferred) return;
|
|
5431
|
-
return {
|
|
5432
|
-
...fallback,
|
|
5433
|
-
...preferred
|
|
5434
|
-
};
|
|
5435
|
-
}
|
|
5436
|
-
function assignDefinedOption(target, key, value) {
|
|
5437
|
-
if (value !== void 0) target[key] = value;
|
|
5438
|
-
}
|
|
5439
|
-
function persistSessionOptions(record, options) {
|
|
5440
|
-
const next = options === void 0 ? void 0 : persistedSessionOptions(options);
|
|
5441
|
-
if (next !== void 0) {
|
|
5442
|
-
record.acpx = {
|
|
5443
|
-
...record.acpx,
|
|
5444
|
-
session_options: next
|
|
5445
|
-
};
|
|
5446
|
-
return;
|
|
5447
|
-
}
|
|
5448
|
-
if (!record.acpx) return;
|
|
5449
|
-
delete record.acpx.session_options;
|
|
5450
|
-
}
|
|
5451
|
-
function sessionOptionsFromRecord(record) {
|
|
5452
|
-
const stored = record.acpx?.session_options;
|
|
5453
|
-
if (!stored) return;
|
|
5454
|
-
const sessionOptions = {};
|
|
5455
|
-
assignStoredOption(sessionOptions, "model", nonEmptyString(stored.model));
|
|
5456
|
-
assignStoredOption(sessionOptions, "allowedTools", storedAllowedTools(stored.allowed_tools));
|
|
5457
|
-
assignStoredOption(sessionOptions, "maxTurns", storedMaxTurns(stored.max_turns));
|
|
5458
|
-
assignStoredOption(sessionOptions, "systemPrompt", storedSystemPromptOption(stored.system_prompt));
|
|
5459
|
-
assignStoredOption(sessionOptions, "env", storedEnvRecord(stored.env));
|
|
5460
|
-
return Object.keys(sessionOptions).length > 0 ? sessionOptions : void 0;
|
|
5461
|
-
}
|
|
5462
|
-
function persistedSessionOptions(options) {
|
|
5463
|
-
const next = {
|
|
5464
|
-
model: nonEmptyString(options.model),
|
|
5465
|
-
allowed_tools: Array.isArray(options.allowedTools) ? [...options.allowedTools] : void 0,
|
|
5466
|
-
max_turns: typeof options.maxTurns === "number" ? options.maxTurns : void 0,
|
|
5467
|
-
system_prompt: normalizeSystemPromptOption(options.systemPrompt),
|
|
5468
|
-
env: storedEnvRecord(options.env)
|
|
5469
|
-
};
|
|
5470
|
-
return hasPersistedSessionOptions(next) ? next : void 0;
|
|
5471
|
-
}
|
|
5472
|
-
function hasPersistedSessionOptions(options) {
|
|
5473
|
-
return options.model !== void 0 || options.allowed_tools !== void 0 || options.max_turns !== void 0 || options.system_prompt !== void 0 || options.env !== void 0;
|
|
5474
|
-
}
|
|
5475
|
-
function storedEnvRecord(value) {
|
|
5476
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return;
|
|
5477
|
-
const entries = Object.entries(value);
|
|
5478
|
-
const result = {};
|
|
5479
|
-
for (const [key, raw] of entries) {
|
|
5480
|
-
if (typeof raw !== "string") continue;
|
|
5481
|
-
result[key] = raw;
|
|
5482
|
-
}
|
|
5483
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
5484
|
-
}
|
|
5485
|
-
function normalizeSystemPromptOption(value) {
|
|
5486
|
-
const prompt = nonEmptyString(value);
|
|
5487
|
-
if (prompt !== void 0) return prompt;
|
|
5488
|
-
const append = appendedSystemPrompt(value);
|
|
5489
|
-
return append === void 0 ? void 0 : { append };
|
|
5490
|
-
}
|
|
5491
|
-
function appendedSystemPrompt(value) {
|
|
5492
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
5493
|
-
return nonEmptyString(value.append);
|
|
5494
|
-
}
|
|
5495
|
-
function assignStoredOption(target, key, value) {
|
|
5496
|
-
assignDefinedOption(target, key, value);
|
|
5497
|
-
}
|
|
5498
|
-
function storedAllowedTools(value) {
|
|
5499
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string") ? [...value] : void 0;
|
|
5500
|
-
}
|
|
5501
|
-
function storedMaxTurns(value) {
|
|
5502
|
-
return typeof value === "number" ? value : void 0;
|
|
5503
|
-
}
|
|
5504
|
-
function storedSystemPromptOption(value) {
|
|
5505
|
-
return normalizeSystemPromptOption(value);
|
|
5506
|
-
}
|
|
5507
|
-
function nonEmptyString(value) {
|
|
5508
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
5509
|
-
}
|
|
5510
|
-
//#endregion
|
|
5511
4992
|
//#region src/session/model-state.ts
|
|
5512
4993
|
function configOptionsAreAuthoritative(state) {
|
|
5513
4994
|
return state.model_control === "config_option";
|
|
@@ -5518,7 +4999,7 @@ function legacyModelState(state) {
|
|
|
5518
4999
|
currentModelId: state.current_model_id ?? "",
|
|
5519
5000
|
availableModels: state.available_models.map((modelId) => ({
|
|
5520
5001
|
modelId,
|
|
5521
|
-
name: modelId
|
|
5002
|
+
name: state.available_model_names && Object.hasOwn(state.available_model_names, modelId) ? state.available_model_names[modelId] : modelId
|
|
5522
5003
|
}))
|
|
5523
5004
|
};
|
|
5524
5005
|
}
|
|
@@ -5532,11 +5013,13 @@ function advertisedModelState(state) {
|
|
|
5532
5013
|
function applyAdvertisedModelState(state, models) {
|
|
5533
5014
|
state.current_model_id = models.currentModelId;
|
|
5534
5015
|
state.available_models = models.availableModels.map((model) => model.modelId);
|
|
5016
|
+
state.available_model_names = Object.fromEntries(models.availableModels.map((model) => [model.modelId, model.name]));
|
|
5535
5017
|
state.model_control = models.configId ? "config_option" : "legacy_set_model";
|
|
5536
5018
|
}
|
|
5537
5019
|
function clearAdvertisedModelState(state) {
|
|
5538
5020
|
delete state.current_model_id;
|
|
5539
5021
|
delete state.available_models;
|
|
5022
|
+
delete state.available_model_names;
|
|
5540
5023
|
delete state.model_control;
|
|
5541
5024
|
}
|
|
5542
5025
|
function removeModelConfigOptions(state) {
|
|
@@ -5890,6 +5373,7 @@ function cloneSessionAcpxState(state) {
|
|
|
5890
5373
|
desired_config_options: state.desired_config_options ? { ...state.desired_config_options } : void 0,
|
|
5891
5374
|
current_model_id: state.current_model_id,
|
|
5892
5375
|
available_models: state.available_models ? [...state.available_models] : void 0,
|
|
5376
|
+
...state.available_model_names ? { available_model_names: { ...state.available_model_names } } : {},
|
|
5893
5377
|
model_control: state.model_control,
|
|
5894
5378
|
available_commands: state.available_commands ? state.available_commands.map((command) => ({ ...command })) : void 0,
|
|
5895
5379
|
config_options: state.config_options ? deepClone(state.config_options) : void 0,
|
|
@@ -5909,10 +5393,10 @@ function cloneSessionOptions(options) {
|
|
|
5909
5393
|
function cloneSystemPromptOption(option) {
|
|
5910
5394
|
return typeof option === "string" ? option : { append: option.append };
|
|
5911
5395
|
}
|
|
5912
|
-
function recordPromptSubmission(conversation, prompt, timestamp = isoNow()) {
|
|
5396
|
+
function recordPromptSubmission(conversation, prompt, timestamp = isoNow(), messageId) {
|
|
5913
5397
|
const userContent = (typeof prompt === "string" ? textPrompt(prompt) : prompt).map((content) => contentToUserContent(content)).filter((content) => content !== void 0);
|
|
5914
5398
|
if (userContent.length === 0) return;
|
|
5915
|
-
const promptMessageId = nextUserMessageId();
|
|
5399
|
+
const promptMessageId = messageId ?? nextUserMessageId();
|
|
5916
5400
|
conversation.messages.push({ User: {
|
|
5917
5401
|
id: promptMessageId,
|
|
5918
5402
|
content: userContent.map((content) => {
|
|
@@ -5938,10 +5422,10 @@ function hasAgentReplyAfterPrompt(conversation, promptMessageId) {
|
|
|
5938
5422
|
}
|
|
5939
5423
|
return false;
|
|
5940
5424
|
}
|
|
5941
|
-
function recordSessionUpdate(conversation, state, notification, timestamp = isoNow()) {
|
|
5425
|
+
function recordSessionUpdate(conversation, state, notification, timestamp = isoNow(), userMessageId) {
|
|
5942
5426
|
const acpx = ensureAcpxState$1(state);
|
|
5943
5427
|
const update = notification.update;
|
|
5944
|
-
applySessionUpdate(conversation, acpx, update);
|
|
5428
|
+
applySessionUpdate(conversation, acpx, update, userMessageId);
|
|
5945
5429
|
updateConversationTimestamp(conversation, timestamp);
|
|
5946
5430
|
trimConversationForRuntime(conversation);
|
|
5947
5431
|
return acpx;
|
|
@@ -5954,13 +5438,13 @@ function recordPromptResponseUsage(conversation, usage, promptMessageId, timesta
|
|
|
5954
5438
|
trimConversationForRuntime(conversation);
|
|
5955
5439
|
return true;
|
|
5956
5440
|
}
|
|
5957
|
-
function applySessionUpdate(conversation, acpx, update) {
|
|
5441
|
+
function applySessionUpdate(conversation, acpx, update, userMessageId) {
|
|
5958
5442
|
const handler = SESSION_UPDATE_HANDLERS[update.sessionUpdate];
|
|
5959
|
-
handler?.(conversation, acpx, update);
|
|
5443
|
+
handler?.(conversation, acpx, update, userMessageId);
|
|
5960
5444
|
}
|
|
5961
5445
|
const SESSION_UPDATE_HANDLERS = {
|
|
5962
|
-
user_message_chunk: (conversation, _acpx, update) => {
|
|
5963
|
-
if (update.sessionUpdate === "user_message_chunk") appendUserMessageChunk(conversation, update.content);
|
|
5446
|
+
user_message_chunk: (conversation, _acpx, update, userMessageId) => {
|
|
5447
|
+
if (update.sessionUpdate === "user_message_chunk") appendUserMessageChunk(conversation, update.content, userMessageId);
|
|
5964
5448
|
},
|
|
5965
5449
|
agent_message_chunk: (conversation, _acpx, update) => {
|
|
5966
5450
|
if (update.sessionUpdate === "agent_message_chunk") appendAgentMessageChunk(conversation, update.content, appendAgentText);
|
|
@@ -5990,11 +5474,11 @@ const SESSION_UPDATE_HANDLERS = {
|
|
|
5990
5474
|
if (update.sessionUpdate === "config_option_update") applyConfigOptionsModelState(acpx, deepClone(update.configOptions));
|
|
5991
5475
|
}
|
|
5992
5476
|
};
|
|
5993
|
-
function appendUserMessageChunk(conversation, content) {
|
|
5477
|
+
function appendUserMessageChunk(conversation, content, messageId) {
|
|
5994
5478
|
const userContent = contentToUserContent(content);
|
|
5995
5479
|
if (!userContent) return;
|
|
5996
5480
|
conversation.messages.push({ User: {
|
|
5997
|
-
id: nextUserMessageId(),
|
|
5481
|
+
id: messageId ?? nextUserMessageId(),
|
|
5998
5482
|
content: [userContent]
|
|
5999
5483
|
} });
|
|
6000
5484
|
}
|
|
@@ -6018,7 +5502,7 @@ function applySessionInfoUpdate(conversation, update) {
|
|
|
6018
5502
|
if (hasOwn(update, "title")) conversation.title = update.title ?? null;
|
|
6019
5503
|
if (hasOwn(update, "updatedAt")) conversation.updated_at = update.updatedAt ?? conversation.updated_at;
|
|
6020
5504
|
}
|
|
6021
|
-
function recordClientOperation(conversation, state,
|
|
5505
|
+
function recordClientOperation(conversation, state, _operation, timestamp = isoNow()) {
|
|
6022
5506
|
const acpx = ensureAcpxState$1(state);
|
|
6023
5507
|
updateConversationTimestamp(conversation, timestamp);
|
|
6024
5508
|
trimConversationForRuntime(conversation);
|
|
@@ -6057,6 +5541,147 @@ function trimRuntimeToolResult(result) {
|
|
|
6057
5541
|
if (typeof result.output === "string") result.output = trimRuntimeText(result.output, MAX_RUNTIME_TOOL_IO_CHARS);
|
|
6058
5542
|
}
|
|
6059
5543
|
//#endregion
|
|
5544
|
+
//#region src/runtime/engine/lifecycle.ts
|
|
5545
|
+
function createInitialSessionRecord(params) {
|
|
5546
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5547
|
+
return {
|
|
5548
|
+
schema: "acpx.session.v1",
|
|
5549
|
+
acpxRecordId: params.recordId,
|
|
5550
|
+
acpSessionId: params.sessionId,
|
|
5551
|
+
agentSessionId: params.agentSessionId,
|
|
5552
|
+
agentCommand: params.agentCommand,
|
|
5553
|
+
agentArgv: params.agentArgv,
|
|
5554
|
+
cwd: params.cwd,
|
|
5555
|
+
name: params.name,
|
|
5556
|
+
createdAt: now,
|
|
5557
|
+
lastUsedAt: now,
|
|
5558
|
+
lastSeq: 0,
|
|
5559
|
+
eventLog: defaultSessionEventLog(params.recordId),
|
|
5560
|
+
closed: false,
|
|
5561
|
+
closedAt: void 0,
|
|
5562
|
+
...createSessionConversation(now),
|
|
5563
|
+
acpx: {}
|
|
5564
|
+
};
|
|
5565
|
+
}
|
|
5566
|
+
function applyLifecycleSnapshotToRecord(record, snapshot) {
|
|
5567
|
+
if (!snapshot) return;
|
|
5568
|
+
record.pid = snapshot.running ? snapshot.pid : void 0;
|
|
5569
|
+
record.agentStartedAt = snapshot.startedAt;
|
|
5570
|
+
if (snapshot.lastExit) {
|
|
5571
|
+
record.lastAgentExitCode = snapshot.lastExit.exitCode;
|
|
5572
|
+
record.lastAgentExitSignal = snapshot.lastExit.signal;
|
|
5573
|
+
record.lastAgentExitAt = snapshot.lastExit.exitedAt;
|
|
5574
|
+
record.lastAgentDisconnectReason = snapshot.lastExit.reason;
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
record.lastAgentExitCode = void 0;
|
|
5578
|
+
record.lastAgentExitSignal = void 0;
|
|
5579
|
+
record.lastAgentExitAt = void 0;
|
|
5580
|
+
record.lastAgentDisconnectReason = void 0;
|
|
5581
|
+
}
|
|
5582
|
+
function reconcileAgentSessionId(record, agentSessionId) {
|
|
5583
|
+
const normalized = normalizeAgentSessionId(agentSessionId);
|
|
5584
|
+
if (!normalized) return;
|
|
5585
|
+
record.agentSessionId = normalized;
|
|
5586
|
+
}
|
|
5587
|
+
function sessionHasAgentMessages(recordOrConversation) {
|
|
5588
|
+
return recordOrConversation.messages.some((message) => typeof message === "object" && message !== null && "Agent" in message);
|
|
5589
|
+
}
|
|
5590
|
+
function applyConversation(record, conversation) {
|
|
5591
|
+
record.title = conversation.title;
|
|
5592
|
+
record.updated_at = conversation.updated_at;
|
|
5593
|
+
record.messages = conversation.messages;
|
|
5594
|
+
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
|
5595
|
+
record.cumulative_cost = conversation.cumulative_cost;
|
|
5596
|
+
record.request_token_usage = conversation.request_token_usage;
|
|
5597
|
+
}
|
|
5598
|
+
//#endregion
|
|
5599
|
+
//#region src/runtime/engine/session-options.ts
|
|
5600
|
+
function mergeSessionOptions(preferred, fallback) {
|
|
5601
|
+
const merged = { ...fallback };
|
|
5602
|
+
assignDefinedOption(merged, "model", preferred?.model);
|
|
5603
|
+
assignDefinedOption(merged, "allowedTools", preferred?.allowedTools);
|
|
5604
|
+
assignDefinedOption(merged, "maxTurns", preferred?.maxTurns);
|
|
5605
|
+
assignDefinedOption(merged, "systemPrompt", preferred?.systemPrompt);
|
|
5606
|
+
assignDefinedOption(merged, "env", mergeEnvRecords(fallback?.env, preferred?.env));
|
|
5607
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
5608
|
+
}
|
|
5609
|
+
function mergeEnvRecords(fallback, preferred) {
|
|
5610
|
+
if (!fallback && !preferred) return;
|
|
5611
|
+
return {
|
|
5612
|
+
...fallback,
|
|
5613
|
+
...preferred
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
function assignDefinedOption(target, key, value) {
|
|
5617
|
+
if (value !== void 0) target[key] = value;
|
|
5618
|
+
}
|
|
5619
|
+
function persistSessionOptions(record, options) {
|
|
5620
|
+
const next = options === void 0 ? void 0 : persistedSessionOptions(options);
|
|
5621
|
+
if (next !== void 0) {
|
|
5622
|
+
record.acpx = {
|
|
5623
|
+
...record.acpx,
|
|
5624
|
+
session_options: next
|
|
5625
|
+
};
|
|
5626
|
+
return;
|
|
5627
|
+
}
|
|
5628
|
+
if (!record.acpx) return;
|
|
5629
|
+
delete record.acpx.session_options;
|
|
5630
|
+
}
|
|
5631
|
+
function sessionOptionsFromRecord(record) {
|
|
5632
|
+
const stored = record.acpx?.session_options;
|
|
5633
|
+
if (!stored) return;
|
|
5634
|
+
const sessionOptions = {};
|
|
5635
|
+
assignDefinedOption(sessionOptions, "model", nonEmptyString(stored.model));
|
|
5636
|
+
assignDefinedOption(sessionOptions, "allowedTools", storedAllowedTools(stored.allowed_tools));
|
|
5637
|
+
assignDefinedOption(sessionOptions, "maxTurns", storedMaxTurns(stored.max_turns));
|
|
5638
|
+
assignDefinedOption(sessionOptions, "systemPrompt", normalizeSystemPromptOption(stored.system_prompt));
|
|
5639
|
+
assignDefinedOption(sessionOptions, "env", storedEnvRecord(stored.env));
|
|
5640
|
+
return Object.keys(sessionOptions).length > 0 ? sessionOptions : void 0;
|
|
5641
|
+
}
|
|
5642
|
+
function persistedSessionOptions(options) {
|
|
5643
|
+
const next = {
|
|
5644
|
+
model: nonEmptyString(options.model),
|
|
5645
|
+
allowed_tools: Array.isArray(options.allowedTools) ? [...options.allowedTools] : void 0,
|
|
5646
|
+
max_turns: typeof options.maxTurns === "number" ? options.maxTurns : void 0,
|
|
5647
|
+
system_prompt: normalizeSystemPromptOption(options.systemPrompt),
|
|
5648
|
+
env: storedEnvRecord(options.env)
|
|
5649
|
+
};
|
|
5650
|
+
return hasPersistedSessionOptions(next) ? next : void 0;
|
|
5651
|
+
}
|
|
5652
|
+
function hasPersistedSessionOptions(options) {
|
|
5653
|
+
return options.model !== void 0 || options.allowed_tools !== void 0 || options.max_turns !== void 0 || options.system_prompt !== void 0 || options.env !== void 0;
|
|
5654
|
+
}
|
|
5655
|
+
function storedEnvRecord(value) {
|
|
5656
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return;
|
|
5657
|
+
const entries = Object.entries(value);
|
|
5658
|
+
const result = {};
|
|
5659
|
+
for (const [key, raw] of entries) {
|
|
5660
|
+
if (typeof raw !== "string") continue;
|
|
5661
|
+
result[key] = raw;
|
|
5662
|
+
}
|
|
5663
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
5664
|
+
}
|
|
5665
|
+
function normalizeSystemPromptOption(value) {
|
|
5666
|
+
const prompt = nonEmptyString(value);
|
|
5667
|
+
if (prompt !== void 0) return prompt;
|
|
5668
|
+
const append = appendedSystemPrompt(value);
|
|
5669
|
+
return append === void 0 ? void 0 : { append };
|
|
5670
|
+
}
|
|
5671
|
+
function appendedSystemPrompt(value) {
|
|
5672
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
5673
|
+
return nonEmptyString(value.append);
|
|
5674
|
+
}
|
|
5675
|
+
function storedAllowedTools(value) {
|
|
5676
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? [...value] : void 0;
|
|
5677
|
+
}
|
|
5678
|
+
function storedMaxTurns(value) {
|
|
5679
|
+
return typeof value === "number" ? value : void 0;
|
|
5680
|
+
}
|
|
5681
|
+
function nonEmptyString(value) {
|
|
5682
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
5683
|
+
}
|
|
5684
|
+
//#endregion
|
|
6060
5685
|
//#region src/session/mode-preference.ts
|
|
6061
5686
|
function ensureAcpxState(state) {
|
|
6062
5687
|
return state ?? {};
|
|
@@ -6147,6 +5772,11 @@ function applyConfigOptionsToRecord(record, result) {
|
|
|
6147
5772
|
if (!configOptions) return;
|
|
6148
5773
|
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
6149
5774
|
}
|
|
5775
|
+
function applyInitialModelSelection(record, originalModels, requestedModel, modelApplication) {
|
|
5776
|
+
applyConfigOptionsToRecord(record, modelApplication.response);
|
|
5777
|
+
syncAdvertisedModelState(record, modelApplication.response ? modelStateFromConfigOptions(modelApplication.response.configOptions) : originalModels);
|
|
5778
|
+
if (modelApplication.applied) setCurrentModelId(record, currentModelIdFromSetModelResponse(modelApplication.response, requestedModel));
|
|
5779
|
+
}
|
|
6150
5780
|
function applyAcceptedConfigOptions(state, response) {
|
|
6151
5781
|
const next = cloneSessionAcpxState(state) ?? {};
|
|
6152
5782
|
if (!response) return next;
|
|
@@ -6181,15 +5811,6 @@ function applyConfigOptionSelection(state, configId, value, response, modelConfi
|
|
|
6181
5811
|
}
|
|
6182
5812
|
//#endregion
|
|
6183
5813
|
//#region src/runtime/engine/reconnect.ts
|
|
6184
|
-
function isProcessAlive(pid) {
|
|
6185
|
-
if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
6186
|
-
try {
|
|
6187
|
-
process.kill(pid, 0);
|
|
6188
|
-
return true;
|
|
6189
|
-
} catch {
|
|
6190
|
-
return false;
|
|
6191
|
-
}
|
|
6192
|
-
}
|
|
6193
5814
|
const SESSION_LOAD_UNSUPPORTED_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
6194
5815
|
function shouldFallbackToNewSession(error, record) {
|
|
6195
5816
|
if (isHardReconnectFailure(error)) return false;
|
|
@@ -6307,12 +5928,6 @@ async function connectAndLoadSession(options) {
|
|
|
6307
5928
|
record.closed = false;
|
|
6308
5929
|
record.closedAt = void 0;
|
|
6309
5930
|
options.onConnectedRecord?.(record);
|
|
6310
|
-
let resumed = false;
|
|
6311
|
-
let loadError;
|
|
6312
|
-
let sessionId = record.acpSessionId;
|
|
6313
|
-
let createdFreshSession = false;
|
|
6314
|
-
let pendingAgentSessionId = record.agentSessionId;
|
|
6315
|
-
let sessionModels;
|
|
6316
5931
|
const loadState = await loadOrCreateRuntimeSession({
|
|
6317
5932
|
client,
|
|
6318
5933
|
record,
|
|
@@ -6320,12 +5935,7 @@ async function connectAndLoadSession(options) {
|
|
|
6320
5935
|
sameSessionOnly,
|
|
6321
5936
|
timeoutMs: options.timeoutMs
|
|
6322
5937
|
});
|
|
6323
|
-
resumed = loadState
|
|
6324
|
-
loadError = loadState.loadError;
|
|
6325
|
-
sessionId = loadState.sessionId;
|
|
6326
|
-
createdFreshSession = loadState.createdFreshSession;
|
|
6327
|
-
pendingAgentSessionId = loadState.pendingAgentSessionId;
|
|
6328
|
-
sessionModels = loadState.sessionModels;
|
|
5938
|
+
const { resumed, loadError, sessionId, createdFreshSession, pendingAgentSessionId, sessionModels } = loadState;
|
|
6329
5939
|
const preferenceReplay = await replaySessionPreferences({
|
|
6330
5940
|
client,
|
|
6331
5941
|
record,
|
|
@@ -6463,35 +6073,17 @@ async function loadOrCreateRuntimeSession(params) {
|
|
|
6463
6073
|
resumed: true,
|
|
6464
6074
|
createdFreshSession: false
|
|
6465
6075
|
};
|
|
6466
|
-
if (params.client.supportsResumeSession()) return await
|
|
6467
|
-
if (params.client.supportsLoadSession()) return await loadRuntimeSession(params);
|
|
6076
|
+
if (params.client.supportsResumeSession()) return await loadRuntimeSession(params, true);
|
|
6077
|
+
if (params.client.supportsLoadSession()) return await loadRuntimeSession(params, false);
|
|
6468
6078
|
if (params.sameSessionOnly) throw makeSessionResumeRequiredError({
|
|
6469
6079
|
record: params.record,
|
|
6470
6080
|
reason: "agent does not support session/resume or session/load"
|
|
6471
6081
|
});
|
|
6472
6082
|
return await createFreshRuntimeSession(params.client, params.record, params.timeoutMs);
|
|
6473
6083
|
}
|
|
6474
|
-
async function
|
|
6475
|
-
try {
|
|
6476
|
-
const resumeResult = await withTimeout(params.client.resumeSession(params.record.acpSessionId, params.record.cwd), params.timeoutMs);
|
|
6477
|
-
reconcileAgentSessionId(params.record, resumeResult.agentSessionId);
|
|
6478
|
-
applyConfigOptionsToRecord(params.record, resumeResult);
|
|
6479
|
-
return {
|
|
6480
|
-
sessionId: params.record.acpSessionId,
|
|
6481
|
-
pendingAgentSessionId: params.record.agentSessionId,
|
|
6482
|
-
sessionModels: resumeResult.models,
|
|
6483
|
-
configOptionsPresent: resumeResult.configOptionsPresent,
|
|
6484
|
-
legacyModelMetadataPresent: resumeResult.legacyModelMetadataPresent,
|
|
6485
|
-
resumed: true,
|
|
6486
|
-
createdFreshSession: false
|
|
6487
|
-
};
|
|
6488
|
-
} catch (error) {
|
|
6489
|
-
return await recoverRuntimeSessionLoadFailure(params, error);
|
|
6490
|
-
}
|
|
6491
|
-
}
|
|
6492
|
-
async function loadRuntimeSession(params) {
|
|
6084
|
+
async function loadRuntimeSession(params, resume) {
|
|
6493
6085
|
try {
|
|
6494
|
-
const loadResult = await withTimeout(params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, { suppressReplayUpdates: true }), params.timeoutMs);
|
|
6086
|
+
const loadResult = await withTimeout(resume ? params.client.resumeSession(params.record.acpSessionId, params.record.cwd) : params.client.loadSessionWithOptions(params.record.acpSessionId, params.record.cwd, { suppressReplayUpdates: true }), params.timeoutMs);
|
|
6495
6087
|
reconcileAgentSessionId(params.record, loadResult.agentSessionId);
|
|
6496
6088
|
applyConfigOptionsToRecord(params.record, loadResult);
|
|
6497
6089
|
return {
|
|
@@ -6608,7 +6200,7 @@ async function withConnectedSession(options) {
|
|
|
6608
6200
|
resumed,
|
|
6609
6201
|
loadError
|
|
6610
6202
|
});
|
|
6611
|
-
const now = isoNow$
|
|
6203
|
+
const now = isoNow$1();
|
|
6612
6204
|
record.lastUsedAt = now;
|
|
6613
6205
|
record.closed = false;
|
|
6614
6206
|
record.closedAt = void 0;
|
|
@@ -6629,7 +6221,7 @@ async function withConnectedSession(options) {
|
|
|
6629
6221
|
});
|
|
6630
6222
|
else await client.cancelActivePrompt(2500);
|
|
6631
6223
|
applyLifecycleSnapshotToRecord(record, client.getAgentLifecycleSnapshot());
|
|
6632
|
-
record.lastUsedAt = isoNow$
|
|
6224
|
+
record.lastUsedAt = isoNow$1();
|
|
6633
6225
|
await options.saveRecord(record).catch(() => {});
|
|
6634
6226
|
await client.close();
|
|
6635
6227
|
});
|
|
@@ -6655,10 +6247,13 @@ function recoveredSessionResult(response, conversation, promptMessageId) {
|
|
|
6655
6247
|
...responseMetaField(response?._meta)
|
|
6656
6248
|
};
|
|
6657
6249
|
}
|
|
6250
|
+
function abortTimedOutRequests(error, client, sessionId) {
|
|
6251
|
+
if (error instanceof TimeoutError) client.endPromptElicitation?.(sessionId);
|
|
6252
|
+
}
|
|
6658
6253
|
async function runPromptTurn(params) {
|
|
6659
6254
|
let settledResponse;
|
|
6660
6255
|
try {
|
|
6661
|
-
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation);
|
|
6256
|
+
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestWritten, params.onElicitation, params.onPermissionRequest);
|
|
6662
6257
|
promptPromise.then((response) => {
|
|
6663
6258
|
settledResponse = response;
|
|
6664
6259
|
}, () => {});
|
|
@@ -6675,6 +6270,7 @@ async function runPromptTurn(params) {
|
|
|
6675
6270
|
...responseMetaField(response._meta)
|
|
6676
6271
|
};
|
|
6677
6272
|
} catch (error) {
|
|
6273
|
+
abortTimedOutRequests(error, params.client, params.sessionId);
|
|
6678
6274
|
if (!(error instanceof TimeoutError) || !params.promptMessageId) throw error;
|
|
6679
6275
|
await params.client.waitForSessionUpdatesIdle?.({
|
|
6680
6276
|
idleMs: SESSION_REPLY_IDLE_MS,
|
|
@@ -6738,6 +6334,6 @@ var LiveSessionCheckpoint = class {
|
|
|
6738
6334
|
}
|
|
6739
6335
|
};
|
|
6740
6336
|
//#endregion
|
|
6741
|
-
export {
|
|
6337
|
+
export { getPerfMetricsSnapshot as $, isRequestedModelUnsupportedError as A, isRetryablePromptError as At, absolutePath as B, OUTPUT_FORMATS as Bt, recordSessionUpdate as C, isLegacyZedCodexAcpInvocation as Ct, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as D, withTimeout as Dt, AcpClient as E, withInterrupt as Et, parsePromptSource as F, AUTH_POLICIES as Ft, listSessions as G, AgentSpawnError as Gt, findSession as H, PERMISSION_POLICY_ACTIONS as Ht, promptToDisplayText as I, EXIT_CODES as It, pruneSessions as J, listSessionsForAgent as K, QueueConnectionError as Kt, textPrompt as L, NON_INTERACTIVE_PERMISSION_POLICIES as Lt, PromptInputValidationError as M, extractAcpError as Mt, isPromptInput as N, isAcpResourceNotFoundError as Nt, REQUESTED_MODEL_UNSUPPORTED_REASONS as O, exitCodeForOutputErrorCode as Ot, mergePromptSourceWithText as P, toAcpErrorPayload as Pt, formatPerfMetric as Q, permissionModeSatisfies as R, OUTPUT_ERROR_CODES as Rt, recordPromptSubmission as S, parsePromptStopReason as St, advertisedModelState as T, TimeoutError as Tt, findSessionByDirectoryWalk as U, SESSION_RECORD_SCHEMA as Ut, findGitRepositoryRoot as V, PERMISSION_MODES as Vt, isoNow$1 as W, AcpxOperationalError as Wt, writeSessionRecord as X, resolveSessionRecord as Y, assertPersistedKeyPolicy as Z, reconcileAgentSessionId as _, isProcessAlive as _t, applyConfigOptionSelection as a, startPerfTimer as at, createSessionConversation as b, parseJsonRpcErrorMessage as bt, applyModelSelection as c, normalizeAgentSessionId as ct, mergeSessionOptions as d, sessionBaseDir as dt, incrementPerfCounter as et, persistSessionOptions as f, sessionEventActivePath as ft, createInitialSessionRecord as g, writePrivateJsonFile as gt, applyLifecycleSnapshotToRecord as h, writePrivateFile as ht, connectAndLoadSession as i, setPerfGauge as it, getAcpxVersion as j, normalizeOutputError as jt, RequestedModelUnsupportedError as k, formatErrorMessage as kt, applyRequestedModelIfAdvertised as l, DEFAULT_EVENT_SEGMENT_MAX_BYTES as lt, applyConversation as m, sessionEventSegmentPath as mt, runPromptTurn as n, recordPerfDuration as nt, applyConfigOptionsToRecord as o, parseSessionRecord as ot, sessionOptionsFromRecord as p, sessionEventLockPath as pt, normalizeName as q, QueueProtocolError as qt, withConnectedSession as r, resetPerfMetrics as rt, applyInitialModelSelection as s, serializeSessionRecordForDisk as st, LiveSessionCheckpoint as t, measurePerf as tt, setDesiredModeId as u, defaultSessionEventLog as ut, cloneSessionAcpxState as v, extractSessionUpdateNotification as vt, trimConversationForRuntime as w, InterruptedError as wt, recordClientOperation as x, parsePermissionNotice as xt, cloneSessionConversation as y, isAcpJsonRpcMessage as yt, DEFAULT_HISTORY_LIMIT as z, OUTPUT_ERROR_ORIGINS as zt };
|
|
6742
6338
|
|
|
6743
|
-
//# sourceMappingURL=live-checkpoint-
|
|
6339
|
+
//# sourceMappingURL=live-checkpoint-BEfxBKCh.js.map
|