@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/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, resolve, dirname, basename, isAbsolute, normalize, relative, extname, sep } from 'path';
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((resolve26) => {
491
- state.stream.end(() => resolve26());
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((resolve26) => state.stream.end(() => resolve26())));
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((resolve26, reject) => {
648
+ await new Promise((resolve27, reject) => {
649
649
  stream.once("error", reject);
650
- stream.once("open", resolve26);
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((resolve26, reject) => {
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
- resolve26(Buffer.concat(chunks).toString("utf8"));
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((resolve26, reject) => {
980
+ await new Promise((resolve27, reject) => {
981
981
  stream.once("error", reject);
982
- stream.once("open", resolve26);
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((resolve26) => {
3079
- setTimeout(() => resolve26(null), capMs);
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((resolve26) => {
3144
- setTimeout(() => resolve26(null), capMs);
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
- var CONFIG_FILE_PATH = () => join(homedir(), ".agentproto", "config.json");
3484
- function sanitizeAcpAgents(raw, target) {
3485
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
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((resolve26) => setTimeout(resolve26, POLL_RETRY_DELAY_MS));
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((resolve26) => {
5515
- resolveClaim = resolve26;
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((resolve26) => {
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
- resolve26(v);
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: resolve26 } = getMcpCredentialDeps();
5923
- if (!resolve26) return mcpServers;
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 resolve26({ credentialRef: ref });
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: resolve26 } = getMcpCredentialDeps();
6038
- if (!resolve26) return null;
6436
+ const { resolveSandboxSecret: resolve27 } = getMcpCredentialDeps();
6437
+ if (!resolve27) return null;
6039
6438
  try {
6040
- return await resolve26(slug);
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((resolve26) => {
7297
- stream.end(() => resolve26());
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((resolve26) => {
7307
- stream.end(() => resolve26());
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, resolve26 = defaultResolver) {
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 ? resolve26(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((resolve26) => {
7808
+ return new Promise((resolve27) => {
7408
7809
  execFile("git", ["status", "--porcelain"], { cwd }, (err, stdout) => {
7409
7810
  if (err) {
7410
- resolve26(void 0);
7811
+ resolve27(void 0);
7411
7812
  return;
7412
7813
  }
7413
7814
  const trimmed = stdout.trim();
7414
- resolve26(trimmed || "(working tree clean)");
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((resolve26, reject) => {
8921
+ return new Promise((resolve27, reject) => {
8514
8922
  const onBusy = (busy) => {
8515
8923
  if (busy) return;
8516
8924
  cleanup();
8517
- resolve26();
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((resolve26) => {
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", () => resolve26({ exitCode: 1, stdout }));
10594
- child.on("close", (code) => resolve26({ exitCode: code ?? 1, stdout }));
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((resolve26) => setTimeout(resolve26, SEQUENTIAL_POLL_INTERVAL_MS));
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((resolve26) => {
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
- resolve26(outcome);
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((resolve26) => {
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
- resolve26(result);
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((resolve26) => {
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
- resolve26({ timedOut: true });
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
- resolve26({ timedOut: false, state });
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((resolve26, reject) => {
25466
+ await new Promise((resolve27, reject) => {
24884
25467
  server.once("error", reject);
24885
- server.listen(opts.port, bind, () => resolve26());
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((resolve26) => server.close(() => resolve26()));
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
- await registry.enqueuePrompt(id2, prompt, { interrupt });
25557
- json(202, { ok: true, id: id2, queued: true });
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((resolve26, reject) => {
26513
+ await new Promise((resolve27, reject) => {
25908
26514
  fileStream.once("error", reject);
25909
- fileStream.once("open", resolve26);
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((resolve26, reject) => {
26571
+ await new Promise((resolve27, reject) => {
25966
26572
  fileStream.once("error", reject);
25967
- fileStream.once("open", resolve26);
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((resolve26, reject) => {
29527
+ return new Promise((resolve27, reject) => {
28896
29528
  const unsubs = [];
28897
29529
  const done = () => {
28898
29530
  for (const u of unsubs) u();
28899
- resolve26();
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((resolve26, reject) => {
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
- resolve26(response);
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((resolve26) => {
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
- resolve26(value);
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, resolve26) {
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 () => resolve26 ? resolve26(entry.id) != null : true
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((resolve26, reject) => {
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(() => resolve26(port));
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((resolve26) => setTimeout(resolve26, POLL_INTERVAL_MS));
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((resolve26, reject) => {
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
- resolve26({ proc, match: m[0], stopTail: () => clearInterval(poll) });
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((resolve26) => {
32082
+ await new Promise((resolve27) => {
31447
32083
  if (proc.exitCode !== null) {
31448
32084
  clearTimeout(timer);
31449
- resolve26();
32085
+ resolve27();
31450
32086
  return;
31451
32087
  }
31452
32088
  proc.once("exit", () => {
31453
32089
  clearTimeout(timer);
31454
- resolve26();
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((resolve26) => {
32562
+ await new Promise((resolve27) => {
31927
32563
  if (proc.exitCode !== null) {
31928
32564
  clearTimeout(timer);
31929
- resolve26();
32565
+ resolve27();
31930
32566
  return;
31931
32567
  }
31932
32568
  proc.once("exit", () => {
31933
32569
  clearTimeout(timer);
31934
- resolve26();
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((resolve26, reject) => {
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
- resolve26(pubUrl);
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
- resolve26(reMatch[1]);
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
- resolve26(parsed.url);
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((resolve26) => {
32795
+ await new Promise((resolve27) => {
32160
32796
  if (proc.exitCode !== null) {
32161
32797
  clearTimeout(timer);
32162
- resolve26();
32798
+ resolve27();
32163
32799
  return;
32164
32800
  }
32165
32801
  proc.once("exit", () => {
32166
32802
  clearTimeout(timer);
32167
- resolve26();
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((resolve26) => {
34466
+ return new Promise((resolve27) => {
33831
34467
  if (!sink.isOpen) {
33832
- resolve26();
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
- resolve26();
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((resolve26) => {
33850
- if (signal.aborted) return resolve26();
33851
- const timer = setTimeout(resolve26, ms);
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
- resolve26();
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,