@agentproto/runtime 2.6.0 → 2.7.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/README.md +16 -0
- package/dist/catalog-models.d.ts +3 -3
- package/dist/config.d.ts +27 -3
- package/dist/config.mjs.map +1 -1
- package/dist/{context-continuity-B9n0t0v-.d.ts → context-continuity-ib9_bVYM.d.ts} +12 -0
- package/dist/index.d.ts +277 -17
- package/dist/index.mjs +892 -244
- package/dist/index.mjs.map +1 -1
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/{session-config-DIf6wYYP.d.ts → session-config-DbWP9RRj.d.ts} +1 -1
- package/dist/{spawn-defaults-CYJoeHeO.d.ts → spawn-defaults-DVgmfxWo.d.ts} +1 -1
- package/dist/user-presets.d.ts +2 -2
- package/package.json +9 -9
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createReadStream, promises, readdirSync, readFileSync, mkdirSync, writeFileSync, existsSync, renameSync, chmodSync, openSync, closeSync, realpathSync, statSync, createWriteStream } from 'fs';
|
|
2
2
|
import { homedir, hostname, tmpdir } from 'os';
|
|
3
|
-
import { join,
|
|
3
|
+
import { join, dirname, resolve, basename, isAbsolute, normalize, relative, extname, delimiter, sep } from 'path';
|
|
4
4
|
import { createInterface } from 'readline';
|
|
5
5
|
import { timingSafeEqual, createHmac, randomBytes, randomUUID, createHash } from 'crypto';
|
|
6
6
|
import { mkdir, writeFile, unlink, readdir, readFile, stat, chmod, rename, rm, appendFile, mkdtemp, realpath, access } from 'fs/promises';
|
|
@@ -487,8 +487,8 @@ function createTranscriptWriter(opts) {
|
|
|
487
487
|
if (!state) return Promise.resolve();
|
|
488
488
|
flushBuffers(sessionId, state);
|
|
489
489
|
states.delete(sessionId);
|
|
490
|
-
return new Promise((
|
|
491
|
-
state.stream.end(() =>
|
|
490
|
+
return new Promise((resolve27) => {
|
|
491
|
+
state.stream.end(() => resolve27());
|
|
492
492
|
});
|
|
493
493
|
},
|
|
494
494
|
closeAll() {
|
|
@@ -497,7 +497,7 @@ function createTranscriptWriter(opts) {
|
|
|
497
497
|
const state = states.get(sessionId);
|
|
498
498
|
if (!state) continue;
|
|
499
499
|
flushBuffers(sessionId, state);
|
|
500
|
-
closings.push(new Promise((
|
|
500
|
+
closings.push(new Promise((resolve27) => state.stream.end(() => resolve27())));
|
|
501
501
|
}
|
|
502
502
|
states.clear();
|
|
503
503
|
return Promise.all(closings).then(() => void 0);
|
|
@@ -645,9 +645,9 @@ async function exportClaudeCodeSession(adapterSessionId, cwd) {
|
|
|
645
645
|
let stream;
|
|
646
646
|
try {
|
|
647
647
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
648
|
-
await new Promise((
|
|
648
|
+
await new Promise((resolve27, reject) => {
|
|
649
649
|
stream.once("error", reject);
|
|
650
|
-
stream.once("open",
|
|
650
|
+
stream.once("open", resolve27);
|
|
651
651
|
});
|
|
652
652
|
} catch (err) {
|
|
653
653
|
const code = err.code;
|
|
@@ -921,7 +921,7 @@ async function discoverHermesSessions(cwd, since, expectedId) {
|
|
|
921
921
|
}
|
|
922
922
|
async function defaultHermesRunner(adapterSessionId) {
|
|
923
923
|
const { spawn: spawn7 } = await import('child_process');
|
|
924
|
-
return new Promise((
|
|
924
|
+
return new Promise((resolve27, reject) => {
|
|
925
925
|
const chunks = [];
|
|
926
926
|
const errChunks = [];
|
|
927
927
|
const proc = spawn7(
|
|
@@ -940,7 +940,7 @@ async function defaultHermesRunner(adapterSessionId) {
|
|
|
940
940
|
)
|
|
941
941
|
);
|
|
942
942
|
} else {
|
|
943
|
-
|
|
943
|
+
resolve27(Buffer.concat(chunks).toString("utf8"));
|
|
944
944
|
}
|
|
945
945
|
});
|
|
946
946
|
});
|
|
@@ -977,9 +977,9 @@ async function exportDaemonEventsSession(sessionId, desc) {
|
|
|
977
977
|
let stream;
|
|
978
978
|
try {
|
|
979
979
|
stream = createReadStream(filePath, { encoding: "utf8" });
|
|
980
|
-
await new Promise((
|
|
980
|
+
await new Promise((resolve27, reject) => {
|
|
981
981
|
stream.once("error", reject);
|
|
982
|
-
stream.once("open",
|
|
982
|
+
stream.once("open", resolve27);
|
|
983
983
|
});
|
|
984
984
|
} catch (err) {
|
|
985
985
|
const code = err.code;
|
|
@@ -2045,6 +2045,108 @@ var init_conversation_store = __esm({
|
|
|
2045
2045
|
};
|
|
2046
2046
|
}
|
|
2047
2047
|
});
|
|
2048
|
+
|
|
2049
|
+
// src/config.ts
|
|
2050
|
+
var config_exports = {};
|
|
2051
|
+
__export(config_exports, {
|
|
2052
|
+
CONFIG_FILE_PATH: () => CONFIG_FILE_PATH,
|
|
2053
|
+
CONFIG_VERSION: () => CONFIG_VERSION,
|
|
2054
|
+
getConfigKey: () => getConfigKey,
|
|
2055
|
+
loadConfig: () => loadConfig,
|
|
2056
|
+
saveConfig: () => saveConfig,
|
|
2057
|
+
setConfigKey: () => setConfigKey
|
|
2058
|
+
});
|
|
2059
|
+
function sanitizeAcpAgents(raw, target) {
|
|
2060
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
2061
|
+
console.warn(
|
|
2062
|
+
`[runtime/config] ${target}: 'acpAgents' is not an object \u2014 ignoring`
|
|
2063
|
+
);
|
|
2064
|
+
return void 0;
|
|
2065
|
+
}
|
|
2066
|
+
const out = {};
|
|
2067
|
+
for (const [slug, value] of Object.entries(raw)) {
|
|
2068
|
+
if (value && typeof value === "object" && !Array.isArray(value) && typeof value.bin === "string" && value.bin.length > 0) {
|
|
2069
|
+
out[slug] = value;
|
|
2070
|
+
} else {
|
|
2071
|
+
console.warn(
|
|
2072
|
+
`[runtime/config] ${target}: acpAgents.${slug} is missing a string 'bin' \u2014 ignoring`
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
2077
|
+
}
|
|
2078
|
+
async function loadConfig(path) {
|
|
2079
|
+
const target = path ?? CONFIG_FILE_PATH();
|
|
2080
|
+
try {
|
|
2081
|
+
const raw = await promises.readFile(target, "utf8");
|
|
2082
|
+
const parsed = JSON.parse(raw);
|
|
2083
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2084
|
+
const cfg = parsed;
|
|
2085
|
+
if (cfg.acpAgents !== void 0) {
|
|
2086
|
+
cfg.acpAgents = sanitizeAcpAgents(cfg.acpAgents, target);
|
|
2087
|
+
}
|
|
2088
|
+
return cfg;
|
|
2089
|
+
}
|
|
2090
|
+
console.warn(
|
|
2091
|
+
`[runtime/config] ${target}: top-level value is not an object \u2014 ignoring`
|
|
2092
|
+
);
|
|
2093
|
+
return {};
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
const code = err.code;
|
|
2096
|
+
if (code && code !== "ENOENT") {
|
|
2097
|
+
console.warn(
|
|
2098
|
+
`[runtime/config] failed to read ${target}: ${err instanceof Error ? err.message : String(err)}`
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
return {};
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
async function saveConfig(next, path) {
|
|
2105
|
+
const target = path ?? CONFIG_FILE_PATH();
|
|
2106
|
+
const payload = { ...next, version: CONFIG_VERSION };
|
|
2107
|
+
const dir = dirname(target);
|
|
2108
|
+
await promises.mkdir(dir, { recursive: true });
|
|
2109
|
+
const tmp = `${target}.tmp`;
|
|
2110
|
+
await promises.writeFile(tmp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
2111
|
+
await promises.rename(tmp, target);
|
|
2112
|
+
}
|
|
2113
|
+
function getConfigKey(cfg, dotted) {
|
|
2114
|
+
let cur = cfg;
|
|
2115
|
+
for (const part of dotted.split(".")) {
|
|
2116
|
+
if (cur == null || typeof cur !== "object") return void 0;
|
|
2117
|
+
cur = cur[part];
|
|
2118
|
+
}
|
|
2119
|
+
return cur;
|
|
2120
|
+
}
|
|
2121
|
+
function setConfigKey(cfg, dotted, value) {
|
|
2122
|
+
const parts = dotted.split(".");
|
|
2123
|
+
const out = { ...cfg };
|
|
2124
|
+
let cur = out;
|
|
2125
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
2126
|
+
const k = parts[i];
|
|
2127
|
+
const next = cur[k];
|
|
2128
|
+
if (next && typeof next === "object" && !Array.isArray(next)) {
|
|
2129
|
+
cur[k] = { ...next };
|
|
2130
|
+
} else {
|
|
2131
|
+
cur[k] = {};
|
|
2132
|
+
}
|
|
2133
|
+
cur = cur[k];
|
|
2134
|
+
}
|
|
2135
|
+
const leaf = parts[parts.length - 1];
|
|
2136
|
+
if (value === void 0) {
|
|
2137
|
+
delete cur[leaf];
|
|
2138
|
+
} else {
|
|
2139
|
+
cur[leaf] = value;
|
|
2140
|
+
}
|
|
2141
|
+
return out;
|
|
2142
|
+
}
|
|
2143
|
+
var CONFIG_VERSION, CONFIG_FILE_PATH;
|
|
2144
|
+
var init_config = __esm({
|
|
2145
|
+
"src/config.ts"() {
|
|
2146
|
+
CONFIG_VERSION = 1;
|
|
2147
|
+
CONFIG_FILE_PATH = () => join(homedir(), ".agentproto", "config.json");
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2048
2150
|
async function writeRuntimeMeta(workspace, meta) {
|
|
2049
2151
|
const dir = join(workspace, ".agentproto");
|
|
2050
2152
|
try {
|
|
@@ -3075,8 +3177,8 @@ async function enrichWithRemainingQuota(rollup, opts) {
|
|
|
3075
3177
|
})
|
|
3076
3178
|
);
|
|
3077
3179
|
const capMs = opts.timeoutMs ?? DEFAULT_ENRICH_CAP_MS;
|
|
3078
|
-
const cap = new Promise((
|
|
3079
|
-
setTimeout(() =>
|
|
3180
|
+
const cap = new Promise((resolve27) => {
|
|
3181
|
+
setTimeout(() => resolve27(null), capMs);
|
|
3080
3182
|
});
|
|
3081
3183
|
const settled = await Promise.race([work, cap]);
|
|
3082
3184
|
if (settled === null) return rollup;
|
|
@@ -3140,8 +3242,8 @@ async function enrichWithAccountCredits(rollup, opts) {
|
|
|
3140
3242
|
})
|
|
3141
3243
|
);
|
|
3142
3244
|
const capMs = opts.timeoutMs ?? DEFAULT_ENRICH_CAP_MS;
|
|
3143
|
-
const cap = new Promise((
|
|
3144
|
-
setTimeout(() =>
|
|
3245
|
+
const cap = new Promise((resolve27) => {
|
|
3246
|
+
setTimeout(() => resolve27(null), capMs);
|
|
3145
3247
|
});
|
|
3146
3248
|
const settled = await Promise.race([work, cap]);
|
|
3147
3249
|
if (settled === null) return rollup;
|
|
@@ -3480,52 +3582,9 @@ function normalizeConfig(parsed) {
|
|
|
3480
3582
|
if (active !== void 0) out.active = active;
|
|
3481
3583
|
return out;
|
|
3482
3584
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
console.warn(
|
|
3487
|
-
`[runtime/config] ${target}: 'acpAgents' is not an object \u2014 ignoring`
|
|
3488
|
-
);
|
|
3489
|
-
return void 0;
|
|
3490
|
-
}
|
|
3491
|
-
const out = {};
|
|
3492
|
-
for (const [slug, value] of Object.entries(raw)) {
|
|
3493
|
-
if (value && typeof value === "object" && !Array.isArray(value) && typeof value.bin === "string" && value.bin.length > 0) {
|
|
3494
|
-
out[slug] = value;
|
|
3495
|
-
} else {
|
|
3496
|
-
console.warn(
|
|
3497
|
-
`[runtime/config] ${target}: acpAgents.${slug} is missing a string 'bin' \u2014 ignoring`
|
|
3498
|
-
);
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
3502
|
-
}
|
|
3503
|
-
async function loadConfig(path) {
|
|
3504
|
-
const target = path ?? CONFIG_FILE_PATH();
|
|
3505
|
-
try {
|
|
3506
|
-
const raw = await promises.readFile(target, "utf8");
|
|
3507
|
-
const parsed = JSON.parse(raw);
|
|
3508
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3509
|
-
const cfg = parsed;
|
|
3510
|
-
if (cfg.acpAgents !== void 0) {
|
|
3511
|
-
cfg.acpAgents = sanitizeAcpAgents(cfg.acpAgents, target);
|
|
3512
|
-
}
|
|
3513
|
-
return cfg;
|
|
3514
|
-
}
|
|
3515
|
-
console.warn(
|
|
3516
|
-
`[runtime/config] ${target}: top-level value is not an object \u2014 ignoring`
|
|
3517
|
-
);
|
|
3518
|
-
return {};
|
|
3519
|
-
} catch (err) {
|
|
3520
|
-
const code = err.code;
|
|
3521
|
-
if (code && code !== "ENOENT") {
|
|
3522
|
-
console.warn(
|
|
3523
|
-
`[runtime/config] failed to read ${target}: ${err instanceof Error ? err.message : String(err)}`
|
|
3524
|
-
);
|
|
3525
|
-
}
|
|
3526
|
-
return {};
|
|
3527
|
-
}
|
|
3528
|
-
}
|
|
3585
|
+
|
|
3586
|
+
// src/session-spawn.ts
|
|
3587
|
+
init_config();
|
|
3529
3588
|
var markerSchema = z.object({ worktreeId: z.string() });
|
|
3530
3589
|
function statOrUndefined(path) {
|
|
3531
3590
|
try {
|
|
@@ -4111,6 +4170,7 @@ function resolveContextContinuityPolicy(globalDefault, harnessDefault, modelDefa
|
|
|
4111
4170
|
function computeContextPct(contextSize, contextUsed) {
|
|
4112
4171
|
if (contextSize === void 0 || contextSize <= 0) return null;
|
|
4113
4172
|
if (contextUsed === void 0 || contextUsed < 0) return null;
|
|
4173
|
+
if (contextUsed === contextSize) return null;
|
|
4114
4174
|
const used = Math.min(contextUsed, contextSize);
|
|
4115
4175
|
return Math.round(used / contextSize * 100);
|
|
4116
4176
|
}
|
|
@@ -4420,6 +4480,9 @@ function composeRoleContext(role, promptAppend, registry) {
|
|
|
4420
4480
|
return [role.disposition, spawnLine, promptAppend].filter((p) => !!p).join("\n\n");
|
|
4421
4481
|
}
|
|
4422
4482
|
|
|
4483
|
+
// src/role-registry.ts
|
|
4484
|
+
init_config();
|
|
4485
|
+
|
|
4423
4486
|
// src/role-pack.ts
|
|
4424
4487
|
var FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
4425
4488
|
function parseFields(raw) {
|
|
@@ -4642,7 +4705,7 @@ function createSandboxAgentSessionProxy(opts) {
|
|
|
4642
4705
|
`sandbox proxy: ${consecutivePollFailures} consecutive poll failures against the box daemon (session "${remoteSessionId}") \u2014 giving up. Last error: ${pollErr instanceof Error ? pollErr.message : String(pollErr)}`
|
|
4643
4706
|
);
|
|
4644
4707
|
}
|
|
4645
|
-
await new Promise((
|
|
4708
|
+
await new Promise((resolve27) => setTimeout(resolve27, POLL_RETRY_DELAY_MS));
|
|
4646
4709
|
continue;
|
|
4647
4710
|
}
|
|
4648
4711
|
if (result.timedOut) continue;
|
|
@@ -4703,6 +4766,7 @@ function createSandboxAgentSessionProxy(opts) {
|
|
|
4703
4766
|
}
|
|
4704
4767
|
|
|
4705
4768
|
// src/worktree-isolation.ts
|
|
4769
|
+
init_config();
|
|
4706
4770
|
var WORKTREE_ISOLATION_ENV = "AGENTPROTO_WORKTREES_ISOLATION";
|
|
4707
4771
|
var DEFAULT_WORKTREE_ISOLATION = "on-request";
|
|
4708
4772
|
function normalizeWorktreeField(field) {
|
|
@@ -4762,6 +4826,7 @@ async function loadWorktreeIsolation(loadCfg = loadConfig) {
|
|
|
4762
4826
|
}
|
|
4763
4827
|
|
|
4764
4828
|
// src/spawn-attach.ts
|
|
4829
|
+
init_config();
|
|
4765
4830
|
var SPAWN_ATTACH_ENV = "AGENTPROTO_SPAWN_ATTACH";
|
|
4766
4831
|
var DEFAULT_SPAWN_ATTACH = "always";
|
|
4767
4832
|
function normalizeAttachField(field) {
|
|
@@ -4799,6 +4864,9 @@ async function loadSpawnAttach(loadCfg = loadConfig) {
|
|
|
4799
4864
|
}
|
|
4800
4865
|
return DEFAULT_SPAWN_ATTACH;
|
|
4801
4866
|
}
|
|
4867
|
+
|
|
4868
|
+
// src/spawn-dedupe.ts
|
|
4869
|
+
init_config();
|
|
4802
4870
|
var SPAWN_DEDUPE_ENV = "AGENTPROTO_SPAWN_DEDUPE";
|
|
4803
4871
|
var DEFAULT_SPAWN_DEDUPE = "always";
|
|
4804
4872
|
var IMPLICIT_KEY_PREFIX = "\0implicit";
|
|
@@ -4822,6 +4890,305 @@ async function loadSpawnDedupe(loadCfg = loadConfig) {
|
|
|
4822
4890
|
}
|
|
4823
4891
|
return DEFAULT_SPAWN_DEDUPE;
|
|
4824
4892
|
}
|
|
4893
|
+
var MARKER = "@agentproto-bot";
|
|
4894
|
+
var fmtTokens = (n) => {
|
|
4895
|
+
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
4896
|
+
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
4897
|
+
};
|
|
4898
|
+
function cwdLabel(cwd, workspaceSlug) {
|
|
4899
|
+
const leaf = basename(cwd);
|
|
4900
|
+
if (!workspaceSlug || workspaceSlug === leaf) return workspaceSlug ?? leaf;
|
|
4901
|
+
return `${workspaceSlug}/${leaf}`;
|
|
4902
|
+
}
|
|
4903
|
+
var buildFooter = ({
|
|
4904
|
+
prov,
|
|
4905
|
+
authMode,
|
|
4906
|
+
runId,
|
|
4907
|
+
runUrl,
|
|
4908
|
+
sha,
|
|
4909
|
+
kind = "review"
|
|
4910
|
+
}) => {
|
|
4911
|
+
const parts = [`\u{1F916} **${MARKER}** \u2014 ${kind}`];
|
|
4912
|
+
if (prov?.sessionId) {
|
|
4913
|
+
parts.push(`session \`${prov.sessionId}\`${prov.label ? ` (\`${prov.label}\`)` : ""}`);
|
|
4914
|
+
}
|
|
4915
|
+
if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(" / "));
|
|
4916
|
+
else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : ""}`);
|
|
4917
|
+
else if (authMode) parts.push(authMode);
|
|
4918
|
+
if (prov?.authProfile) parts.push(`auth-profile \`${prov.authProfile}\``);
|
|
4919
|
+
if (prov?.model) parts.push(`model \`${prov.model}\``);
|
|
4920
|
+
if (prov?.sandboxId) parts.push(`e2b \`${prov.sandboxId}\``);
|
|
4921
|
+
if (prov?.parentSessionId) parts.push(`supervisor \`${prov.parentSessionId}\``);
|
|
4922
|
+
const tin = fmtTokens(prov?.tokensIn);
|
|
4923
|
+
const tout = fmtTokens(prov?.tokensOut);
|
|
4924
|
+
if (tin || tout) parts.push(`${tin ?? "?"} in / ${tout ?? "?"} out`);
|
|
4925
|
+
if (typeof prov?.costUsd === "number") {
|
|
4926
|
+
parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== "adapter" ? ` (${prov.source})` : ""}`);
|
|
4927
|
+
}
|
|
4928
|
+
const showLocalHostCwd = prov?.source === "local" || prov?.source === "daemon" || !runId;
|
|
4929
|
+
if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`);
|
|
4930
|
+
if (showLocalHostCwd) {
|
|
4931
|
+
if (prov?.host) parts.push(`host \`${prov.host}\``);
|
|
4932
|
+
if (prov?.cwd) parts.push(`cwd \`${cwdLabel(prov.cwd, prov.workspaceSlug)}\``);
|
|
4933
|
+
}
|
|
4934
|
+
if (sha) parts.push(`sha \`${sha.slice(0, 7)}\``);
|
|
4935
|
+
return `
|
|
4936
|
+
|
|
4937
|
+
---
|
|
4938
|
+
<sub>${parts.join(" \xB7 ")}</sub>`;
|
|
4939
|
+
};
|
|
4940
|
+
function sessionFooterProvenance(session, options = {}) {
|
|
4941
|
+
const prov = {
|
|
4942
|
+
sessionId: session.id,
|
|
4943
|
+
label: session.label,
|
|
4944
|
+
adapter: session.harness ?? session.adapterSlug,
|
|
4945
|
+
model: session.model,
|
|
4946
|
+
authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,
|
|
4947
|
+
parentSessionId: options.supervisor?.id,
|
|
4948
|
+
costUsd: session.costUsd,
|
|
4949
|
+
tokensIn: session.tokensIn,
|
|
4950
|
+
tokensOut: session.tokensOut,
|
|
4951
|
+
source: options.source ?? "daemon",
|
|
4952
|
+
host: options.host,
|
|
4953
|
+
cwd: session.cwd,
|
|
4954
|
+
workspaceSlug: session.workspaceSlug
|
|
4955
|
+
};
|
|
4956
|
+
return { prov, authMode: session.auth?.mode };
|
|
4957
|
+
}
|
|
4958
|
+
function buildSessionPrFooter(session, options = {}) {
|
|
4959
|
+
const { prov, authMode } = sessionFooterProvenance(session, options);
|
|
4960
|
+
return buildFooter({ prov, authMode, sha: options.sha, kind: "PR" });
|
|
4961
|
+
}
|
|
4962
|
+
function appendFooterOnce(body, footer) {
|
|
4963
|
+
if (body.includes(MARKER)) return body;
|
|
4964
|
+
return `${body}${footer}`;
|
|
4965
|
+
}
|
|
4966
|
+
function parseGhPrCreate(command, args, stdout) {
|
|
4967
|
+
if (basename(command) !== "gh") return null;
|
|
4968
|
+
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
4969
|
+
if (positionals[0] !== "pr" || positionals[1] !== "create") return null;
|
|
4970
|
+
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
4971
|
+
let match;
|
|
4972
|
+
let last = null;
|
|
4973
|
+
while ((match = re.exec(stdout)) !== null) {
|
|
4974
|
+
last = { url: match[0], number: Number(match[1]) };
|
|
4975
|
+
}
|
|
4976
|
+
return last;
|
|
4977
|
+
}
|
|
4978
|
+
function cwdRelated(sessionCwd, cwd) {
|
|
4979
|
+
if (sessionCwd === cwd) return true;
|
|
4980
|
+
const sep2 = "/";
|
|
4981
|
+
return cwd.startsWith(sessionCwd + sep2) || sessionCwd.startsWith(cwd + sep2);
|
|
4982
|
+
}
|
|
4983
|
+
function pickExecutorSession(sessions, cwd) {
|
|
4984
|
+
const candidates = sessions.filter(
|
|
4985
|
+
(s) => s.kind === "agent-cli" && typeof s.cwd === "string" && cwdRelated(s.cwd, cwd)
|
|
4986
|
+
);
|
|
4987
|
+
if (candidates.length === 0) return void 0;
|
|
4988
|
+
const alive = (s) => s.status === "running" || s.status === "starting";
|
|
4989
|
+
const byRecency = (a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "");
|
|
4990
|
+
const live = candidates.filter(alive).sort(byRecency);
|
|
4991
|
+
if (live.length > 0) return live[0];
|
|
4992
|
+
return [...candidates].sort(byRecency)[0];
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4995
|
+
// src/gh-provenance-shim.ts
|
|
4996
|
+
var PROVENANCE_WRAP_GH_ENV = "AGENTPROTO_PROVENANCE_WRAP_GH";
|
|
4997
|
+
var DEFAULT_WRAP_GH = false;
|
|
4998
|
+
var GH_PROVENANCE_ENABLE_ENV = "AGENTPROTO_GH_PROVENANCE";
|
|
4999
|
+
var GH_PROVENANCE_ADAPTER_ENV = "AGENTPROTO_ADAPTER";
|
|
5000
|
+
var GH_PROVENANCE_MODEL_ENV = "AGENTPROTO_MODEL";
|
|
5001
|
+
function parseWrapGh(raw) {
|
|
5002
|
+
if (raw === void 0) return void 0;
|
|
5003
|
+
const v = raw.trim().toLowerCase();
|
|
5004
|
+
if (v === "1" || v === "true" || v === "yes" || v === "on") return true;
|
|
5005
|
+
if (v === "0" || v === "false" || v === "no" || v === "off") return false;
|
|
5006
|
+
return void 0;
|
|
5007
|
+
}
|
|
5008
|
+
async function loadProvenanceWrapGh(loadCfg = defaultLoadConfig) {
|
|
5009
|
+
const fromEnv = parseWrapGh(process.env[PROVENANCE_WRAP_GH_ENV]);
|
|
5010
|
+
if (fromEnv !== void 0) return fromEnv;
|
|
5011
|
+
try {
|
|
5012
|
+
const cfg = await loadCfg();
|
|
5013
|
+
if (typeof cfg.provenance?.wrapGh === "boolean") return cfg.provenance.wrapGh;
|
|
5014
|
+
} catch {
|
|
5015
|
+
}
|
|
5016
|
+
return DEFAULT_WRAP_GH;
|
|
5017
|
+
}
|
|
5018
|
+
async function defaultLoadConfig() {
|
|
5019
|
+
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
5020
|
+
return loadConfig2();
|
|
5021
|
+
}
|
|
5022
|
+
function assembleShimPath(shimDir, basePath, sep2 = delimiter) {
|
|
5023
|
+
const base = basePath ?? "";
|
|
5024
|
+
const entries = base.length > 0 ? base.split(sep2) : [];
|
|
5025
|
+
if (entries[0] === shimDir) return base;
|
|
5026
|
+
return [shimDir, ...entries].join(sep2);
|
|
5027
|
+
}
|
|
5028
|
+
function buildGhShimEnv(input) {
|
|
5029
|
+
const env = {
|
|
5030
|
+
PATH: assembleShimPath(input.shimDir, input.basePath, input.sep),
|
|
5031
|
+
[GH_PROVENANCE_ENABLE_ENV]: "1"
|
|
5032
|
+
};
|
|
5033
|
+
if (input.adapter) env[GH_PROVENANCE_ADAPTER_ENV] = input.adapter;
|
|
5034
|
+
if (input.model) env[GH_PROVENANCE_MODEL_ENV] = input.model;
|
|
5035
|
+
return env;
|
|
5036
|
+
}
|
|
5037
|
+
function renderGhShimScript(opts) {
|
|
5038
|
+
return `#!${opts.nodePath}
|
|
5039
|
+
"use strict"
|
|
5040
|
+
// GENERATED by @agentproto/runtime (gh-provenance-shim.ts). Do not edit by
|
|
5041
|
+
// hand \u2014 the daemon rewrites this on boot. See that module for the rationale.
|
|
5042
|
+
const { spawnSync } = require("node:child_process")
|
|
5043
|
+
const { statSync, realpathSync } = require("node:fs")
|
|
5044
|
+
const path = require("node:path")
|
|
5045
|
+
const os = require("node:os")
|
|
5046
|
+
|
|
5047
|
+
// The real filesystem location of this shim, so we can skip it while scanning
|
|
5048
|
+
// PATH \u2014 compared by realpath so a symlinked temp/home dir (macOS /var ->
|
|
5049
|
+
// /private/var) can't fool the equality check into an infinite recursion.
|
|
5050
|
+
function realOf(p) {
|
|
5051
|
+
try { return realpathSync(p) } catch { return p }
|
|
5052
|
+
}
|
|
5053
|
+
|
|
5054
|
+
const MARKER = ${JSON.stringify(MARKER)}
|
|
5055
|
+
const SESSION_ID_ENV = ${JSON.stringify(SESSION_ID_ENV)}
|
|
5056
|
+
const WORKSPACE_SLUG_ENV = ${JSON.stringify(WORKSPACE_SLUG_ENV)}
|
|
5057
|
+
const ADAPTER_ENV = ${JSON.stringify(GH_PROVENANCE_ADAPTER_ENV)}
|
|
5058
|
+
const MODEL_ENV = ${JSON.stringify(GH_PROVENANCE_MODEL_ENV)}
|
|
5059
|
+
|
|
5060
|
+
const args = process.argv.slice(2)
|
|
5061
|
+
const shimDir = __dirname
|
|
5062
|
+
|
|
5063
|
+
// Resolve the REAL gh: the first executable \`gh\` on PATH that is NOT this
|
|
5064
|
+
// shim's own directory (guarding against infinite recursion).
|
|
5065
|
+
function findRealGh() {
|
|
5066
|
+
const selfReal = realOf(shimDir)
|
|
5067
|
+
const dirs = (process.env.PATH || "").split(path.delimiter)
|
|
5068
|
+
for (const d of dirs) {
|
|
5069
|
+
if (!d) continue
|
|
5070
|
+
const candidate = path.join(d, "gh")
|
|
5071
|
+
try { if (!statSync(candidate).isFile()) continue } catch { continue }
|
|
5072
|
+
if (realOf(d) === selfReal) continue
|
|
5073
|
+
return candidate
|
|
5074
|
+
}
|
|
5075
|
+
return null
|
|
5076
|
+
}
|
|
5077
|
+
|
|
5078
|
+
const realGh = findRealGh()
|
|
5079
|
+
if (!realGh) {
|
|
5080
|
+
// No real gh anywhere \u2014 behave exactly as its absence would: not found.
|
|
5081
|
+
process.stderr.write("agentproto gh-provenance shim: real 'gh' not found on PATH\\n")
|
|
5082
|
+
process.exit(127)
|
|
5083
|
+
}
|
|
5084
|
+
|
|
5085
|
+
// Only \`gh pr create\` is targeted (v1). Everything else passes straight
|
|
5086
|
+
// through to the real gh, exit code and all.
|
|
5087
|
+
const positionals = args.filter(a => !a.startsWith("-"))
|
|
5088
|
+
const isPrCreate = positionals[0] === "pr" && positionals[1] === "create"
|
|
5089
|
+
|
|
5090
|
+
function exitFrom(result) {
|
|
5091
|
+
if (result.error) {
|
|
5092
|
+
process.stderr.write(String(result.error && result.error.message) + "\\n")
|
|
5093
|
+
return 127
|
|
5094
|
+
}
|
|
5095
|
+
return typeof result.status === "number" ? result.status : 1
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5098
|
+
if (!isPrCreate) {
|
|
5099
|
+
const passthrough = spawnSync(realGh, args, { stdio: "inherit" })
|
|
5100
|
+
process.exit(exitFrom(passthrough))
|
|
5101
|
+
}
|
|
5102
|
+
|
|
5103
|
+
// Targeted: run the real create, capture stdout while echoing it through so
|
|
5104
|
+
// the user still sees gh's own output (it prints the PR URL there).
|
|
5105
|
+
const run = spawnSync(realGh, args, { encoding: "utf8", stdio: ["inherit", "pipe", "inherit"] })
|
|
5106
|
+
const stdout = run.stdout || ""
|
|
5107
|
+
if (stdout) process.stdout.write(stdout)
|
|
5108
|
+
const exitCode = exitFrom(run)
|
|
5109
|
+
|
|
5110
|
+
// Footer step is COSMETIC: any failure here must never change the exit code.
|
|
5111
|
+
try {
|
|
5112
|
+
if (exitCode === 0) {
|
|
5113
|
+
const parsed = parsePrUrl(stdout)
|
|
5114
|
+
if (parsed) stampFooter(parsed)
|
|
5115
|
+
}
|
|
5116
|
+
} catch { /* swallow \u2014 stamping never fails the underlying gh */ }
|
|
5117
|
+
process.exit(exitCode)
|
|
5118
|
+
|
|
5119
|
+
// --- pure-ish helpers (mirrors pr-provenance.ts's parseGhPrCreate/buildFooter) ---
|
|
5120
|
+
|
|
5121
|
+
function parsePrUrl(text) {
|
|
5122
|
+
// Take the LAST match so an advisory "Warning: \u2026/pull/\u2026" can't shadow the
|
|
5123
|
+
// real URL gh prints on its own line.
|
|
5124
|
+
const re = /https?:\\/\\/([^\\/\\s]+)\\/([^\\/\\s]+)\\/([^\\/\\s]+)\\/pull\\/(\\d+)/g
|
|
5125
|
+
let m
|
|
5126
|
+
let last = null
|
|
5127
|
+
while ((m = re.exec(text)) !== null) {
|
|
5128
|
+
last = { host: m[1], owner: m[2], repo: m[3], number: Number(m[4]), url: m[0] }
|
|
5129
|
+
}
|
|
5130
|
+
return last
|
|
5131
|
+
}
|
|
5132
|
+
|
|
5133
|
+
function cwdLabel(cwd, ws) {
|
|
5134
|
+
const leaf = path.basename(cwd)
|
|
5135
|
+
if (!ws || ws === leaf) return ws || leaf
|
|
5136
|
+
return ws + "/" + leaf
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5139
|
+
function buildFooter() {
|
|
5140
|
+
const parts = ["\u{1F916} **" + MARKER + "** \u2014 PR"]
|
|
5141
|
+
const sid = process.env[SESSION_ID_ENV]
|
|
5142
|
+
if (sid) parts.push("session \`" + sid + "\`")
|
|
5143
|
+
const adapter = process.env[ADAPTER_ENV]
|
|
5144
|
+
if (adapter) parts.push(adapter)
|
|
5145
|
+
const model = process.env[MODEL_ENV]
|
|
5146
|
+
if (model) parts.push("model \`" + model + "\`")
|
|
5147
|
+
const host = os.hostname()
|
|
5148
|
+
if (host) parts.push("host \`" + host + "\`")
|
|
5149
|
+
parts.push("cwd \`" + cwdLabel(process.cwd(), process.env[WORKSPACE_SLUG_ENV]) + "\`")
|
|
5150
|
+
return "\\n\\n---\\n<sub>" + parts.join(" \xB7 ") + "</sub>"
|
|
5151
|
+
}
|
|
5152
|
+
|
|
5153
|
+
function stampFooter(parsed) {
|
|
5154
|
+
// Read the current body via the real gh; append the footer once (idempotent
|
|
5155
|
+
// by MARKER, so a retry never stacks a second one).
|
|
5156
|
+
const view = spawnSync(realGh, ["pr", "view", parsed.url, "--json", "body", "-q", ".body"], { encoding: "utf8" })
|
|
5157
|
+
if (view.status !== 0) return
|
|
5158
|
+
let body = typeof view.stdout === "string" ? view.stdout : ""
|
|
5159
|
+
if (body.endsWith("\\n")) body = body.slice(0, -1)
|
|
5160
|
+
if (body.includes(MARKER)) return
|
|
5161
|
+
const newBody = body + buildFooter()
|
|
5162
|
+
// Post-create PATCH via \`gh api\` \u2014 never touches the create's own args.
|
|
5163
|
+
const apiPath = "repos/" + parsed.owner + "/" + parsed.repo + "/pulls/" + parsed.number
|
|
5164
|
+
const apiArgs = ["api"]
|
|
5165
|
+
if (parsed.host && parsed.host !== "github.com") apiArgs.push("--hostname", parsed.host)
|
|
5166
|
+
apiArgs.push(apiPath, "-X", "PATCH", "-f", "body=" + newBody)
|
|
5167
|
+
spawnSync(realGh, apiArgs, { stdio: "ignore" })
|
|
5168
|
+
}
|
|
5169
|
+
`;
|
|
5170
|
+
}
|
|
5171
|
+
function defaultGhShimBaseDir() {
|
|
5172
|
+
return join(homedir(), ".agentproto", "shims");
|
|
5173
|
+
}
|
|
5174
|
+
var shimDirCache = /* @__PURE__ */ new Map();
|
|
5175
|
+
function ensureGhShimDir(opts = {}) {
|
|
5176
|
+
const baseDir = opts.baseDir ?? defaultGhShimBaseDir();
|
|
5177
|
+
const nodePath = opts.nodePath ?? process.execPath;
|
|
5178
|
+
const key = `${baseDir}\0${nodePath}`;
|
|
5179
|
+
const cached = shimDirCache.get(key);
|
|
5180
|
+
if (cached) return cached;
|
|
5181
|
+
const task = (async () => {
|
|
5182
|
+
await mkdir(baseDir, { recursive: true });
|
|
5183
|
+
const shimPath = join(baseDir, "gh");
|
|
5184
|
+
await writeFile(shimPath, renderGhShimScript({ nodePath }), "utf8");
|
|
5185
|
+
await chmod(shimPath, 493);
|
|
5186
|
+
return resolve(baseDir);
|
|
5187
|
+
})();
|
|
5188
|
+
shimDirCache.set(key, task);
|
|
5189
|
+
task.catch(() => shimDirCache.delete(key));
|
|
5190
|
+
return task;
|
|
5191
|
+
}
|
|
4825
5192
|
|
|
4826
5193
|
// src/session-spawn.ts
|
|
4827
5194
|
var SPAWN_CLAIM_WINDOW_MS = 6e5;
|
|
@@ -5053,7 +5420,8 @@ async function spawnAgentSession(deps2, input) {
|
|
|
5053
5420
|
provisionWorktree,
|
|
5054
5421
|
resolveWorktreeIsolation,
|
|
5055
5422
|
resolveSpawnAttach,
|
|
5056
|
-
resolveSpawnDedupe
|
|
5423
|
+
resolveSpawnDedupe,
|
|
5424
|
+
resolveProvenanceWrapGh
|
|
5057
5425
|
} = deps2;
|
|
5058
5426
|
const explicitCwd = input.cwd !== void 0;
|
|
5059
5427
|
const explicitWorkspaceSlug = input.workspaceSlug !== void 0;
|
|
@@ -5511,8 +5879,8 @@ async function spawnAgentSession(deps2, input) {
|
|
|
5511
5879
|
}
|
|
5512
5880
|
let resolveClaim;
|
|
5513
5881
|
claims.set(key, {
|
|
5514
|
-
result: new Promise((
|
|
5515
|
-
resolveClaim =
|
|
5882
|
+
result: new Promise((resolve27) => {
|
|
5883
|
+
resolveClaim = resolve27;
|
|
5516
5884
|
}),
|
|
5517
5885
|
// An implicit claim expires sooner than an explicit one — see
|
|
5518
5886
|
// `IMPLICIT_SPAWN_CLAIM_WINDOW_MS`'s docblock.
|
|
@@ -5541,6 +5909,7 @@ async function spawnAgentSession(deps2, input) {
|
|
|
5541
5909
|
workspaceSlug: resolvedSlug,
|
|
5542
5910
|
cwd,
|
|
5543
5911
|
adapterSlug: input.adapter,
|
|
5912
|
+
adapterConfigDir: adapterConfigDirFor(mintedSessionId),
|
|
5544
5913
|
harness: input.harness ?? input.adapter,
|
|
5545
5914
|
...resolved?.routeSelection !== void 0 ? { routeSelection: resolved.routeSelection } : {},
|
|
5546
5915
|
...resolved?.authDescriptor?.provider !== void 0 ? { adapterProvider: resolved.authDescriptor.provider } : {},
|
|
@@ -5615,6 +5984,11 @@ async function spawnAgentSession(deps2, input) {
|
|
|
5615
5984
|
const agentSession2 = await resolved.startSession({
|
|
5616
5985
|
cwd: finalCwd,
|
|
5617
5986
|
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
|
|
5987
|
+
// Persistent isolated-config dir, keyed by this session's id —
|
|
5988
|
+
// recorded on the pending descriptor above so restart/lazy-resume
|
|
5989
|
+
// can hand the respawned adapter the same dir (native-resume
|
|
5990
|
+
// store). Adapters that don't isolate a config dir ignore it.
|
|
5991
|
+
configDir: adapterConfigDirFor(mintedSessionId),
|
|
5618
5992
|
...input.mode ? { mode: input.mode } : {},
|
|
5619
5993
|
...launchConfig.options ? { options: launchConfig.options } : {},
|
|
5620
5994
|
...launchConfig.wireModel ? { model: launchConfig.wireModel } : {},
|
|
@@ -5723,9 +6097,29 @@ ${asyncPrompt}`;
|
|
|
5723
6097
|
sandboxId = booted.sandboxId;
|
|
5724
6098
|
sandboxTeardown = booted.sandboxTeardown;
|
|
5725
6099
|
} else {
|
|
6100
|
+
let ghProvenanceEnv = {};
|
|
6101
|
+
try {
|
|
6102
|
+
const wrapGh = await (resolveProvenanceWrapGh ?? loadProvenanceWrapGh)();
|
|
6103
|
+
if (wrapGh) {
|
|
6104
|
+
const shimDir = await ensureGhShimDir();
|
|
6105
|
+
ghProvenanceEnv = buildGhShimEnv({
|
|
6106
|
+
shimDir,
|
|
6107
|
+
basePath: process.env.PATH ?? "",
|
|
6108
|
+
adapter: input.harness ?? input.adapter,
|
|
6109
|
+
...input.model ?? resolved?.defaultModel ? { model: input.model ?? resolved?.defaultModel } : {}
|
|
6110
|
+
});
|
|
6111
|
+
}
|
|
6112
|
+
} catch {
|
|
6113
|
+
ghProvenanceEnv = {};
|
|
6114
|
+
}
|
|
5726
6115
|
agentSession = await resolved.startSession({
|
|
5727
6116
|
cwd,
|
|
5728
6117
|
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
|
|
6118
|
+
// Persistent isolated-config dir, keyed by this session's id —
|
|
6119
|
+
// recorded on the descriptor below so restart/lazy-resume can hand
|
|
6120
|
+
// the respawned adapter the same dir (native-resume store).
|
|
6121
|
+
// Adapters that don't isolate a config dir ignore it.
|
|
6122
|
+
configDir: adapterConfigDirFor(mintedSessionId),
|
|
5729
6123
|
...input.mode ? { mode: input.mode } : {},
|
|
5730
6124
|
...launchConfig.options ? { options: launchConfig.options } : {},
|
|
5731
6125
|
...launchConfig.wireModel ? { model: launchConfig.wireModel } : {},
|
|
@@ -5742,6 +6136,10 @@ ${asyncPrompt}`;
|
|
|
5742
6136
|
// exec's. `agent_start` has no caller-facing `env` passthrough to
|
|
5743
6137
|
// collide with, so this is the entire env for this spawn.
|
|
5744
6138
|
env: {
|
|
6139
|
+
// Provenance shim env FIRST (PATH + adapter/model) so the identity
|
|
6140
|
+
// vars below always win — they must never be forgeable or shadowed,
|
|
6141
|
+
// and `ghProvenanceEnv` never carries an identity var anyway.
|
|
6142
|
+
...ghProvenanceEnv,
|
|
5745
6143
|
[SESSION_ID_ENV]: mintedSessionId,
|
|
5746
6144
|
[WORKSPACE_SLUG_ENV]: resolvedSlug,
|
|
5747
6145
|
// Lineage (PARENT_SESSION_ID_ENV's doc, sessions.ts) — mirrors
|
|
@@ -5780,6 +6178,7 @@ ${effectivePrompt}`;
|
|
|
5780
6178
|
cwd,
|
|
5781
6179
|
agentSession,
|
|
5782
6180
|
adapterSlug: input.adapter,
|
|
6181
|
+
adapterConfigDir: adapterConfigDirFor(mintedSessionId),
|
|
5783
6182
|
...resolved?.resumable !== void 0 ? { resumable: resolved.resumable } : {},
|
|
5784
6183
|
...resolved?.nativeTerminalResume !== void 0 ? { nativeTerminalResume: resolved.nativeTerminalResume } : {},
|
|
5785
6184
|
harness: input.harness ?? input.adapter,
|
|
@@ -5898,13 +6297,13 @@ async function isSharedDirtyCwd(cwd) {
|
|
|
5898
6297
|
const identity = resolveWorktreeIdentity(cwd);
|
|
5899
6298
|
if (identity?.worktreeId !== void 0) return false;
|
|
5900
6299
|
const { spawn: spawn7 } = await import('child_process');
|
|
5901
|
-
return await new Promise((
|
|
6300
|
+
return await new Promise((resolve27) => {
|
|
5902
6301
|
let stdout = "";
|
|
5903
6302
|
let settled = false;
|
|
5904
6303
|
const done = (v) => {
|
|
5905
6304
|
if (!settled) {
|
|
5906
6305
|
settled = true;
|
|
5907
|
-
|
|
6306
|
+
resolve27(v);
|
|
5908
6307
|
}
|
|
5909
6308
|
};
|
|
5910
6309
|
const child = spawn7("git", ["-C", cwd, "status", "--porcelain"], {
|
|
@@ -5919,15 +6318,15 @@ async function isSharedDirtyCwd(cwd) {
|
|
|
5919
6318
|
}
|
|
5920
6319
|
async function resolveMcpCredentialHeaders(mcpServers) {
|
|
5921
6320
|
if (!mcpServers || mcpServers.length === 0) return mcpServers;
|
|
5922
|
-
const { resolveMcpCredentialHeaders:
|
|
5923
|
-
if (!
|
|
6321
|
+
const { resolveMcpCredentialHeaders: resolve27 } = getMcpCredentialDeps();
|
|
6322
|
+
if (!resolve27) return mcpServers;
|
|
5924
6323
|
return Promise.all(
|
|
5925
6324
|
mcpServers.map(async (entry) => {
|
|
5926
6325
|
const ref = entry.credentialRef;
|
|
5927
6326
|
if (!ref) return entry;
|
|
5928
6327
|
let brokered;
|
|
5929
6328
|
try {
|
|
5930
|
-
brokered = await
|
|
6329
|
+
brokered = await resolve27({ credentialRef: ref });
|
|
5931
6330
|
} catch (err) {
|
|
5932
6331
|
console.warn(
|
|
5933
6332
|
`[agent_start] credentialRef resolution failed for "${entry.name}" (${ref}): ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6034,10 +6433,10 @@ function sandboxAuthFromResolved(auth) {
|
|
|
6034
6433
|
};
|
|
6035
6434
|
}
|
|
6036
6435
|
async function resolveSandboxSecret(slug) {
|
|
6037
|
-
const { resolveSandboxSecret:
|
|
6038
|
-
if (!
|
|
6436
|
+
const { resolveSandboxSecret: resolve27 } = getMcpCredentialDeps();
|
|
6437
|
+
if (!resolve27) return null;
|
|
6039
6438
|
try {
|
|
6040
|
-
return await
|
|
6439
|
+
return await resolve27(slug);
|
|
6041
6440
|
} catch (err) {
|
|
6042
6441
|
console.warn(
|
|
6043
6442
|
`[agent_start] sandbox secret resolution failed for "${slug}": ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6491,6 +6890,7 @@ ${contextBlocks.join("\n\n")}` : "";
|
|
|
6491
6890
|
const judgeSessionId = mintSessionId();
|
|
6492
6891
|
const agentSession = await resolved.startSession({
|
|
6493
6892
|
cwd,
|
|
6893
|
+
configDir: adapterConfigDirFor(judgeSessionId),
|
|
6494
6894
|
...spec.model ? { model: spec.model } : {},
|
|
6495
6895
|
env: {
|
|
6496
6896
|
[SESSION_ID_ENV]: judgeSessionId,
|
|
@@ -6505,6 +6905,7 @@ ${contextBlocks.join("\n\n")}` : "";
|
|
|
6505
6905
|
cwd,
|
|
6506
6906
|
agentSession,
|
|
6507
6907
|
adapterSlug: spec.adapter,
|
|
6908
|
+
adapterConfigDir: adapterConfigDirFor(judgeSessionId),
|
|
6508
6909
|
label: `judge:${state.policyId}`,
|
|
6509
6910
|
// PR #800: groups machine-run gate sessions in the VS Code tree.
|
|
6510
6911
|
origin: "gate",
|
|
@@ -7293,8 +7694,8 @@ function createTerminalTranscriptWriter(opts) {
|
|
|
7293
7694
|
const stream = streams.get(sessionId);
|
|
7294
7695
|
if (!stream) return Promise.resolve();
|
|
7295
7696
|
streams.delete(sessionId);
|
|
7296
|
-
return new Promise((
|
|
7297
|
-
stream.end(() =>
|
|
7697
|
+
return new Promise((resolve27) => {
|
|
7698
|
+
stream.end(() => resolve27());
|
|
7298
7699
|
});
|
|
7299
7700
|
},
|
|
7300
7701
|
closeAll() {
|
|
@@ -7303,8 +7704,8 @@ function createTerminalTranscriptWriter(opts) {
|
|
|
7303
7704
|
const stream = streams.get(sessionId);
|
|
7304
7705
|
if (!stream) continue;
|
|
7305
7706
|
closings.push(
|
|
7306
|
-
new Promise((
|
|
7307
|
-
stream.end(() =>
|
|
7707
|
+
new Promise((resolve27) => {
|
|
7708
|
+
stream.end(() => resolve27());
|
|
7308
7709
|
})
|
|
7309
7710
|
);
|
|
7310
7711
|
}
|
|
@@ -7322,7 +7723,7 @@ var defaultResolver = (model) => resolvePricing(model);
|
|
|
7322
7723
|
function tokenCost(tokens, pricePer1M) {
|
|
7323
7724
|
return tokens === void 0 ? 0 : tokens * pricePer1M / 1e6;
|
|
7324
7725
|
}
|
|
7325
|
-
function deriveSessionUsage(input,
|
|
7726
|
+
function deriveSessionUsage(input, resolve27 = defaultResolver) {
|
|
7326
7727
|
const contextUsed = plausibleContextUsed(input.contextSize, input.contextUsed);
|
|
7327
7728
|
const base = {
|
|
7328
7729
|
...input.model !== void 0 ? { model: input.model } : {},
|
|
@@ -7336,7 +7737,7 @@ function deriveSessionUsage(input, resolve26 = defaultResolver) {
|
|
|
7336
7737
|
}
|
|
7337
7738
|
const hasTokens = input.tokensIn !== void 0 || input.tokensOut !== void 0;
|
|
7338
7739
|
if (hasTokens) {
|
|
7339
|
-
const pricing = input.model !== void 0 ?
|
|
7740
|
+
const pricing = input.model !== void 0 ? resolve27(input.model) : void 0;
|
|
7340
7741
|
if (!pricing) {
|
|
7341
7742
|
return { ...base, source: "no-pricing" };
|
|
7342
7743
|
}
|
|
@@ -7404,14 +7805,14 @@ async function buildRecentDigest(sessionId) {
|
|
|
7404
7805
|
}
|
|
7405
7806
|
async function captureGitStatus(cwd) {
|
|
7406
7807
|
if (!cwd) return void 0;
|
|
7407
|
-
return new Promise((
|
|
7808
|
+
return new Promise((resolve27) => {
|
|
7408
7809
|
execFile("git", ["status", "--porcelain"], { cwd }, (err, stdout) => {
|
|
7409
7810
|
if (err) {
|
|
7410
|
-
|
|
7811
|
+
resolve27(void 0);
|
|
7411
7812
|
return;
|
|
7412
7813
|
}
|
|
7413
7814
|
const trimmed = stdout.trim();
|
|
7414
|
-
|
|
7815
|
+
resolve27(trimmed || "(working tree clean)");
|
|
7415
7816
|
});
|
|
7416
7817
|
});
|
|
7417
7818
|
}
|
|
@@ -7665,6 +8066,9 @@ var SESSIONS_FILE_PATH = () => resolve(homedir(), ".agentproto", "sessions.json"
|
|
|
7665
8066
|
function mintSessionId() {
|
|
7666
8067
|
return `sess_${randomUUID().slice(0, 8)}`;
|
|
7667
8068
|
}
|
|
8069
|
+
function adapterConfigDirFor(sessionId) {
|
|
8070
|
+
return resolve(homedir(), ".agentproto", "adapter-config", sessionId);
|
|
8071
|
+
}
|
|
7668
8072
|
var SESSION_ID_ENV = "AGENTPROTO_SESSION_ID";
|
|
7669
8073
|
var WORKSPACE_SLUG_ENV = "AGENTPROTO_WORKSPACE_SLUG";
|
|
7670
8074
|
var PARENT_SESSION_ID_ENV = "AGENTPROTO_PARENT_SESSION_ID";
|
|
@@ -7704,6 +8108,7 @@ function toSessionSummary(desc) {
|
|
|
7704
8108
|
activitySummary: desc.activitySummary,
|
|
7705
8109
|
archived: desc.archived,
|
|
7706
8110
|
keepAlive: desc.keepAlive,
|
|
8111
|
+
pinned: desc.pinned,
|
|
7707
8112
|
pty: desc.pty,
|
|
7708
8113
|
name: desc.name,
|
|
7709
8114
|
argv: desc.argv,
|
|
@@ -7727,6 +8132,7 @@ function toSessionSummary(desc) {
|
|
|
7727
8132
|
blockedOn: desc.blockedOn,
|
|
7728
8133
|
stalledSinceMs: desc.stalledSinceMs,
|
|
7729
8134
|
pendingBgTasks: desc.pendingBgTasks,
|
|
8135
|
+
lastTurnErroredAt: desc.lastTurnErroredAt,
|
|
7730
8136
|
origin: desc.origin,
|
|
7731
8137
|
parentSessionId: desc.parentSessionId,
|
|
7732
8138
|
depth: desc.depth,
|
|
@@ -7894,8 +8300,10 @@ function createSessionsRegistry(opts) {
|
|
|
7894
8300
|
desc.currentPhase = desc.busy ? "thinking" : "idle";
|
|
7895
8301
|
};
|
|
7896
8302
|
const watchersById = /* @__PURE__ */ new Map();
|
|
8303
|
+
const watcherDetailsById = /* @__PURE__ */ new Map();
|
|
7897
8304
|
const stampWatchers = (desc) => {
|
|
7898
8305
|
desc.watchers = watchersById.get(desc.id) ?? 0;
|
|
8306
|
+
desc.watcherDetails = [...watcherDetailsById.get(desc.id) ?? []];
|
|
7899
8307
|
};
|
|
7900
8308
|
const childrenBusyCounts = () => {
|
|
7901
8309
|
const all = Array.from(sessions.values());
|
|
@@ -8510,11 +8918,11 @@ function createSessionsRegistry(opts) {
|
|
|
8510
8918
|
};
|
|
8511
8919
|
const waitForTurnSettled = (rt, id, caller) => {
|
|
8512
8920
|
if (!rt.busy) return Promise.resolve();
|
|
8513
|
-
return new Promise((
|
|
8921
|
+
return new Promise((resolve27, reject) => {
|
|
8514
8922
|
const onBusy = (busy) => {
|
|
8515
8923
|
if (busy) return;
|
|
8516
8924
|
cleanup();
|
|
8517
|
-
|
|
8925
|
+
resolve27();
|
|
8518
8926
|
};
|
|
8519
8927
|
const cleanup = () => {
|
|
8520
8928
|
clearTimeout(timer);
|
|
@@ -8547,6 +8955,30 @@ function createSessionsRegistry(opts) {
|
|
|
8547
8955
|
}
|
|
8548
8956
|
await waitForTurnSettled(rt, id, caller);
|
|
8549
8957
|
};
|
|
8958
|
+
const dispatchQueuedPrompt = (rt) => {
|
|
8959
|
+
const queue = rt.desc.promptQueue;
|
|
8960
|
+
const next = queue?.[0];
|
|
8961
|
+
if (!queue || !next) return;
|
|
8962
|
+
rt.desc.promptQueue = queue.slice(1);
|
|
8963
|
+
schedulePersist();
|
|
8964
|
+
void (async () => {
|
|
8965
|
+
try {
|
|
8966
|
+
await maybeResumeAgent(rt);
|
|
8967
|
+
const liveRt = validateAgentTurn(rt.desc.id, "queue-drain");
|
|
8968
|
+
await runAgentTurn(
|
|
8969
|
+
liveRt,
|
|
8970
|
+
next.message,
|
|
8971
|
+
next.source ? { promptSource: next.source } : void 0
|
|
8972
|
+
);
|
|
8973
|
+
} catch (err) {
|
|
8974
|
+
appendLine(
|
|
8975
|
+
rt,
|
|
8976
|
+
`[error] queued prompt dropped \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
8977
|
+
"stderr"
|
|
8978
|
+
);
|
|
8979
|
+
}
|
|
8980
|
+
})();
|
|
8981
|
+
};
|
|
8550
8982
|
const recordFailedResume = (rt) => {
|
|
8551
8983
|
rt.desc.resumeAttempts = (rt.desc.resumeAttempts ?? 0) + 1;
|
|
8552
8984
|
rt.desc.lastResumeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -8942,6 +9374,13 @@ ${message}`;
|
|
|
8942
9374
|
delete rt.desc.nextRestartAt;
|
|
8943
9375
|
delete rt.desc.recentRestartAts;
|
|
8944
9376
|
}
|
|
9377
|
+
if (turnEndReason === "error") {
|
|
9378
|
+
rt.desc.lastTurnErroredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9379
|
+
schedulePersist();
|
|
9380
|
+
} else if (rt.desc.lastTurnErroredAt !== void 0) {
|
|
9381
|
+
delete rt.desc.lastTurnErroredAt;
|
|
9382
|
+
schedulePersist();
|
|
9383
|
+
}
|
|
8945
9384
|
if (rt.readUsage) {
|
|
8946
9385
|
try {
|
|
8947
9386
|
const usage2 = await rt.readUsage();
|
|
@@ -9048,6 +9487,7 @@ ${message}`;
|
|
|
9048
9487
|
});
|
|
9049
9488
|
}
|
|
9050
9489
|
}
|
|
9490
|
+
dispatchQueuedPrompt(rt);
|
|
9051
9491
|
}
|
|
9052
9492
|
};
|
|
9053
9493
|
const wireOutputStreams = (rt) => {
|
|
@@ -9218,6 +9658,9 @@ ${message}`;
|
|
|
9218
9658
|
// Persist the spawn-time MCP mounts so resume re-mounts the same
|
|
9219
9659
|
// toolset (orchestrator WP1). Reference-only shape — no secrets.
|
|
9220
9660
|
...input.mcpServers ? { mcpServers: input.mcpServers } : {},
|
|
9661
|
+
// Persist the isolated-config location so restart/lazy-resume can
|
|
9662
|
+
// hand the respawned adapter the SAME dir (native-resume store).
|
|
9663
|
+
...input.adapterConfigDir ? { adapterConfigDir: input.adapterConfigDir } : {},
|
|
9221
9664
|
// Parent attribution + depth (orchestrator WP4). Depth is always
|
|
9222
9665
|
// recorded (defaults to 0) so subtree/depth logic never has to
|
|
9223
9666
|
// distinguish "absent" from "root".
|
|
@@ -9324,6 +9767,7 @@ ${message}`;
|
|
|
9324
9767
|
...input.title ? { title: input.title } : {},
|
|
9325
9768
|
...input.label ? { renamedByUser: false } : {},
|
|
9326
9769
|
...input.mcpServers ? { mcpServers: input.mcpServers } : {},
|
|
9770
|
+
...input.adapterConfigDir ? { adapterConfigDir: input.adapterConfigDir } : {},
|
|
9327
9771
|
...input.parentSessionId ? { parentSessionId: input.parentSessionId } : {},
|
|
9328
9772
|
...input.notifyParentOnCrash ? { notifyParentOnCrash: true } : {},
|
|
9329
9773
|
...input.origin ? { origin: input.origin } : {},
|
|
@@ -9695,6 +10139,17 @@ ${message}`;
|
|
|
9695
10139
|
if (opts2?.interrupt && rtPre.busy) {
|
|
9696
10140
|
await interruptInFlightTurn(rtPre, id, "enqueuePrompt");
|
|
9697
10141
|
}
|
|
10142
|
+
if (opts2?.queue && rtPre.busy) {
|
|
10143
|
+
const item = {
|
|
10144
|
+
id: opts2.queueId ?? `q_${randomUUID().slice(0, 8)}`,
|
|
10145
|
+
message,
|
|
10146
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10147
|
+
...opts2.source ? { source: opts2.source } : {}
|
|
10148
|
+
};
|
|
10149
|
+
rtPre.desc.promptQueue = opts2.force ? [item, ...rtPre.desc.promptQueue ?? []] : [...rtPre.desc.promptQueue ?? [], item];
|
|
10150
|
+
schedulePersist();
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
9698
10153
|
await maybeResumeAgent(rtPre);
|
|
9699
10154
|
const rt = validateAgentTurn(id, "enqueuePrompt");
|
|
9700
10155
|
void runAgentTurn(rt, message, opts2?.source ? { promptSource: opts2.source } : void 0).catch((err) => {
|
|
@@ -9705,6 +10160,17 @@ ${message}`;
|
|
|
9705
10160
|
);
|
|
9706
10161
|
});
|
|
9707
10162
|
},
|
|
10163
|
+
removeQueuedPrompt(id, queueId) {
|
|
10164
|
+
const rt = sessions.get(id);
|
|
10165
|
+
if (!rt?.desc.promptQueue?.length) return { removed: false };
|
|
10166
|
+
const next = rt.desc.promptQueue.filter((p) => p.id !== queueId);
|
|
10167
|
+
const removed = next.length !== rt.desc.promptQueue.length;
|
|
10168
|
+
if (removed) {
|
|
10169
|
+
rt.desc.promptQueue = next;
|
|
10170
|
+
schedulePersist();
|
|
10171
|
+
}
|
|
10172
|
+
return { removed };
|
|
10173
|
+
},
|
|
9708
10174
|
async resumeOnBoot(id) {
|
|
9709
10175
|
const rt = sessions.get(id);
|
|
9710
10176
|
if (!rt) return { status: "skipped", reason: "unknown" };
|
|
@@ -9934,13 +10400,26 @@ ${message}`;
|
|
|
9934
10400
|
}
|
|
9935
10401
|
return desc;
|
|
9936
10402
|
},
|
|
9937
|
-
incWatchers(id) {
|
|
10403
|
+
incWatchers(id, detail) {
|
|
9938
10404
|
watchersById.set(id, (watchersById.get(id) ?? 0) + 1);
|
|
10405
|
+
if (detail) {
|
|
10406
|
+
const list = watcherDetailsById.get(id);
|
|
10407
|
+
if (list) list.push(detail);
|
|
10408
|
+
else watcherDetailsById.set(id, [detail]);
|
|
10409
|
+
}
|
|
9939
10410
|
},
|
|
9940
|
-
decWatchers(id) {
|
|
10411
|
+
decWatchers(id, detail) {
|
|
9941
10412
|
const next = (watchersById.get(id) ?? 0) - 1;
|
|
9942
10413
|
if (next > 0) watchersById.set(id, next);
|
|
9943
10414
|
else watchersById.delete(id);
|
|
10415
|
+
if (detail) {
|
|
10416
|
+
const list = watcherDetailsById.get(id);
|
|
10417
|
+
if (list) {
|
|
10418
|
+
const idx = list.indexOf(detail);
|
|
10419
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
10420
|
+
if (list.length === 0) watcherDetailsById.delete(id);
|
|
10421
|
+
}
|
|
10422
|
+
}
|
|
9944
10423
|
},
|
|
9945
10424
|
attach(id, onLine) {
|
|
9946
10425
|
const rt = sessions.get(id);
|
|
@@ -10255,6 +10734,44 @@ ${message}`;
|
|
|
10255
10734
|
stampProcessAlive(rt.desc);
|
|
10256
10735
|
return rt.desc;
|
|
10257
10736
|
},
|
|
10737
|
+
setPinned(id, pinned) {
|
|
10738
|
+
const rt = sessions.get(id);
|
|
10739
|
+
if (!rt) throw new Error(`setPinned: no session "${id}"`);
|
|
10740
|
+
rt.desc.pinned = pinned;
|
|
10741
|
+
schedulePersist();
|
|
10742
|
+
sessionEvents?.emit({
|
|
10743
|
+
type: "session:pinned-changed",
|
|
10744
|
+
sessionId: id,
|
|
10745
|
+
pinned,
|
|
10746
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10747
|
+
});
|
|
10748
|
+
stampProcessAlive(rt.desc);
|
|
10749
|
+
return rt.desc;
|
|
10750
|
+
},
|
|
10751
|
+
flagAwaitingInput(id, patch) {
|
|
10752
|
+
const rt = sessions.get(id);
|
|
10753
|
+
if (!rt) throw new Error(`flagAwaitingInput: no session "${id}"`);
|
|
10754
|
+
const isAlive = rt.desc.status === "running" || rt.desc.status === "starting";
|
|
10755
|
+
if (!isAlive) {
|
|
10756
|
+
throw new Error(
|
|
10757
|
+
`flagAwaitingInput: session "${id}" is ${rt.desc.status}, not live \u2014 only a running/starting session's awaiting-input classification can be corrected.`
|
|
10758
|
+
);
|
|
10759
|
+
}
|
|
10760
|
+
rt.desc.awaitingInput = patch.awaitingInput;
|
|
10761
|
+
rt.desc.awaitingQuestion = patch.awaitingInput && patch.question !== void 0 ? { text: patch.question, source: "structured" } : void 0;
|
|
10762
|
+
schedulePersist();
|
|
10763
|
+
sessionEvents?.emit({
|
|
10764
|
+
type: "session:awaiting-input-flagged",
|
|
10765
|
+
sessionId: id,
|
|
10766
|
+
awaitingInput: patch.awaitingInput,
|
|
10767
|
+
reason: patch.reason,
|
|
10768
|
+
...rt.desc.awaitingQuestion ? { question: rt.desc.awaitingQuestion } : {},
|
|
10769
|
+
...rt.desc.label ? { label: rt.desc.label } : {},
|
|
10770
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
10771
|
+
});
|
|
10772
|
+
stampProcessAlive(rt.desc);
|
|
10773
|
+
return rt.desc;
|
|
10774
|
+
},
|
|
10258
10775
|
listPendingPermissions(filter) {
|
|
10259
10776
|
const all = Array.from(pendingPermissions.values());
|
|
10260
10777
|
const scoped = filter?.sessionId ? all.filter((p) => p.sessionId === filter.sessionId) : all;
|
|
@@ -10422,109 +10939,6 @@ function quoteArg(arg) {
|
|
|
10422
10939
|
if (/^[a-zA-Z0-9._/=:@,+-]+$/.test(arg)) return arg;
|
|
10423
10940
|
return `"${arg.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
10424
10941
|
}
|
|
10425
|
-
var MARKER = "@agentproto-bot";
|
|
10426
|
-
var fmtTokens = (n) => {
|
|
10427
|
-
if (typeof n !== "number" || !Number.isFinite(n)) return null;
|
|
10428
|
-
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
10429
|
-
};
|
|
10430
|
-
function cwdLabel(cwd, workspaceSlug) {
|
|
10431
|
-
const leaf = basename(cwd);
|
|
10432
|
-
if (!workspaceSlug || workspaceSlug === leaf) return workspaceSlug ?? leaf;
|
|
10433
|
-
return `${workspaceSlug}/${leaf}`;
|
|
10434
|
-
}
|
|
10435
|
-
var buildFooter = ({
|
|
10436
|
-
prov,
|
|
10437
|
-
authMode,
|
|
10438
|
-
runId,
|
|
10439
|
-
runUrl,
|
|
10440
|
-
sha,
|
|
10441
|
-
kind = "review"
|
|
10442
|
-
}) => {
|
|
10443
|
-
const parts = [`\u{1F916} **${MARKER}** \u2014 ${kind}`];
|
|
10444
|
-
if (prov?.sessionId) {
|
|
10445
|
-
parts.push(`session \`${prov.sessionId}\`${prov.label ? ` (\`${prov.label}\`)` : ""}`);
|
|
10446
|
-
}
|
|
10447
|
-
if (prov?.adapter) parts.push([prov.adapter, authMode].filter(Boolean).join(" / "));
|
|
10448
|
-
else if (!prov?.sessionId) parts.push(`legacy fallback${authMode ? ` (${authMode})` : ""}`);
|
|
10449
|
-
else if (authMode) parts.push(authMode);
|
|
10450
|
-
if (prov?.authProfile) parts.push(`auth-profile \`${prov.authProfile}\``);
|
|
10451
|
-
if (prov?.model) parts.push(`model \`${prov.model}\``);
|
|
10452
|
-
if (prov?.sandboxId) parts.push(`e2b \`${prov.sandboxId}\``);
|
|
10453
|
-
if (prov?.parentSessionId) parts.push(`supervisor \`${prov.parentSessionId}\``);
|
|
10454
|
-
const tin = fmtTokens(prov?.tokensIn);
|
|
10455
|
-
const tout = fmtTokens(prov?.tokensOut);
|
|
10456
|
-
if (tin || tout) parts.push(`${tin ?? "?"} in / ${tout ?? "?"} out`);
|
|
10457
|
-
if (typeof prov?.costUsd === "number") {
|
|
10458
|
-
parts.push(`$${prov.costUsd.toFixed(4)}${prov.source && prov.source !== "adapter" ? ` (${prov.source})` : ""}`);
|
|
10459
|
-
}
|
|
10460
|
-
const showLocalHostCwd = prov?.source === "local" || prov?.source === "daemon" || !runId;
|
|
10461
|
-
if (runId && !showLocalHostCwd) parts.push(`run [${runId}](${runUrl})`);
|
|
10462
|
-
if (showLocalHostCwd) {
|
|
10463
|
-
if (prov?.host) parts.push(`host \`${prov.host}\``);
|
|
10464
|
-
if (prov?.cwd) parts.push(`cwd \`${cwdLabel(prov.cwd, prov.workspaceSlug)}\``);
|
|
10465
|
-
}
|
|
10466
|
-
if (sha) parts.push(`sha \`${sha.slice(0, 7)}\``);
|
|
10467
|
-
return `
|
|
10468
|
-
|
|
10469
|
-
---
|
|
10470
|
-
<sub>${parts.join(" \xB7 ")}</sub>`;
|
|
10471
|
-
};
|
|
10472
|
-
function sessionFooterProvenance(session, options = {}) {
|
|
10473
|
-
const prov = {
|
|
10474
|
-
sessionId: session.id,
|
|
10475
|
-
label: session.label,
|
|
10476
|
-
adapter: session.harness ?? session.adapterSlug,
|
|
10477
|
-
model: session.model,
|
|
10478
|
-
authProfile: session.accessProfile?.label ?? session.accessProfile?.profileRef,
|
|
10479
|
-
parentSessionId: options.supervisor?.id,
|
|
10480
|
-
costUsd: session.costUsd,
|
|
10481
|
-
tokensIn: session.tokensIn,
|
|
10482
|
-
tokensOut: session.tokensOut,
|
|
10483
|
-
source: options.source ?? "daemon",
|
|
10484
|
-
host: options.host,
|
|
10485
|
-
cwd: session.cwd,
|
|
10486
|
-
workspaceSlug: session.workspaceSlug
|
|
10487
|
-
};
|
|
10488
|
-
return { prov, authMode: session.auth?.mode };
|
|
10489
|
-
}
|
|
10490
|
-
function buildSessionPrFooter(session, options = {}) {
|
|
10491
|
-
const { prov, authMode } = sessionFooterProvenance(session, options);
|
|
10492
|
-
return buildFooter({ prov, authMode, sha: options.sha, kind: "PR" });
|
|
10493
|
-
}
|
|
10494
|
-
function appendFooterOnce(body, footer) {
|
|
10495
|
-
if (body.includes(MARKER)) return body;
|
|
10496
|
-
return `${body}${footer}`;
|
|
10497
|
-
}
|
|
10498
|
-
function parseGhPrCreate(command, args, stdout) {
|
|
10499
|
-
if (basename(command) !== "gh") return null;
|
|
10500
|
-
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
10501
|
-
if (positionals[0] !== "pr" || positionals[1] !== "create") return null;
|
|
10502
|
-
const re = /https?:\/\/\S+?\/pull\/(\d+)/g;
|
|
10503
|
-
let match;
|
|
10504
|
-
let last = null;
|
|
10505
|
-
while ((match = re.exec(stdout)) !== null) {
|
|
10506
|
-
last = { url: match[0], number: Number(match[1]) };
|
|
10507
|
-
}
|
|
10508
|
-
return last;
|
|
10509
|
-
}
|
|
10510
|
-
function cwdRelated(sessionCwd, cwd) {
|
|
10511
|
-
if (sessionCwd === cwd) return true;
|
|
10512
|
-
const sep2 = "/";
|
|
10513
|
-
return cwd.startsWith(sessionCwd + sep2) || sessionCwd.startsWith(cwd + sep2);
|
|
10514
|
-
}
|
|
10515
|
-
function pickExecutorSession(sessions, cwd) {
|
|
10516
|
-
const candidates = sessions.filter(
|
|
10517
|
-
(s) => s.kind === "agent-cli" && typeof s.cwd === "string" && cwdRelated(s.cwd, cwd)
|
|
10518
|
-
);
|
|
10519
|
-
if (candidates.length === 0) return void 0;
|
|
10520
|
-
const alive = (s) => s.status === "running" || s.status === "starting";
|
|
10521
|
-
const byRecency = (a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? "");
|
|
10522
|
-
const live = candidates.filter(alive).sort(byRecency);
|
|
10523
|
-
if (live.length > 0) return live[0];
|
|
10524
|
-
return [...candidates].sort(byRecency)[0];
|
|
10525
|
-
}
|
|
10526
|
-
|
|
10527
|
-
// src/pr-provenance-stamp.ts
|
|
10528
10942
|
async function stampPrProvenance(input) {
|
|
10529
10943
|
try {
|
|
10530
10944
|
if (input.exitCode !== 0) return { stamped: false, reason: "command failed" };
|
|
@@ -10584,14 +10998,14 @@ async function stampFooterOnPr(input) {
|
|
|
10584
10998
|
}
|
|
10585
10999
|
var defaultGhRunner = async (args, cwd) => {
|
|
10586
11000
|
const { spawn: spawn7 } = await import('child_process');
|
|
10587
|
-
return await new Promise((
|
|
11001
|
+
return await new Promise((resolve27) => {
|
|
10588
11002
|
let stdout = "";
|
|
10589
11003
|
const child = spawn7("gh", [...args], { cwd, shell: false });
|
|
10590
11004
|
child.stdout?.on("data", (d) => {
|
|
10591
11005
|
stdout += d.toString("utf8");
|
|
10592
11006
|
});
|
|
10593
|
-
child.on("error", () =>
|
|
10594
|
-
child.on("close", (code) =>
|
|
11007
|
+
child.on("error", () => resolve27({ exitCode: 1, stdout }));
|
|
11008
|
+
child.on("close", (code) => resolve27({ exitCode: code ?? 1, stdout }));
|
|
10595
11009
|
});
|
|
10596
11010
|
};
|
|
10597
11011
|
var ALLOWLIST_REL = ".agentproto/allowed-commands.json";
|
|
@@ -12690,6 +13104,7 @@ function stringifyValues(raw) {
|
|
|
12690
13104
|
}
|
|
12691
13105
|
return out;
|
|
12692
13106
|
}
|
|
13107
|
+
init_config();
|
|
12693
13108
|
var RestartOverrideError = class extends Error {
|
|
12694
13109
|
code = "restart_override_invalid";
|
|
12695
13110
|
status = 400;
|
|
@@ -12960,9 +13375,11 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
|
|
|
12960
13375
|
const message = err instanceof Error ? err.message : String(err);
|
|
12961
13376
|
throw new Error(message);
|
|
12962
13377
|
}
|
|
13378
|
+
const restartConfigDir = prev.adapterConfigDir ?? adapterConfigDirFor(restartedSessionId);
|
|
12963
13379
|
const agentSession = await resolved.startSession({
|
|
12964
13380
|
cwd,
|
|
12965
13381
|
...resumeSessionId ? { resumeSessionId } : {},
|
|
13382
|
+
configDir: restartConfigDir,
|
|
12966
13383
|
...launchConfig.wireModel ? { model: launchConfig.wireModel } : {},
|
|
12967
13384
|
...effEffort ? { effort: effEffort } : {},
|
|
12968
13385
|
...effPosture !== void 0 ? { posture: effPosture } : {},
|
|
@@ -12988,6 +13405,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
|
|
|
12988
13405
|
cwd,
|
|
12989
13406
|
agentSession,
|
|
12990
13407
|
adapterSlug,
|
|
13408
|
+
adapterConfigDir: restartConfigDir,
|
|
12991
13409
|
...resolved.resumable !== void 0 ? { resumable: resolved.resumable } : {},
|
|
12992
13410
|
...resolved.nativeTerminalResume !== void 0 ? { nativeTerminalResume: resolved.nativeTerminalResume } : {},
|
|
12993
13411
|
harness: effHarness,
|
|
@@ -15000,6 +15418,91 @@ function registerSessionTools(rawServer, opts) {
|
|
|
15000
15418
|
}
|
|
15001
15419
|
}
|
|
15002
15420
|
);
|
|
15421
|
+
server.tool(
|
|
15422
|
+
"session_flag_status",
|
|
15423
|
+
"Manually correct a session's `awaitingInput`/`awaitingQuestion` classification. This is the ONLY write path for that pair besides the daemon's own internal heuristic (which guesses from the tail of the transcript) and a driver-reported structured prompt \u2014 use this when the heuristic missed a real question (set `awaitingInput:true`, optionally attaching `question`) or flagged a false positive (set `awaitingInput:false`, which also clears any attached `awaitingQuestion` \u2014 a question can't outlive its awaiting-input flag). `reason` is required \u2014 a short justification that rides on the emitted `session:awaiting-input-flagged` event, visible via `session_events_poll`, for audit. Only allowed on a LIVE session (running/starting) \u2014 mirrors the inverse of `session_archive`'s terminal-only guard: a terminal session has no turn left to be awaiting anything. The override itself is NOT sticky \u2014 it's cleared automatically like any other awaiting-input signal on the session's next prompt/turn start.",
|
|
15424
|
+
{
|
|
15425
|
+
idOrName: z.string().min(1).describe("Session id or name to flag \u2014 from `session_list`, must be live."),
|
|
15426
|
+
awaitingInput: z.boolean().describe(
|
|
15427
|
+
"New value for the session's awaiting-input classification \u2014 true if it's actually blocked on a question/decision the heuristic missed, false to clear a false positive."
|
|
15428
|
+
),
|
|
15429
|
+
question: z.string().min(1).optional().describe(
|
|
15430
|
+
'The question text to attach when `awaitingInput:true` \u2014 stored as `awaitingQuestion` (`source:"structured"`). Only meaningful alongside `awaitingInput:true`; passing it with `awaitingInput:false` is a validation error.'
|
|
15431
|
+
),
|
|
15432
|
+
reason: z.string().min(1).describe(
|
|
15433
|
+
"Required short justification for this override \u2014 audit/log only, rides on the emitted `session:awaiting-input-flagged` event."
|
|
15434
|
+
)
|
|
15435
|
+
},
|
|
15436
|
+
async (input) => {
|
|
15437
|
+
if (!input.awaitingInput && input.question !== void 0) {
|
|
15438
|
+
return {
|
|
15439
|
+
content: [
|
|
15440
|
+
{
|
|
15441
|
+
type: "text",
|
|
15442
|
+
text: JSON.stringify({
|
|
15443
|
+
error: "session_flag_status: `question` is only meaningful when `awaitingInput:true` (got `awaitingInput:false` with a `question` set)."
|
|
15444
|
+
})
|
|
15445
|
+
}
|
|
15446
|
+
],
|
|
15447
|
+
isError: true
|
|
15448
|
+
};
|
|
15449
|
+
}
|
|
15450
|
+
const prev = registry.findByIdOrName(input.idOrName);
|
|
15451
|
+
if (!prev) {
|
|
15452
|
+
return {
|
|
15453
|
+
content: [
|
|
15454
|
+
{
|
|
15455
|
+
type: "text",
|
|
15456
|
+
text: JSON.stringify({ error: `no session "${input.idOrName}" found` })
|
|
15457
|
+
}
|
|
15458
|
+
],
|
|
15459
|
+
isError: true
|
|
15460
|
+
};
|
|
15461
|
+
}
|
|
15462
|
+
if (callerScope) {
|
|
15463
|
+
const subtree = collectSubtree(
|
|
15464
|
+
callerScope.ownerSessionId,
|
|
15465
|
+
registry.list({ includeArchived: true })
|
|
15466
|
+
);
|
|
15467
|
+
if (!subtree.has(prev.id)) {
|
|
15468
|
+
return {
|
|
15469
|
+
content: [
|
|
15470
|
+
{
|
|
15471
|
+
type: "text",
|
|
15472
|
+
text: JSON.stringify({
|
|
15473
|
+
error: "orchestrator_session_out_of_scope",
|
|
15474
|
+
message: `session_flag_status: session "${prev.id}" is not in your subtree \u2014 a scoped orchestrator can only flag sessions it (transitively) spawned.`,
|
|
15475
|
+
ok: false,
|
|
15476
|
+
sessionId: prev.id
|
|
15477
|
+
})
|
|
15478
|
+
}
|
|
15479
|
+
],
|
|
15480
|
+
isError: true
|
|
15481
|
+
};
|
|
15482
|
+
}
|
|
15483
|
+
}
|
|
15484
|
+
try {
|
|
15485
|
+
const desc = registry.flagAwaitingInput(prev.id, {
|
|
15486
|
+
awaitingInput: input.awaitingInput,
|
|
15487
|
+
...input.question !== void 0 ? { question: input.question } : {},
|
|
15488
|
+
reason: input.reason
|
|
15489
|
+
});
|
|
15490
|
+
return {
|
|
15491
|
+
content: [{ type: "text", text: JSON.stringify(desc, null, 2) }]
|
|
15492
|
+
};
|
|
15493
|
+
} catch (err) {
|
|
15494
|
+
return {
|
|
15495
|
+
content: [
|
|
15496
|
+
{
|
|
15497
|
+
type: "text",
|
|
15498
|
+
text: `session_flag_status: ${err instanceof Error ? err.message : String(err)}`
|
|
15499
|
+
}
|
|
15500
|
+
],
|
|
15501
|
+
isError: true
|
|
15502
|
+
};
|
|
15503
|
+
}
|
|
15504
|
+
}
|
|
15505
|
+
);
|
|
15003
15506
|
server.tool(
|
|
15004
15507
|
"session_rename",
|
|
15005
15508
|
"Set or clear a session's user-facing name \u2014 the label the sessions tree, transcript header, and tab show. `label` out-ranks `title` in that display chain, so a user rename should write `label` (the default a UI picks) to be sure it shows; `title` is the auto-derived first-sentence fallback. For EACH of `title`/`label`: a non-empty string sets it (trimmed + length-capped), an empty string clears it (reverting to the derived title / a friendly `adapter \xB7 id` fallback), and omitting it leaves that field untouched. Persists across daemon restarts. Does NOT rename the adapter-native session or touch the running agent.",
|
|
@@ -15146,6 +15649,68 @@ function registerSessionTools(rawServer, opts) {
|
|
|
15146
15649
|
}
|
|
15147
15650
|
}
|
|
15148
15651
|
);
|
|
15652
|
+
server.tool(
|
|
15653
|
+
"session_set_pinned",
|
|
15654
|
+
"Set or clear a session's list-visibility pin. When `pinned` is true, the session sorts to the top of `agentproto sessions` and the VS Code sessions webview's dedicated Pinned group. Set false to clear it. Persists across daemon restarts. Purely a sort/display flag \u2014 does NOT touch the idle-reaper, keepAlive, or emit any notification, and does NOT touch the running agent.",
|
|
15655
|
+
{
|
|
15656
|
+
idOrName: z.string().min(1).describe("Session id or name to update \u2014 from `session_list`."),
|
|
15657
|
+
pinned: mcpBool2.describe(
|
|
15658
|
+
"true to pin this session to the top of the list, false to unpin it."
|
|
15659
|
+
)
|
|
15660
|
+
},
|
|
15661
|
+
async (input) => {
|
|
15662
|
+
const prev = registry.findByIdOrName(input.idOrName);
|
|
15663
|
+
if (!prev) {
|
|
15664
|
+
return {
|
|
15665
|
+
content: [
|
|
15666
|
+
{
|
|
15667
|
+
type: "text",
|
|
15668
|
+
text: JSON.stringify({ error: `no session "${input.idOrName}" found` })
|
|
15669
|
+
}
|
|
15670
|
+
],
|
|
15671
|
+
isError: true
|
|
15672
|
+
};
|
|
15673
|
+
}
|
|
15674
|
+
if (callerScope) {
|
|
15675
|
+
const subtree = collectSubtree(
|
|
15676
|
+
callerScope.ownerSessionId,
|
|
15677
|
+
registry.list({ includeArchived: true })
|
|
15678
|
+
);
|
|
15679
|
+
if (!subtree.has(prev.id)) {
|
|
15680
|
+
return {
|
|
15681
|
+
content: [
|
|
15682
|
+
{
|
|
15683
|
+
type: "text",
|
|
15684
|
+
text: JSON.stringify({
|
|
15685
|
+
error: "orchestrator_session_out_of_scope",
|
|
15686
|
+
message: `session_set_pinned: session "${prev.id}" is not in your subtree \u2014 a scoped orchestrator can only update sessions it (transitively) spawned.`,
|
|
15687
|
+
ok: false,
|
|
15688
|
+
sessionId: prev.id
|
|
15689
|
+
})
|
|
15690
|
+
}
|
|
15691
|
+
],
|
|
15692
|
+
isError: true
|
|
15693
|
+
};
|
|
15694
|
+
}
|
|
15695
|
+
}
|
|
15696
|
+
try {
|
|
15697
|
+
const desc = registry.setPinned(prev.id, input.pinned);
|
|
15698
|
+
return {
|
|
15699
|
+
content: [{ type: "text", text: JSON.stringify(desc, null, 2) }]
|
|
15700
|
+
};
|
|
15701
|
+
} catch (err) {
|
|
15702
|
+
return {
|
|
15703
|
+
content: [
|
|
15704
|
+
{
|
|
15705
|
+
type: "text",
|
|
15706
|
+
text: `session_set_pinned: ${err instanceof Error ? err.message : String(err)}`
|
|
15707
|
+
}
|
|
15708
|
+
],
|
|
15709
|
+
isError: true
|
|
15710
|
+
};
|
|
15711
|
+
}
|
|
15712
|
+
}
|
|
15713
|
+
);
|
|
15149
15714
|
server.tool(
|
|
15150
15715
|
"terminal_start",
|
|
15151
15716
|
"Spawn a process under a real PTY (node-pty) on the host. Bytes (including ANSI escapes, alt-screen sequences) flow through the daemon's byte ring buffer; subscribers attach via the WS at /sessions/:id/pty. Use for interactive TUIs (claude, vim, htop) or to orchestrate shells from another agent. Returns the session descriptor.",
|
|
@@ -19715,7 +20280,7 @@ async function waitForSessionTerminal(registry, sessionId) {
|
|
|
19715
20280
|
for (let attempt = 0; attempt < MAX_SEQUENTIAL_POLLS; attempt++) {
|
|
19716
20281
|
const status = registry.get(sessionId)?.status;
|
|
19717
20282
|
if (isSessionTerminal(status)) return;
|
|
19718
|
-
await new Promise((
|
|
20283
|
+
await new Promise((resolve27) => setTimeout(resolve27, SEQUENTIAL_POLL_INTERVAL_MS));
|
|
19719
20284
|
}
|
|
19720
20285
|
}
|
|
19721
20286
|
function resolveAgentRefsForWorkflow(appRegistry, workflowId) {
|
|
@@ -21205,14 +21770,14 @@ function createSupervisorTaskGateRunner(opts) {
|
|
|
21205
21770
|
const { supervisor, registry, workspace } = opts;
|
|
21206
21771
|
const exec = opts.runCommand ?? runCommand;
|
|
21207
21772
|
const settleTimeoutMs = opts.settleTimeoutMs ?? DEFAULT_VERIFY_SETTLE_TIMEOUT_MS;
|
|
21208
|
-
const waitForSettle = (policyId) => new Promise((
|
|
21773
|
+
const waitForSettle = (policyId) => new Promise((resolve27) => {
|
|
21209
21774
|
let settled = false;
|
|
21210
21775
|
const finish = (outcome) => {
|
|
21211
21776
|
if (settled) return;
|
|
21212
21777
|
settled = true;
|
|
21213
21778
|
clearTimeout(timer);
|
|
21214
21779
|
unsub();
|
|
21215
|
-
|
|
21780
|
+
resolve27(outcome);
|
|
21216
21781
|
};
|
|
21217
21782
|
const read = () => {
|
|
21218
21783
|
const state = supervisor.getStatus(policyId);
|
|
@@ -22357,17 +22922,26 @@ async function monitorSessionWait(opts) {
|
|
|
22357
22922
|
};
|
|
22358
22923
|
}
|
|
22359
22924
|
}
|
|
22360
|
-
return new Promise((
|
|
22925
|
+
return new Promise((resolve27) => {
|
|
22361
22926
|
const unsubs = [];
|
|
22362
22927
|
let settled = false;
|
|
22928
|
+
const watcherLabel = callerScope?.ownerSessionId ? registry.get(callerScope.ownerSessionId)?.label ?? registry.get(callerScope.ownerSessionId)?.title : void 0;
|
|
22929
|
+
const watcherDetail = {
|
|
22930
|
+
...callerScope?.ownerSessionId ? { watcherSessionId: callerScope.ownerSessionId } : {},
|
|
22931
|
+
...watcherLabel ? { watcherLabel } : {},
|
|
22932
|
+
event: targetEvent,
|
|
22933
|
+
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
22934
|
+
since: (/* @__PURE__ */ new Date()).toISOString()
|
|
22935
|
+
};
|
|
22363
22936
|
for (const id of resolvedIds) {
|
|
22364
|
-
registry.incWatchers(id);
|
|
22937
|
+
registry.incWatchers(id, watcherDetail);
|
|
22365
22938
|
const watchers = registry.get(id)?.watchers ?? 0;
|
|
22366
22939
|
sessionEvents.emit({
|
|
22367
22940
|
type: "session:watcher-attached",
|
|
22368
22941
|
sessionId: id,
|
|
22369
22942
|
watchers,
|
|
22370
22943
|
...callerScope?.ownerSessionId ? { watcherSessionId: callerScope.ownerSessionId } : {},
|
|
22944
|
+
...watcherLabel ? { label: watcherLabel } : {},
|
|
22371
22945
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
22372
22946
|
});
|
|
22373
22947
|
}
|
|
@@ -22377,17 +22951,18 @@ async function monitorSessionWait(opts) {
|
|
|
22377
22951
|
clearTimeout(timer);
|
|
22378
22952
|
for (const u of unsubs) u();
|
|
22379
22953
|
for (const id of resolvedIds) {
|
|
22380
|
-
registry.decWatchers(id);
|
|
22954
|
+
registry.decWatchers(id, watcherDetail);
|
|
22381
22955
|
const watchers = registry.get(id)?.watchers ?? 0;
|
|
22382
22956
|
sessionEvents.emit({
|
|
22383
22957
|
type: "session:watcher-detached",
|
|
22384
22958
|
sessionId: id,
|
|
22385
22959
|
watchers,
|
|
22386
22960
|
...callerScope?.ownerSessionId ? { watcherSessionId: callerScope.ownerSessionId } : {},
|
|
22961
|
+
...watcherLabel ? { label: watcherLabel } : {},
|
|
22387
22962
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
22388
22963
|
});
|
|
22389
22964
|
}
|
|
22390
|
-
|
|
22965
|
+
resolve27(result);
|
|
22391
22966
|
};
|
|
22392
22967
|
const relevantTypes = targetEvent === "any" ? ["session:turn-end", "session:awaiting-input", "session:exited"] : targetEvent === "turn-end" ? ["session:turn-end", "session:awaiting-input"] : targetEvent === "awaiting-input" ? ["session:awaiting-input"] : ["session:exited"];
|
|
22393
22968
|
const idSet = new Set(resolvedIds);
|
|
@@ -22424,13 +22999,13 @@ async function monitorPolicyWait(opts) {
|
|
|
22424
22999
|
if (isSettledStatus(initial.status)) {
|
|
22425
23000
|
return { timedOut: false, state: initial };
|
|
22426
23001
|
}
|
|
22427
|
-
return new Promise((
|
|
23002
|
+
return new Promise((resolve27) => {
|
|
22428
23003
|
let settled = false;
|
|
22429
23004
|
const timer = setTimeout(() => {
|
|
22430
23005
|
if (settled) return;
|
|
22431
23006
|
settled = true;
|
|
22432
23007
|
unsub();
|
|
22433
|
-
|
|
23008
|
+
resolve27({ timedOut: true });
|
|
22434
23009
|
}, timeoutMs);
|
|
22435
23010
|
const unsub = supervisor.onSettle((id) => {
|
|
22436
23011
|
if (id !== policyId) return;
|
|
@@ -22440,7 +23015,7 @@ async function monitorPolicyWait(opts) {
|
|
|
22440
23015
|
settled = true;
|
|
22441
23016
|
clearTimeout(timer);
|
|
22442
23017
|
unsub();
|
|
22443
|
-
|
|
23018
|
+
resolve27({ timedOut: false, state });
|
|
22444
23019
|
});
|
|
22445
23020
|
});
|
|
22446
23021
|
}
|
|
@@ -23804,6 +24379,14 @@ async function startHttpServer(opts) {
|
|
|
23804
24379
|
workspace: opts.meta.workspace,
|
|
23805
24380
|
registered: opts.meta.registered,
|
|
23806
24381
|
uptimeMs: Date.now() - startedAt,
|
|
24382
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
24383
|
+
// What is actually running — version, process, and the exact
|
|
24384
|
+
// node+entry pair launchd (or the shell) exec'd. Lifecycle tooling
|
|
24385
|
+
// (`agentproto daemon start/stop/status`) reports these.
|
|
24386
|
+
version: opts.meta.version ?? null,
|
|
24387
|
+
pid: process.pid,
|
|
24388
|
+
node: process.execPath,
|
|
24389
|
+
entry: process.argv[1] ?? null,
|
|
23807
24390
|
resumeSessionsOnBoot: opts.meta.resumeSessionsOnBoot === true,
|
|
23808
24391
|
idleReapAfterMs: opts.meta.idleReapAfterMs ?? 0,
|
|
23809
24392
|
crashDetectIntervalMs: opts.meta.crashDetectIntervalMs ?? 0,
|
|
@@ -24880,16 +25463,16 @@ async function startHttpServer(opts) {
|
|
|
24880
25463
|
});
|
|
24881
25464
|
});
|
|
24882
25465
|
const bind = opts.bind ?? "127.0.0.1";
|
|
24883
|
-
await new Promise((
|
|
25466
|
+
await new Promise((resolve27, reject) => {
|
|
24884
25467
|
server.once("error", reject);
|
|
24885
|
-
server.listen(opts.port, bind, () =>
|
|
25468
|
+
server.listen(opts.port, bind, () => resolve27());
|
|
24886
25469
|
});
|
|
24887
25470
|
return {
|
|
24888
25471
|
url: `http://${bind}:${opts.port}`,
|
|
24889
25472
|
async stop() {
|
|
24890
25473
|
wss.close();
|
|
24891
25474
|
server.closeAllConnections();
|
|
24892
|
-
await new Promise((
|
|
25475
|
+
await new Promise((resolve27) => server.close(() => resolve27()));
|
|
24893
25476
|
}
|
|
24894
25477
|
};
|
|
24895
25478
|
}
|
|
@@ -25539,6 +26122,8 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25539
26122
|
const body = await readJsonBody(req);
|
|
25540
26123
|
const prompt = body?.prompt;
|
|
25541
26124
|
const interrupt = body?.interrupt === true;
|
|
26125
|
+
const queue = body?.queue === true;
|
|
26126
|
+
const force = body?.force === true;
|
|
25542
26127
|
const validPrompt = typeof prompt === "string" && prompt.length > 0 || Array.isArray(prompt) && prompt.length > 0 && prompt.every((b) => b !== null && typeof b === "object") || prompt !== null && typeof prompt === "object" && !Array.isArray(prompt);
|
|
25543
26128
|
if (!validPrompt) {
|
|
25544
26129
|
json(400, {
|
|
@@ -25553,8 +26138,20 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25553
26138
|
const fireAndForget = wait === "false" || wait === "0";
|
|
25554
26139
|
try {
|
|
25555
26140
|
if (fireAndForget) {
|
|
25556
|
-
|
|
25557
|
-
|
|
26141
|
+
const queueId = queue ? `q_${randomUUID().slice(0, 8)}` : void 0;
|
|
26142
|
+
await registry.enqueuePrompt(id2, prompt, { interrupt, queue, force, queueId });
|
|
26143
|
+
const promptQueue = queueId ? registry.get(id2)?.promptQueue : void 0;
|
|
26144
|
+
const queuePosition = promptQueue?.findIndex((p) => p.id === queueId) ?? -1;
|
|
26145
|
+
json(202, {
|
|
26146
|
+
ok: true,
|
|
26147
|
+
id: id2,
|
|
26148
|
+
queued: true,
|
|
26149
|
+
// Present only when this prompt actually landed in the FIFO
|
|
26150
|
+
// (busy + `queue: true`) rather than dispatching immediately —
|
|
26151
|
+
// an idle session's `queueId` never appears in `promptQueue`,
|
|
26152
|
+
// so `queuePosition` stays -1 and this is omitted.
|
|
26153
|
+
...queuePosition >= 0 ? { pending: true, queueId, queuePosition: queuePosition + 1 } : {}
|
|
26154
|
+
});
|
|
25558
26155
|
} else {
|
|
25559
26156
|
await registry.sendPrompt(id2, prompt, { interrupt });
|
|
25560
26157
|
json(200, { ok: true, id: id2 });
|
|
@@ -25570,6 +26167,15 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25570
26167
|
}
|
|
25571
26168
|
return true;
|
|
25572
26169
|
}
|
|
26170
|
+
const queueItemMatch = path.match(/^\/sessions\/([^/]+)\/queue\/([^/]+)$/);
|
|
26171
|
+
if (queueItemMatch && req.method === "DELETE") {
|
|
26172
|
+
const id2 = queueItemMatch[1];
|
|
26173
|
+
const queueId = queueItemMatch[2];
|
|
26174
|
+
if (!id2 || !queueId) return false;
|
|
26175
|
+
const { removed } = registry.removeQueuedPrompt(id2, queueId);
|
|
26176
|
+
json(200, { ok: true, id: id2, queueId, removed });
|
|
26177
|
+
return true;
|
|
26178
|
+
}
|
|
25573
26179
|
const interruptMatch = path.match(/^\/sessions\/([^/]+)\/interrupt$/);
|
|
25574
26180
|
if (interruptMatch && req.method === "POST") {
|
|
25575
26181
|
const id2 = interruptMatch[1];
|
|
@@ -25829,7 +26435,7 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25829
26435
|
return true;
|
|
25830
26436
|
}
|
|
25831
26437
|
const idMatch = path.match(
|
|
25832
|
-
/^\/sessions\/([^/]+)(\/events\/stream|\/stream|\/kill|\/preview|\/export|\/conversation|\/events|\/wait)?$/
|
|
26438
|
+
/^\/sessions\/([^/]+)(\/events\/stream|\/stream|\/kill|\/pin|\/preview|\/export|\/conversation|\/events|\/wait)?$/
|
|
25833
26439
|
);
|
|
25834
26440
|
if (!idMatch) return false;
|
|
25835
26441
|
const [, rawIdOrName, suffix] = idMatch;
|
|
@@ -25904,9 +26510,9 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25904
26510
|
let fileStream;
|
|
25905
26511
|
try {
|
|
25906
26512
|
fileStream = createReadStream(filePath, { encoding: "utf8" });
|
|
25907
|
-
await new Promise((
|
|
26513
|
+
await new Promise((resolve27, reject) => {
|
|
25908
26514
|
fileStream.once("error", reject);
|
|
25909
|
-
fileStream.once("open",
|
|
26515
|
+
fileStream.once("open", resolve27);
|
|
25910
26516
|
});
|
|
25911
26517
|
} catch (err) {
|
|
25912
26518
|
const code = err.code;
|
|
@@ -25962,9 +26568,9 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
25962
26568
|
let fileStream;
|
|
25963
26569
|
try {
|
|
25964
26570
|
fileStream = createReadStream(filePath, { encoding: "utf8" });
|
|
25965
|
-
await new Promise((
|
|
26571
|
+
await new Promise((resolve27, reject) => {
|
|
25966
26572
|
fileStream.once("error", reject);
|
|
25967
|
-
fileStream.once("open",
|
|
26573
|
+
fileStream.once("open", resolve27);
|
|
25968
26574
|
});
|
|
25969
26575
|
} catch (err) {
|
|
25970
26576
|
const code = err.code;
|
|
@@ -26127,6 +26733,27 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
|
|
|
26127
26733
|
json(ok ? 200 : 404, { ok, sessionId: id });
|
|
26128
26734
|
return true;
|
|
26129
26735
|
}
|
|
26736
|
+
if (suffix === "/pin" && req.method === "POST") {
|
|
26737
|
+
if (!resolvedDesc) {
|
|
26738
|
+
json(404, { error: "session_not_found", id: rawIdOrName });
|
|
26739
|
+
return true;
|
|
26740
|
+
}
|
|
26741
|
+
const body = await readJsonBody(req);
|
|
26742
|
+
const b = body && typeof body === "object" ? body : {};
|
|
26743
|
+
const pinned = typeof b.pinned === "boolean" ? b.pinned : b.pinned === "true" ? true : b.pinned === "false" ? false : void 0;
|
|
26744
|
+
if (pinned === void 0) {
|
|
26745
|
+
json(400, { error: "invalid_body", message: "`pinned` must be a boolean" });
|
|
26746
|
+
return true;
|
|
26747
|
+
}
|
|
26748
|
+
try {
|
|
26749
|
+
registry.setPinned(id, pinned);
|
|
26750
|
+
json(200, { ok: true, sessionId: id, pinned });
|
|
26751
|
+
} catch (err) {
|
|
26752
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
26753
|
+
json(msg.includes("no session") ? 404 : 500, { error: "set_pinned_failed", message: msg });
|
|
26754
|
+
}
|
|
26755
|
+
return true;
|
|
26756
|
+
}
|
|
26130
26757
|
if (!suffix && req.method === "GET") {
|
|
26131
26758
|
if (!resolvedDesc) {
|
|
26132
26759
|
json(404, { error: "session_not_found", id: rawIdOrName });
|
|
@@ -27470,6 +28097,9 @@ async function runRestartSweepPass(opts) {
|
|
|
27470
28097
|
}
|
|
27471
28098
|
return summary;
|
|
27472
28099
|
}
|
|
28100
|
+
|
|
28101
|
+
// src/index.ts
|
|
28102
|
+
init_config();
|
|
27473
28103
|
var BINDINGS_FILE_PATH = () => resolve(homedir(), ".agentproto", "transmitter-bindings.json");
|
|
27474
28104
|
var PERSIST_DEBOUNCE_MS4 = 1500;
|
|
27475
28105
|
var keyOf = (alias, source, contactRef) => `${alias}:${source}:${contactRef}`;
|
|
@@ -28791,6 +29421,7 @@ var SessionsRegistryAgentHost = class {
|
|
|
28791
29421
|
const stepSessionId = mintSessionId();
|
|
28792
29422
|
const agentSession = await resolved.startSession({
|
|
28793
29423
|
cwd,
|
|
29424
|
+
configDir: adapterConfigDirFor(stepSessionId),
|
|
28794
29425
|
env: {
|
|
28795
29426
|
[SESSION_ID_ENV]: stepSessionId,
|
|
28796
29427
|
[WORKSPACE_SLUG_ENV]: workspaceSlug
|
|
@@ -28803,6 +29434,7 @@ var SessionsRegistryAgentHost = class {
|
|
|
28803
29434
|
cwd,
|
|
28804
29435
|
agentSession,
|
|
28805
29436
|
adapterSlug: adapter,
|
|
29437
|
+
adapterConfigDir: adapterConfigDirFor(stepSessionId),
|
|
28806
29438
|
label: `agent-step:${adapter}`,
|
|
28807
29439
|
...resolved.commandPreview ? { commandPreview: resolved.commandPreview } : {}
|
|
28808
29440
|
});
|
|
@@ -28892,11 +29524,11 @@ var SessionsRegistryAgentHost = class {
|
|
|
28892
29524
|
}
|
|
28893
29525
|
// ── Internal helpers ──────────────────────────────────────────────────
|
|
28894
29526
|
waitTurnEnd(sessionId) {
|
|
28895
|
-
return new Promise((
|
|
29527
|
+
return new Promise((resolve27, reject) => {
|
|
28896
29528
|
const unsubs = [];
|
|
28897
29529
|
const done = () => {
|
|
28898
29530
|
for (const u of unsubs) u();
|
|
28899
|
-
|
|
29531
|
+
resolve27();
|
|
28900
29532
|
};
|
|
28901
29533
|
const fail = (reason) => {
|
|
28902
29534
|
for (const u of unsubs) u();
|
|
@@ -29140,7 +29772,7 @@ function createOnEscalate(state, persist) {
|
|
|
29140
29772
|
state.run.status = "awaiting-input";
|
|
29141
29773
|
persist();
|
|
29142
29774
|
try {
|
|
29143
|
-
return await new Promise((
|
|
29775
|
+
return await new Promise((resolve27, reject) => {
|
|
29144
29776
|
const timeoutMs = policy.timeoutMs ?? 3e5;
|
|
29145
29777
|
const timer = setTimeout(() => {
|
|
29146
29778
|
state.pendingResolve = void 0;
|
|
@@ -29152,7 +29784,7 @@ function createOnEscalate(state, persist) {
|
|
|
29152
29784
|
resolver: (response) => {
|
|
29153
29785
|
clearTimeout(timer);
|
|
29154
29786
|
state.pendingResolve = void 0;
|
|
29155
|
-
|
|
29787
|
+
resolve27(response);
|
|
29156
29788
|
}
|
|
29157
29789
|
};
|
|
29158
29790
|
});
|
|
@@ -29706,14 +30338,14 @@ function createActivityProjector(opts) {
|
|
|
29706
30338
|
if (current && isTerminalActivityState(current.state)) {
|
|
29707
30339
|
return Promise.resolve(current);
|
|
29708
30340
|
}
|
|
29709
|
-
return new Promise((
|
|
30341
|
+
return new Promise((resolve27) => {
|
|
29710
30342
|
let settled = false;
|
|
29711
30343
|
const finish = (value) => {
|
|
29712
30344
|
if (settled) return;
|
|
29713
30345
|
settled = true;
|
|
29714
30346
|
clearTimeout(timer);
|
|
29715
30347
|
unsubscribeWait();
|
|
29716
|
-
|
|
30348
|
+
resolve27(value);
|
|
29717
30349
|
};
|
|
29718
30350
|
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
29719
30351
|
const unsubscribeWait = opts.sessionEvents.on("activity:changed", (ev) => {
|
|
@@ -29828,6 +30460,7 @@ function createInboundWatcher(opts) {
|
|
|
29828
30460
|
const contactSessionId = mintSessionId();
|
|
29829
30461
|
const agentSession = await resolved.startSession({
|
|
29830
30462
|
cwd: state.input.cwd,
|
|
30463
|
+
configDir: adapterConfigDirFor(contactSessionId),
|
|
29831
30464
|
...mcpServers ? { mcpServers } : {},
|
|
29832
30465
|
env: {
|
|
29833
30466
|
[SESSION_ID_ENV]: contactSessionId,
|
|
@@ -29842,6 +30475,7 @@ function createInboundWatcher(opts) {
|
|
|
29842
30475
|
cwd: state.input.cwd,
|
|
29843
30476
|
agentSession,
|
|
29844
30477
|
adapterSlug: state.input.adapter,
|
|
30478
|
+
adapterConfigDir: adapterConfigDirFor(contactSessionId),
|
|
29845
30479
|
origin: "webhook",
|
|
29846
30480
|
initialPrompt: prompt,
|
|
29847
30481
|
label: labelParts.join(":"),
|
|
@@ -30215,6 +30849,7 @@ function createCronScheduler(opts) {
|
|
|
30215
30849
|
const agentSessionId = mintSessionId();
|
|
30216
30850
|
const agentSession = await resolved.startSession({
|
|
30217
30851
|
cwd,
|
|
30852
|
+
configDir: adapterConfigDirFor(agentSessionId),
|
|
30218
30853
|
...action.model ? { model: action.model } : {},
|
|
30219
30854
|
...action.mode ? { mode: action.mode } : {},
|
|
30220
30855
|
...action.permissionHold ? { permissionHold: true } : {},
|
|
@@ -30230,6 +30865,7 @@ function createCronScheduler(opts) {
|
|
|
30230
30865
|
cwd,
|
|
30231
30866
|
agentSession,
|
|
30232
30867
|
adapterSlug: action.adapter,
|
|
30868
|
+
adapterConfigDir: adapterConfigDirFor(agentSessionId),
|
|
30233
30869
|
origin: "cron",
|
|
30234
30870
|
label: `cron:${job.id}`,
|
|
30235
30871
|
...action.mode ? { mode: action.mode } : {},
|
|
@@ -30777,7 +31413,7 @@ async function registerBuiltinRoutes(opts) {
|
|
|
30777
31413
|
console.log(`[runtime] loaded custom routes: ${loadedIds.join(", ")}`);
|
|
30778
31414
|
}
|
|
30779
31415
|
}
|
|
30780
|
-
function makeBrowserHandle(entry,
|
|
31416
|
+
function makeBrowserHandle(entry, resolve27) {
|
|
30781
31417
|
return {
|
|
30782
31418
|
slug: entry.id,
|
|
30783
31419
|
name: entry.name,
|
|
@@ -30788,7 +31424,7 @@ function makeBrowserHandle(entry, resolve26) {
|
|
|
30788
31424
|
// check() is available for on-demand health probes but is never called
|
|
30789
31425
|
// by the lister (kit invariant OQ-5). Returns true when the adapter
|
|
30790
31426
|
// is present in the injected resolver map, false otherwise.
|
|
30791
|
-
check: async () =>
|
|
31427
|
+
check: async () => resolve27 ? resolve27(entry.id) != null : true
|
|
30792
31428
|
};
|
|
30793
31429
|
}
|
|
30794
31430
|
var noopLedger = {
|
|
@@ -30860,7 +31496,7 @@ var localSandboxProvider = {
|
|
|
30860
31496
|
}
|
|
30861
31497
|
};
|
|
30862
31498
|
async function getFreePort() {
|
|
30863
|
-
return new Promise((
|
|
31499
|
+
return new Promise((resolve27, reject) => {
|
|
30864
31500
|
const srv = createServer$1();
|
|
30865
31501
|
srv.once("error", reject);
|
|
30866
31502
|
srv.listen(0, "127.0.0.1", () => {
|
|
@@ -30870,7 +31506,7 @@ async function getFreePort() {
|
|
|
30870
31506
|
return;
|
|
30871
31507
|
}
|
|
30872
31508
|
const { port } = address;
|
|
30873
|
-
srv.close(() =>
|
|
31509
|
+
srv.close(() => resolve27(port));
|
|
30874
31510
|
});
|
|
30875
31511
|
});
|
|
30876
31512
|
}
|
|
@@ -30883,7 +31519,7 @@ async function probeHealth(url, timeoutMs) {
|
|
|
30883
31519
|
} catch {
|
|
30884
31520
|
}
|
|
30885
31521
|
if (Date.now() >= deadline) return false;
|
|
30886
|
-
await new Promise((
|
|
31522
|
+
await new Promise((resolve27) => setTimeout(resolve27, POLL_INTERVAL_MS));
|
|
30887
31523
|
}
|
|
30888
31524
|
}
|
|
30889
31525
|
|
|
@@ -31291,7 +31927,7 @@ async function spawnCloudflaredUntil(argv, opts) {
|
|
|
31291
31927
|
};
|
|
31292
31928
|
let settled = false;
|
|
31293
31929
|
let forwardedLen = 0;
|
|
31294
|
-
return await new Promise((
|
|
31930
|
+
return await new Promise((resolve27, reject) => {
|
|
31295
31931
|
const poll = setInterval(() => {
|
|
31296
31932
|
const text10 = readAll();
|
|
31297
31933
|
if (opts.onLog) {
|
|
@@ -31309,7 +31945,7 @@ async function spawnCloudflaredUntil(argv, opts) {
|
|
|
31309
31945
|
if (m) {
|
|
31310
31946
|
settled = true;
|
|
31311
31947
|
clearTimeout(timer);
|
|
31312
|
-
|
|
31948
|
+
resolve27({ proc, match: m[0], stopTail: () => clearInterval(poll) });
|
|
31313
31949
|
}
|
|
31314
31950
|
}, POLL_INTERVAL_MS2);
|
|
31315
31951
|
if (poll.unref) poll.unref();
|
|
@@ -31443,15 +32079,15 @@ function quickTunnelProvider() {
|
|
|
31443
32079
|
}
|
|
31444
32080
|
}, 3e3);
|
|
31445
32081
|
timer.unref();
|
|
31446
|
-
await new Promise((
|
|
32082
|
+
await new Promise((resolve27) => {
|
|
31447
32083
|
if (proc.exitCode !== null) {
|
|
31448
32084
|
clearTimeout(timer);
|
|
31449
|
-
|
|
32085
|
+
resolve27();
|
|
31450
32086
|
return;
|
|
31451
32087
|
}
|
|
31452
32088
|
proc.once("exit", () => {
|
|
31453
32089
|
clearTimeout(timer);
|
|
31454
|
-
|
|
32090
|
+
resolve27();
|
|
31455
32091
|
});
|
|
31456
32092
|
});
|
|
31457
32093
|
}
|
|
@@ -31923,15 +32559,15 @@ function namedTunnelProvider(cfg) {
|
|
|
31923
32559
|
}
|
|
31924
32560
|
}, 3e3);
|
|
31925
32561
|
timer.unref();
|
|
31926
|
-
await new Promise((
|
|
32562
|
+
await new Promise((resolve27) => {
|
|
31927
32563
|
if (proc.exitCode !== null) {
|
|
31928
32564
|
clearTimeout(timer);
|
|
31929
|
-
|
|
32565
|
+
resolve27();
|
|
31930
32566
|
return;
|
|
31931
32567
|
}
|
|
31932
32568
|
proc.once("exit", () => {
|
|
31933
32569
|
clearTimeout(timer);
|
|
31934
|
-
|
|
32570
|
+
resolve27();
|
|
31935
32571
|
});
|
|
31936
32572
|
});
|
|
31937
32573
|
}
|
|
@@ -32055,7 +32691,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
32055
32691
|
};
|
|
32056
32692
|
proc.stderr?.on("data", forwardLogs);
|
|
32057
32693
|
proc.stdout?.on("data", forwardLogs);
|
|
32058
|
-
const url = await new Promise((
|
|
32694
|
+
const url = await new Promise((resolve27, reject) => {
|
|
32059
32695
|
let settled = false;
|
|
32060
32696
|
const timer = setTimeout(() => {
|
|
32061
32697
|
if (settled) return;
|
|
@@ -32083,7 +32719,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
32083
32719
|
settled = true;
|
|
32084
32720
|
clearTimeout(timer);
|
|
32085
32721
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
32086
|
-
|
|
32722
|
+
resolve27(pubUrl);
|
|
32087
32723
|
}
|
|
32088
32724
|
} catch {
|
|
32089
32725
|
}
|
|
@@ -32098,7 +32734,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
32098
32734
|
settled = true;
|
|
32099
32735
|
clearTimeout(timer);
|
|
32100
32736
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
32101
|
-
|
|
32737
|
+
resolve27(reMatch[1]);
|
|
32102
32738
|
return;
|
|
32103
32739
|
}
|
|
32104
32740
|
for (const line of text10.split(/\r?\n/)) {
|
|
@@ -32109,7 +32745,7 @@ function ngrokTunnelProvider(opts) {
|
|
|
32109
32745
|
settled = true;
|
|
32110
32746
|
clearTimeout(timer);
|
|
32111
32747
|
if (apiPollHandle) clearInterval(apiPollHandle);
|
|
32112
|
-
|
|
32748
|
+
resolve27(parsed.url);
|
|
32113
32749
|
return;
|
|
32114
32750
|
}
|
|
32115
32751
|
} catch {
|
|
@@ -32156,15 +32792,15 @@ function ngrokTunnelProvider(opts) {
|
|
|
32156
32792
|
}
|
|
32157
32793
|
}, 3e3);
|
|
32158
32794
|
timer.unref();
|
|
32159
|
-
await new Promise((
|
|
32795
|
+
await new Promise((resolve27) => {
|
|
32160
32796
|
if (proc.exitCode !== null) {
|
|
32161
32797
|
clearTimeout(timer);
|
|
32162
|
-
|
|
32798
|
+
resolve27();
|
|
32163
32799
|
return;
|
|
32164
32800
|
}
|
|
32165
32801
|
proc.once("exit", () => {
|
|
32166
32802
|
clearTimeout(timer);
|
|
32167
|
-
|
|
32803
|
+
resolve27();
|
|
32168
32804
|
});
|
|
32169
32805
|
});
|
|
32170
32806
|
}
|
|
@@ -33827,16 +34463,16 @@ function dialUrl(rendezvousUrl, token) {
|
|
|
33827
34463
|
return `${rendezvousUrl}${sep2}side=daemon&t=${encodeURIComponent(token)}`;
|
|
33828
34464
|
}
|
|
33829
34465
|
function waitClosed(sink, signal) {
|
|
33830
|
-
return new Promise((
|
|
34466
|
+
return new Promise((resolve27) => {
|
|
33831
34467
|
if (!sink.isOpen) {
|
|
33832
|
-
|
|
34468
|
+
resolve27();
|
|
33833
34469
|
return;
|
|
33834
34470
|
}
|
|
33835
34471
|
let settled = false;
|
|
33836
34472
|
const finish = () => {
|
|
33837
34473
|
if (settled) return;
|
|
33838
34474
|
settled = true;
|
|
33839
|
-
|
|
34475
|
+
resolve27();
|
|
33840
34476
|
};
|
|
33841
34477
|
sink.onClose(() => finish());
|
|
33842
34478
|
signal.addEventListener("abort", () => {
|
|
@@ -33846,13 +34482,13 @@ function waitClosed(sink, signal) {
|
|
|
33846
34482
|
});
|
|
33847
34483
|
}
|
|
33848
34484
|
function sleep(ms, signal) {
|
|
33849
|
-
return new Promise((
|
|
33850
|
-
if (signal.aborted) return
|
|
33851
|
-
const timer = setTimeout(
|
|
34485
|
+
return new Promise((resolve27) => {
|
|
34486
|
+
if (signal.aborted) return resolve27();
|
|
34487
|
+
const timer = setTimeout(resolve27, ms);
|
|
33852
34488
|
if (typeof timer.unref === "function") timer.unref();
|
|
33853
34489
|
signal.addEventListener("abort", () => {
|
|
33854
34490
|
clearTimeout(timer);
|
|
33855
|
-
|
|
34491
|
+
resolve27();
|
|
33856
34492
|
});
|
|
33857
34493
|
});
|
|
33858
34494
|
}
|
|
@@ -33907,6 +34543,9 @@ function composeMode(cfg, modes) {
|
|
|
33907
34543
|
|
|
33908
34544
|
// src/index.ts
|
|
33909
34545
|
init_conversation_store();
|
|
34546
|
+
|
|
34547
|
+
// src/auth-probe.ts
|
|
34548
|
+
init_config();
|
|
33910
34549
|
async function isAgentCliAuthConfigured(slug, descriptor, model) {
|
|
33911
34550
|
const config = await loadConfig();
|
|
33912
34551
|
const spawnDefaults = resolveSpawnDefaults(config.defaults, slug, {});
|
|
@@ -34062,6 +34701,14 @@ async function createGateway(opts) {
|
|
|
34062
34701
|
return await adapter.startSession({
|
|
34063
34702
|
cwd,
|
|
34064
34703
|
resumeSessionId,
|
|
34704
|
+
// Point the respawned adapter at the SAME persistent
|
|
34705
|
+
// isolated-config dir the original spawn used — the
|
|
34706
|
+
// provider's conversation store lives inside it, so this is
|
|
34707
|
+
// what makes `resumeSessionId` restore full context instead
|
|
34708
|
+
// of degrading to the daemon-transcript digest. Absent on
|
|
34709
|
+
// legacy rows spawned before adapterConfigDir existed (those
|
|
34710
|
+
// keep today's digest-fallback behaviour).
|
|
34711
|
+
...descriptor.adapterConfigDir ? { configDir: descriptor.adapterConfigDir } : {},
|
|
34065
34712
|
// Re-mount the persisted spawn-time toolset on resume
|
|
34066
34713
|
// (orchestrator WP1) — closes the gap where re-spawn
|
|
34067
34714
|
// dropped mcpServers.
|
|
@@ -34586,6 +35233,7 @@ async function createGateway(opts) {
|
|
|
34586
35233
|
workspace,
|
|
34587
35234
|
registered,
|
|
34588
35235
|
startedAt,
|
|
35236
|
+
...opts.version ? { version: opts.version } : {},
|
|
34589
35237
|
resumeSessionsOnBoot: opts.resumeSessionsOnBoot === true,
|
|
34590
35238
|
idleReapAfterMs,
|
|
34591
35239
|
crashDetectIntervalMs,
|