@agentproto/runtime 5.8.0 → 5.9.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,12 +1,12 @@
1
1
  import { execFile, spawn, execFileSync } from 'child_process';
2
2
  import { readFile, unlink, mkdir, writeFile, readdir, stat, readlink, chmod, mkdtemp, rm, rename, copyFile, realpath, appendFile, open, access } from 'fs/promises';
3
3
  import { homedir, freemem, loadavg, totalmem, cpus, hostname, tmpdir } from 'os';
4
- import { join, resolve, dirname, basename, isAbsolute, normalize as normalize$1, relative, sep, extname, delimiter } from 'path';
4
+ import { join, resolve, dirname, isAbsolute, basename, normalize as normalize$1, relative, sep, extname, delimiter } from 'path';
5
5
  import { canonicalizePath, resolveCommandSandbox, loadAdapterSpawnSandboxConfig, loadSandboxConfig, COMMAND_SANDBOX_MODE_ENV } from '@agentproto/command-sandbox';
6
6
  import { ensureChromeDevtoolsMcp, resolveChrome, buildHeadlessBrowserMcpEntry, headlessBrowserReadPaths } from '@agentproto/plugin-local-browser';
7
7
  import { resolvePricing, getModelProvider, resolveContextWindow, formatTokens, LLM_PRICING_CATALOG, resolvePricingExact, splitContextWindowHint, MODEL_ALIASES, isKnownLlmId } from '@agentproto/model-catalog/llm';
8
8
  import { randomBytes, randomUUID, timingSafeEqual, createHmac, createHash } from 'crypto';
9
- import { createReadStream, promises, readFileSync, mkdirSync, writeFileSync, renameSync, readdirSync, existsSync, mkdtempSync, chmodSync, openSync, closeSync, fstatSync, readSync, realpathSync, statSync, cpSync, appendFileSync, createWriteStream } from 'fs';
9
+ import { createReadStream, promises, readFileSync, mkdirSync, writeFileSync, renameSync, readdirSync, openSync, fstatSync, readSync, closeSync, existsSync, statSync, mkdtempSync, chmodSync, realpathSync, cpSync, appendFileSync, createWriteStream } from 'fs';
10
10
  import { createInterface } from 'readline';
11
11
  import * as providers_store_star from '@agentproto/providers-store';
12
12
  import { resolveCustomRoute, registerCustomRoute, formatModelRef, resolveLlmModelRoute, tryParseModelRef, stripRouteSuffix, stripFixedNativeVendor } from '@agentproto/model-catalog/route-identity';
@@ -29,7 +29,7 @@ import { SandboxSpecSchema, resolveLifecyclePolicy, createSandboxAgentSessionHos
29
29
  import { APP_UI_DISCOVERY_TOOLS, RUNNER_SELECT_SCRIPT } from '@agentproto/app-client/runner-select';
30
30
  import { DISPLAY_MODE_SCRIPT } from '@agentproto/app-client/display-mode';
31
31
  import { defineTool, catchErrors, ToolError, pageParamsShape, paginated, toToolResult, paginate, toolText } from '@agentproto/tool';
32
- import { toMcpTool, createMcpServer, registerBuiltinTool, registerUiResource } from '@agentproto/mcp-server';
32
+ import { toMcpTool, createMcpServer, registerBuiltinTool, registerUiResource, registerEventsMethods } from '@agentproto/mcp-server';
33
33
  import { SESSION_CHAT_APP_ID, liveSessionApp, makeLiveSessionApp, sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, workBoardApp, reviewPanelApp, makeSessionsPanelApp, makeAgentsOverviewApp, makeBureauSessionsApp, makeSessionStoryPanelApp, makeSessionChatApp, makeWorkBoardApp, makeReviewPanelApp, sessionChatApp } from '@agentproto/apps';
34
34
  import { EventEmitter } from 'events';
35
35
  import { inferLegacyModeKind, parseModelSwitchCommand, isModelSwitchAcknowledgement } from '@agentproto/driver-agent-cli';
@@ -2827,7 +2827,7 @@ function tokenizeCommand(s) {
2827
2827
  if (buf) out.push(buf);
2828
2828
  return out;
2829
2829
  }
2830
- var RESUME_STRATEGIES, RESUME_ID_REJECTED_RE;
2830
+ var RESUME_STRATEGIES, RESUME_ID_REJECTED_RE, RESUME_CONNECTION_LOST_RE;
2831
2831
  var init_resume_strategies = __esm({
2832
2832
  "src/resume-strategies.ts"() {
2833
2833
  init_conversation_store();
@@ -2856,6 +2856,7 @@ var init_resume_strategies = __esm({
2856
2856
  })
2857
2857
  );
2858
2858
  RESUME_ID_REJECTED_RE = /not found|does not support|not supported|unsupported/i;
2859
+ RESUME_CONNECTION_LOST_RE = /connection closed|connection lost|epipe/i;
2859
2860
  }
2860
2861
  });
2861
2862
  function writeCommandLogEntry(sessionId, entry, baseDir) {
@@ -7284,6 +7285,36 @@ var init_bundles = __esm({
7284
7285
  };
7285
7286
  }
7286
7287
  });
7288
+ function directAuthMethods(descriptor, endpoint) {
7289
+ const methods = [];
7290
+ if (subscriptionSurfaceFor(descriptor?.authSubscription, endpoint) !== void 0) {
7291
+ methods.push("oauth-bearer");
7292
+ }
7293
+ if (descriptor?.provider || descriptor?.modelDerivedApiKey) methods.push("api-key");
7294
+ return methods;
7295
+ }
7296
+ function spawnEligibilityManifest(adapter, descriptor, route, model) {
7297
+ const directEndpoint = descriptor?.provider ?? (model ? descriptor?.modelProviders?.[model] : void 0) ?? (model && descriptor?.modelDerivedApiKey ? modelIdPrefixProvider(model) : void 0) ?? (model ? getModelProvider(model) : void 0);
7298
+ const routeId = route?.gateway ?? directEndpoint;
7299
+ if (!routeId) return void 0;
7300
+ const direct = directEndpoint !== void 0 && routeId === directEndpoint;
7301
+ return {
7302
+ manifest: {
7303
+ id: adapter,
7304
+ endpointByRoute: { [routeId]: direct ? directEndpoint : routeId },
7305
+ methodsByRoute: {
7306
+ [routeId]: direct ? directAuthMethods(descriptor, directEndpoint) : ["api-key"]
7307
+ }
7308
+ },
7309
+ routeId,
7310
+ direct
7311
+ };
7312
+ }
7313
+ var init_eligibility_manifest = __esm({
7314
+ "src/eligibility-manifest.ts"() {
7315
+ init_spawn_defaults();
7316
+ }
7317
+ });
7287
7318
  function normalizeRouterPrefixedId(id) {
7288
7319
  const firstSlash = id.indexOf("/");
7289
7320
  if (firstSlash === -1) return id;
@@ -7508,6 +7539,7 @@ function buildCatalogModels(input) {
7508
7539
  ];
7509
7540
  const merged = mergeContributions(contributions);
7510
7541
  const query = input.query ?? {};
7542
+ const queryAdapter = query.adapter ? input.adapters.find((a) => a.slug === query.adapter) : void 0;
7511
7543
  const servableByRoute = /* @__PURE__ */ new Map();
7512
7544
  for (const row of merged) {
7513
7545
  const set = servableByRoute.get(row.route) ?? /* @__PURE__ */ new Set();
@@ -7522,12 +7554,18 @@ function buildCatalogModels(input) {
7522
7554
  if (query.adapter && !row.adapters.includes(query.adapter)) continue;
7523
7555
  const isDirectVendorRoute = row.route === row.vendor && row.adapterModes.length === 0;
7524
7556
  const walletEligible = !isDirectVendorRoute || checkModelWalletEligibility(`${row.vendor}/${row.product}`, row.route).ok;
7525
- const manifest = {
7557
+ const projected = queryAdapter === void 0 ? void 0 : queryAdapter.authDescriptor ? spawnEligibilityManifest(
7558
+ queryAdapter.slug,
7559
+ queryAdapter.authDescriptor,
7560
+ { gateway: row.route },
7561
+ row.ref
7562
+ ) : null;
7563
+ const manifest = projected?.manifest ?? {
7526
7564
  id: `${row.vendor}/${row.product}@${row.route}`,
7527
7565
  endpointByRoute: { [row.route]: billedVendor(row.vendor, row.route) },
7528
7566
  methodsByRoute: { [row.route]: row.methods }
7529
7567
  };
7530
- const eligible = walletEligible ? eligibleProfiles(input.profiles, manifest, row.route).filter(
7568
+ const eligible = walletEligible && projected !== null ? eligibleProfiles(input.profiles, manifest, projected?.routeId ?? row.route).filter(
7531
7569
  (p) => profileAllowsModel(p, row.ref, `${row.vendor}/${row.product}`)
7532
7570
  ) : [];
7533
7571
  const runnable = eligible.length > 0;
@@ -7680,6 +7718,7 @@ var WIDENING_ROUTES, VENDOR_COMPATIBILITY_ROUTES;
7680
7718
  var init_catalog_models = __esm({
7681
7719
  "src/catalog-models.ts"() {
7682
7720
  init_spawn_defaults();
7721
+ init_eligibility_manifest();
7683
7722
  WIDENING_ROUTES = [
7684
7723
  "openrouter",
7685
7724
  "requesty",
@@ -10105,35 +10144,10 @@ function findWorktreeLabelCwdCollision(registry, excludeId, label, cwd) {
10105
10144
  function profileMethodToAuthMode(method) {
10106
10145
  return method === "oauth-bearer" ? "subscription" : "api-key";
10107
10146
  }
10108
- function directAuthMethods(descriptor, endpoint) {
10109
- const methods = [];
10110
- if (subscriptionSurfaceFor(descriptor?.authSubscription, endpoint) !== void 0) {
10111
- methods.push("oauth-bearer");
10112
- }
10113
- if (descriptor?.provider || descriptor?.modelDerivedApiKey) methods.push("api-key");
10114
- return methods;
10115
- }
10116
10147
  function subscriptionOauthMethodId(descriptor, provider) {
10117
10148
  if (!Array.isArray(descriptor?.authSubscription) || !provider) return void 0;
10118
10149
  return `${provider}-oauth`;
10119
10150
  }
10120
- function spawnEligibilityManifest(adapter, descriptor, route, model) {
10121
- const directEndpoint = descriptor?.provider ?? (model ? descriptor?.modelProviders?.[model] : void 0) ?? (model && descriptor?.modelDerivedApiKey ? modelIdPrefixProvider(model) : void 0) ?? (model ? getModelProvider(model) : void 0);
10122
- const routeId = route?.gateway ?? directEndpoint;
10123
- if (!routeId) return void 0;
10124
- const direct = directEndpoint !== void 0 && routeId === directEndpoint;
10125
- return {
10126
- manifest: {
10127
- id: adapter,
10128
- endpointByRoute: { [routeId]: direct ? directEndpoint : routeId },
10129
- methodsByRoute: {
10130
- [routeId]: direct ? directAuthMethods(descriptor, directEndpoint) : ["api-key"]
10131
- }
10132
- },
10133
- routeId,
10134
- direct
10135
- };
10136
- }
10137
10151
  function resolveProfileAwareRoute(adapter, authDescriptor, profile, model) {
10138
10152
  if (!model) return void 0;
10139
10153
  const eligibleCandidates = serviceableModelRoutes(model).map((gateway) => spawnEligibilityManifest(adapter, authDescriptor, { gateway }, model)).filter(
@@ -12246,6 +12260,7 @@ var init_session_spawn = __esm({
12246
12260
  init_bundles();
12247
12261
  init_mcp_imports();
12248
12262
  init_launch_config();
12263
+ init_eligibility_manifest();
12249
12264
  init_claude_code_oauth_source();
12250
12265
  init_providers_store();
12251
12266
  init_catalog_models();
@@ -16638,6 +16653,267 @@ var init_session_end_reason = __esm({
16638
16653
  PROVIDER_LIMIT_ERROR_RE = /\bhit your (?:session|usage) limit\b/i;
16639
16654
  }
16640
16655
  });
16656
+ function sessionIndexPath(sessionId, baseDir) {
16657
+ return join(sessionTranscriptDir(sessionId, baseDir), "index.json");
16658
+ }
16659
+ function capText(text13, max) {
16660
+ const flat = text13.replace(/\s+/g, " ").trim();
16661
+ const points = Array.from(flat);
16662
+ if (points.length <= max) return flat;
16663
+ return points.slice(0, max - 1).join("") + "\u2026";
16664
+ }
16665
+ function isRecord5(value) {
16666
+ return typeof value === "object" && value !== null;
16667
+ }
16668
+ function indexEntryFromDescriptor(desc, extra) {
16669
+ const entry = {
16670
+ id: desc.id,
16671
+ kind: desc.kind,
16672
+ status: desc.status,
16673
+ alive: desc.status === "running" || desc.status === "starting",
16674
+ startedAt: desc.startedAt
16675
+ };
16676
+ if (desc.label !== void 0) entry.label = desc.label;
16677
+ if (desc.title !== void 0) entry.title = desc.title;
16678
+ if (desc.renamedByUser !== void 0) entry.renamedByUser = desc.renamedByUser;
16679
+ if (desc.adapterSlug !== void 0) entry.adapter = desc.adapterSlug;
16680
+ if (desc.model !== void 0) entry.model = desc.model;
16681
+ if (desc.cwd !== void 0) entry.cwd = desc.cwd;
16682
+ if (desc.workspaceSlug !== void 0) entry.workspaceSlug = desc.workspaceSlug;
16683
+ if (desc.origin !== void 0) entry.origin = desc.origin;
16684
+ if (desc.depth !== void 0) entry.depth = desc.depth;
16685
+ if (desc.parentSessionId !== void 0) entry.parentSessionId = desc.parentSessionId;
16686
+ if (desc.lastActivityAt !== void 0) entry.lastActivityAt = desc.lastActivityAt;
16687
+ if (desc.lastTurnReason !== void 0) entry.lastTurnReason = desc.lastTurnReason;
16688
+ if (desc.turnsCompleted !== void 0) entry.turnsCompleted = desc.turnsCompleted;
16689
+ if (desc.costUsd !== void 0) entry.costUsd = desc.costUsd;
16690
+ const prompt = extra?.lastUserPrompt;
16691
+ if (prompt && prompt.text.trim()) {
16692
+ entry.lastUserPrompt = { ts: prompt.ts, text: capText(prompt.text, INDEX_MAX_PROMPT) };
16693
+ }
16694
+ const output = extra?.lastOutputText;
16695
+ if (output && output.trim()) entry.lastOutputText = capText(output, INDEX_MAX_OUTPUT);
16696
+ return entry;
16697
+ }
16698
+ function readSessionIndex(sessionId, baseDir) {
16699
+ let raw;
16700
+ try {
16701
+ raw = readFileSync(sessionIndexPath(sessionId, baseDir), "utf8");
16702
+ } catch {
16703
+ return void 0;
16704
+ }
16705
+ let parsed;
16706
+ try {
16707
+ parsed = JSON.parse(raw);
16708
+ } catch {
16709
+ return void 0;
16710
+ }
16711
+ if (!isRecord5(parsed) || typeof parsed.id !== "string" || parsed.id.length === 0) return void 0;
16712
+ return parsed;
16713
+ }
16714
+ function writeSessionIndex(entry, baseDir) {
16715
+ try {
16716
+ const dir = sessionTranscriptDir(entry.id, baseDir);
16717
+ mkdirSync(dir, { recursive: true });
16718
+ const finalPath = join(dir, "index.json");
16719
+ const tmpPath = `${finalPath}.tmp`;
16720
+ writeFileSync(tmpPath, JSON.stringify(entry) + "\n");
16721
+ renameSync(tmpPath, finalPath);
16722
+ } catch {
16723
+ }
16724
+ }
16725
+ function readTranscriptTail(eventsPath2, maxBytes = INDEX_TAIL_BYTES) {
16726
+ const empty = { prompts: [], bytesRead: 0 };
16727
+ let fd;
16728
+ try {
16729
+ fd = openSync(eventsPath2, "r");
16730
+ } catch {
16731
+ return empty;
16732
+ }
16733
+ let tail;
16734
+ let bytesRead = 0;
16735
+ try {
16736
+ const size = fstatSync(fd).size;
16737
+ const len = Math.min(size, Math.max(0, maxBytes));
16738
+ if (len === 0) return empty;
16739
+ const buf = Buffer.alloc(len);
16740
+ readSync(fd, buf, 0, len, size - len);
16741
+ bytesRead = len;
16742
+ tail = buf.toString("utf8");
16743
+ } catch {
16744
+ return empty;
16745
+ } finally {
16746
+ closeSync(fd);
16747
+ }
16748
+ const prompts = [];
16749
+ let assistant = "";
16750
+ let lastOutputText;
16751
+ let firstTs;
16752
+ const sealAssistant = () => {
16753
+ const t = assistant.trim();
16754
+ if (t) lastOutputText = t;
16755
+ assistant = "";
16756
+ };
16757
+ for (const line of tail.split("\n")) {
16758
+ const trimmed = line.trim();
16759
+ if (!trimmed) continue;
16760
+ let rec;
16761
+ try {
16762
+ rec = JSON.parse(trimmed);
16763
+ } catch {
16764
+ continue;
16765
+ }
16766
+ if (firstTs === void 0 && typeof rec.ts === "string") firstTs = rec.ts;
16767
+ if (rec.kind === "user-prompt" && typeof rec.text === "string") {
16768
+ sealAssistant();
16769
+ if (rec.text.trim()) {
16770
+ prompts.push({ ts: typeof rec.ts === "string" ? rec.ts : "", text: rec.text });
16771
+ }
16772
+ } else if (rec.kind === "text-delta" && typeof rec.text === "string") {
16773
+ assistant += rec.text;
16774
+ } else if (rec.kind === "tool-call" || rec.kind === "tool-result" || rec.kind === "turn-end") {
16775
+ sealAssistant();
16776
+ }
16777
+ }
16778
+ sealAssistant();
16779
+ return { prompts, ...lastOutputText !== void 0 ? { lastOutputText } : {}, bytesRead, ...firstTs !== void 0 ? { firstTs } : {} };
16780
+ }
16781
+ function deriveIndexFromTranscript(sessionId, baseDir) {
16782
+ const eventsPath2 = sessionEventsPath(sessionId, baseDir);
16783
+ if (!existsSync(eventsPath2)) return void 0;
16784
+ const tail = readTranscriptTail(eventsPath2);
16785
+ let startedAt;
16786
+ try {
16787
+ const stat19 = statSync(eventsPath2);
16788
+ if (stat19.birthtimeMs > 0) startedAt = stat19.birthtime.toISOString();
16789
+ else startedAt = stat19.mtime.toISOString();
16790
+ } catch {
16791
+ }
16792
+ startedAt = startedAt ?? tail.firstTs ?? (/* @__PURE__ */ new Date(0)).toISOString();
16793
+ const lastPrompt = tail.prompts[tail.prompts.length - 1];
16794
+ return {
16795
+ id: sessionId,
16796
+ kind: "agent-cli",
16797
+ status: "unknown",
16798
+ alive: false,
16799
+ startedAt,
16800
+ ...lastPrompt ? { lastUserPrompt: { ts: lastPrompt.ts, text: capText(lastPrompt.text, INDEX_MAX_PROMPT) } } : {},
16801
+ ...tail.lastOutputText ? { lastOutputText: capText(tail.lastOutputText, INDEX_MAX_OUTPUT) } : {}
16802
+ };
16803
+ }
16804
+ function readAllSessionIndexes(baseDir) {
16805
+ const dir = baseDir ?? defaultTranscriptBaseDir();
16806
+ let entries;
16807
+ try {
16808
+ entries = readdirSync(dir, { withFileTypes: true });
16809
+ } catch {
16810
+ return [];
16811
+ }
16812
+ const out = [];
16813
+ for (const ent of entries) {
16814
+ if (!ent.isDirectory()) continue;
16815
+ const id = ent.name;
16816
+ const index = readSessionIndex(id, dir);
16817
+ if (index) {
16818
+ out.push(index);
16819
+ continue;
16820
+ }
16821
+ const derived = deriveIndexFromTranscript(id, dir);
16822
+ if (derived) out.push(derived);
16823
+ }
16824
+ out.sort((a, b) => (b.lastActivityAt ?? b.startedAt).localeCompare(a.lastActivityAt ?? a.startedAt));
16825
+ return out;
16826
+ }
16827
+ function matchesSessionQuery(entry, query) {
16828
+ const q = query.trim().toLowerCase();
16829
+ if (!q) return true;
16830
+ if (entry.id.toLowerCase().startsWith(q)) return true;
16831
+ for (const field of [entry.label, entry.title, entry.cwd, entry.workspaceSlug]) {
16832
+ if (field && field.toLowerCase().includes(q)) return true;
16833
+ }
16834
+ return false;
16835
+ }
16836
+ function searchSessionIndexes(entries, query, opts = {}) {
16837
+ const limit = opts.limit ?? INDEX_DEFAULT_LIMIT;
16838
+ return entries.filter((e) => matchesSessionQuery(e, query)).filter((e) => opts.status ? e.status === opts.status : true).sort((a, b) => (b.lastActivityAt ?? b.startedAt).localeCompare(a.lastActivityAt ?? a.startedAt)).slice(0, Math.max(0, limit));
16839
+ }
16840
+ function buildSessionRecap(input) {
16841
+ const { entry } = input;
16842
+ const last = input.last ?? 8;
16843
+ const tail = readTranscriptTail(input.eventsPath);
16844
+ const source = tail.prompts.length > 0 ? tail.prompts : entry.lastUserPrompt ? [entry.lastUserPrompt] : [];
16845
+ const prompts = source.slice(Math.max(0, source.length - last));
16846
+ const lastOutputText = tail.lastOutputText ?? entry.lastOutputText;
16847
+ return {
16848
+ id: entry.id,
16849
+ status: entry.status,
16850
+ alive: entry.alive,
16851
+ ...entry.label !== void 0 ? { label: entry.label } : {},
16852
+ ...entry.title !== void 0 ? { title: entry.title } : {},
16853
+ ...entry.adapter !== void 0 ? { adapter: entry.adapter } : {},
16854
+ ...entry.model !== void 0 ? { model: entry.model } : {},
16855
+ ...entry.cwd !== void 0 ? { cwd: entry.cwd } : {},
16856
+ ...entry.workspaceSlug !== void 0 ? { workspaceSlug: entry.workspaceSlug } : {},
16857
+ ...entry.parentSessionId !== void 0 ? { parentSessionId: entry.parentSessionId } : {},
16858
+ children: [...input.children ?? []],
16859
+ startedAt: entry.startedAt,
16860
+ ...entry.lastActivityAt !== void 0 ? { lastActivityAt: entry.lastActivityAt } : {},
16861
+ ...entry.turnsCompleted !== void 0 ? { turnsCompleted: entry.turnsCompleted } : {},
16862
+ ...entry.costUsd !== void 0 ? { costUsd: entry.costUsd } : {},
16863
+ ...entry.lastTurnReason !== void 0 ? { lastTurnReason: entry.lastTurnReason } : {},
16864
+ prompts,
16865
+ ...lastOutputText ? { lastOutputText: capText(lastOutputText, INDEX_MAX_OUTPUT) } : {},
16866
+ ...input.queuedPrompts !== void 0 ? { queuedPrompts: input.queuedPrompts } : {}
16867
+ };
16868
+ }
16869
+ function backfillSessionIndexes(baseDir, opts) {
16870
+ const dir = baseDir ?? defaultTranscriptBaseDir();
16871
+ const byId = /* @__PURE__ */ new Map();
16872
+ if (opts?.descriptors) {
16873
+ for (const desc of opts.descriptors) byId.set(desc.id, desc);
16874
+ }
16875
+ let dirents;
16876
+ try {
16877
+ dirents = readdirSync(dir, { withFileTypes: true });
16878
+ } catch {
16879
+ return { scanned: 0, created: 0, skipped: 0 };
16880
+ }
16881
+ let scanned = 0;
16882
+ let created = 0;
16883
+ let skipped = 0;
16884
+ for (const ent of dirents) {
16885
+ if (!ent.isDirectory()) continue;
16886
+ scanned++;
16887
+ const id = ent.name;
16888
+ if (readSessionIndex(id, dir)) {
16889
+ skipped++;
16890
+ continue;
16891
+ }
16892
+ const desc = byId.get(id);
16893
+ const derived = deriveIndexFromTranscript(id, dir);
16894
+ if (!desc && !derived) {
16895
+ skipped++;
16896
+ continue;
16897
+ }
16898
+ const entry = desc ? indexEntryFromDescriptor(desc, {
16899
+ ...derived?.lastUserPrompt ? { lastUserPrompt: derived.lastUserPrompt } : {},
16900
+ ...derived?.lastOutputText ? { lastOutputText: derived.lastOutputText } : {}
16901
+ }) : derived;
16902
+ writeSessionIndex(entry, dir);
16903
+ created++;
16904
+ }
16905
+ return { scanned, created, skipped };
16906
+ }
16907
+ var INDEX_MAX_PROMPT, INDEX_MAX_OUTPUT, INDEX_TAIL_BYTES, INDEX_DEFAULT_LIMIT;
16908
+ var init_session_index = __esm({
16909
+ "src/session-index.ts"() {
16910
+ init_transcript_writer();
16911
+ INDEX_MAX_PROMPT = 500;
16912
+ INDEX_MAX_OUTPUT = 300;
16913
+ INDEX_TAIL_BYTES = 256 * 1024;
16914
+ INDEX_DEFAULT_LIMIT = 20;
16915
+ }
16916
+ });
16641
16917
  function isBackgroundToolCallArguments(args) {
16642
16918
  return typeof args === "object" && args !== null && args.run_in_background === true;
16643
16919
  }
@@ -16817,6 +17093,7 @@ function toSessionSummary(desc) {
16817
17093
  blockedOn: desc.blockedOn,
16818
17094
  stalledSinceMs: desc.stalledSinceMs,
16819
17095
  pendingBgTasks: desc.pendingBgTasks,
17096
+ ...desc.wrapupFlag ? { wrapupFlag: desc.wrapupFlag } : {},
16820
17097
  backgroundTasks: desc.backgroundTasks,
16821
17098
  lastTurnErroredAt: desc.lastTurnErroredAt,
16822
17099
  ...desc.lastError ? { lastError: desc.lastError.slice(0, 2e3) } : {},
@@ -16945,6 +17222,18 @@ function stampInterrupted(desc) {
16945
17222
  delete desc.interrupted;
16946
17223
  }
16947
17224
  }
17225
+ function stampAdapterConnected(desc, rt) {
17226
+ const probe = rt.agentSession?.isConnected;
17227
+ if (!probe) {
17228
+ delete desc.adapterConnected;
17229
+ return;
17230
+ }
17231
+ try {
17232
+ desc.adapterConnected = probe.call(rt.agentSession);
17233
+ } catch {
17234
+ delete desc.adapterConnected;
17235
+ }
17236
+ }
16948
17237
  function stampLiveModes(desc, rt) {
16949
17238
  const modes = rt.agentSession?.availableModes;
16950
17239
  if (modes && modes.length > 0) {
@@ -16956,6 +17245,9 @@ function stampLiveModes(desc, rt) {
16956
17245
  function isResumable(desc) {
16957
17246
  return desc.kind === "agent-cli" && !!desc.adapterSlug && !!desc.adapterSessionId && !!desc.cwd && !desc.archived;
16958
17247
  }
17248
+ function crashReasonText(reason, pid) {
17249
+ return reason === "transport-closed" ? `adapter connection closed (pid ${pid ?? "?"} may still be running) \u2014 session crashed` : `adapter process gone (pid ${pid}) \u2014 session crashed`;
17250
+ }
16959
17251
  function canResume(desc) {
16960
17252
  return isResumable(desc) && (desc.resumeAttempts ?? 0) < MAX_RESUME_ATTEMPTS;
16961
17253
  }
@@ -17194,6 +17486,7 @@ function createSessionsRegistry(opts) {
17194
17486
  }
17195
17487
  }
17196
17488
  recordOutcome(rt);
17489
+ writeIndexNow(rt);
17197
17490
  rt.linkProbeStop?.();
17198
17491
  cancelPendingPermissionsForSession(rt);
17199
17492
  if (runWorktreeAutoReclaim && rt.desc.worktreeAutoProvisioned && rt.desc.worktreePath) {
@@ -17428,6 +17721,53 @@ function createSessionsRegistry(opts) {
17428
17721
  void persistSnapshot();
17429
17722
  }, PERSIST_DEBOUNCE_MS2);
17430
17723
  };
17724
+ const INDEX_WRITE_THROTTLE_MS = 400;
17725
+ const dirtyIndexIds = /* @__PURE__ */ new Set();
17726
+ let indexFlushTimer;
17727
+ const captureIndexTail = (rt) => {
17728
+ let lastUserPrompt = rt.lastUserPrompt;
17729
+ let lastOutputText = rt.lastAssistantText;
17730
+ if (lastUserPrompt === void 0 || lastOutputText === void 0) {
17731
+ const tail = readTranscriptTail(sessionEventsPath(rt.desc.id, transcriptBaseDir), INDEX_TAIL_BYTES);
17732
+ if (lastUserPrompt === void 0 && tail.prompts.length > 0) {
17733
+ lastUserPrompt = tail.prompts[tail.prompts.length - 1];
17734
+ }
17735
+ if (lastOutputText === void 0) lastOutputText = tail.lastOutputText;
17736
+ }
17737
+ return {
17738
+ ...lastUserPrompt ? { lastUserPrompt } : {},
17739
+ ...lastOutputText ? { lastOutputText } : {}
17740
+ };
17741
+ };
17742
+ const writeIndexNow = (rt) => {
17743
+ try {
17744
+ writeSessionIndex(indexEntryFromDescriptor(rt.desc, captureIndexTail(rt)), transcriptBaseDir);
17745
+ } catch {
17746
+ }
17747
+ };
17748
+ const flushDirtyIndexes = () => {
17749
+ if (indexFlushTimer) {
17750
+ clearTimeout(indexFlushTimer);
17751
+ indexFlushTimer = void 0;
17752
+ }
17753
+ if (dirtyIndexIds.size === 0) return;
17754
+ const ids = [...dirtyIndexIds];
17755
+ dirtyIndexIds.clear();
17756
+ for (const id of ids) {
17757
+ const rt = sessions.get(id);
17758
+ if (rt) writeIndexNow(rt);
17759
+ }
17760
+ };
17761
+ const scheduleIndexWrite = (rt) => {
17762
+ if (shutdownDone) return;
17763
+ dirtyIndexIds.add(rt.desc.id);
17764
+ if (indexFlushTimer) return;
17765
+ indexFlushTimer = setTimeout(() => {
17766
+ indexFlushTimer = void 0;
17767
+ flushDirtyIndexes();
17768
+ }, INDEX_WRITE_THROTTLE_MS);
17769
+ indexFlushTimer.unref?.();
17770
+ };
17431
17771
  const conversationLinkProbeMs = opts?.conversationLinkProbeMs ?? {
17432
17772
  initialMs: 3e3,
17433
17773
  intervalMs: 15e3
@@ -18030,7 +18370,12 @@ function createSessionsRegistry(opts) {
18030
18370
  schedulePersist();
18031
18371
  };
18032
18372
  const maybeResumeAgent = async (rt, resumedFrom = "daemon-restart") => {
18033
- if (rt.agentSession) return;
18373
+ if (rt.agentSession) {
18374
+ if (rt.agentSession.isConnected?.() === false) {
18375
+ markCrashedInternal(rt.desc.id, "transport-closed");
18376
+ }
18377
+ if (rt.agentSession) return;
18378
+ }
18034
18379
  if (!resumeAgent) return;
18035
18380
  if (!isResumable(rt.desc)) return;
18036
18381
  if (!canResume(rt.desc)) {
@@ -18076,7 +18421,7 @@ function createSessionsRegistry(opts) {
18076
18421
  recordFailedResume(rt);
18077
18422
  return;
18078
18423
  }
18079
- rt.agentSession = fresh;
18424
+ bindAgentSession(rt, fresh);
18080
18425
  bindOutOfTurnEvents(rt);
18081
18426
  stampCapabilities(rt);
18082
18427
  rt.adapterSlug = adapterSlug;
@@ -18133,6 +18478,48 @@ function createSessionsRegistry(opts) {
18133
18478
  rt.resumePromise = void 0;
18134
18479
  }
18135
18480
  };
18481
+ const markCrashedInternal = (id, reason = "process-gone") => {
18482
+ const rt = sessions.get(id);
18483
+ if (!rt) return false;
18484
+ if (rt.desc.kind !== "agent-cli" || rt.desc.status !== "running") {
18485
+ return false;
18486
+ }
18487
+ const pid = rt.desc.pid;
18488
+ const providerLimitMessage = isProviderLimitError(rt.lastErrorMessage) ? rt.lastErrorMessage : void 0;
18489
+ const detail = providerLimitMessage ? `provider usage limit hit: ${providerLimitMessage}` : crashReasonText(reason, pid);
18490
+ rt.desc.status = "error";
18491
+ rt.desc.endedAt = (/* @__PURE__ */ new Date()).toISOString();
18492
+ rt.desc.endedReason = providerLimitMessage ? "provider-limit" : "crashed";
18493
+ rt.desc.crashedAt = rt.desc.endedAt;
18494
+ rt.desc.lastError = detail;
18495
+ if (rt.agentSession) {
18496
+ releaseOutOfTurnEvents(rt);
18497
+ delete rt.desc.backgroundTasks;
18498
+ recordExitUsageSnapshot(rt);
18499
+ void rt.agentSession.close().catch(() => void 0);
18500
+ void transcriptWriter.close(rt.desc.id);
18501
+ tracedSessions.delete(rt.desc.id);
18502
+ rt.agentSession = void 0;
18503
+ }
18504
+ schedulePersist();
18505
+ const banner = providerLimitMessage ? `[provider-limit] ${providerLimitMessage}` : `[crashed] ${detail}`;
18506
+ appendLine(rt, banner, "stderr");
18507
+ transcriptWriter.recordEvent(rt.desc.id, { kind: "notice", text: banner });
18508
+ emitExited(rt);
18509
+ return true;
18510
+ };
18511
+ const bindAgentSession = (rt, session) => {
18512
+ rt.agentSession = session;
18513
+ session.onDisconnect?.((err) => {
18514
+ const id = rt.desc.id;
18515
+ if (rt.agentSession !== session) return;
18516
+ if (markCrashedInternal(id, "transport-closed")) {
18517
+ console.warn(
18518
+ `[sessions] ${id}: adapter connection closed (${err.message}) \u2014 marked crashed; the row stays lazy-resumable.`
18519
+ );
18520
+ }
18521
+ });
18522
+ };
18136
18523
  const buildUsageSnapshot = (rt) => deriveSessionUsage({
18137
18524
  ...rt.desc.model !== void 0 ? { model: rt.desc.model } : {},
18138
18525
  ...rt.adapterReportedCost && rt.desc.costUsd !== void 0 ? { adapterCostUsd: rt.desc.costUsd } : {},
@@ -18592,6 +18979,7 @@ ${message}`;
18592
18979
  ...messageTurn ? { messages: turnOpts.messages } : {}
18593
18980
  } : void 0
18594
18981
  );
18982
+ rt.lastUserPrompt = { ts: (/* @__PURE__ */ new Date()).toISOString(), text: promptTextForIndex(message, turnOpts?.system) };
18595
18983
  const wrapped = typeof message === "string" ? { type: "text", text: message } : message;
18596
18984
  const switchCandidate = typeof message === "string" ? parseModelSwitchCommand(message) : void 0;
18597
18985
  let switchLearned = false;
@@ -18829,6 +19217,7 @@ ${message}`;
18829
19217
  });
18830
19218
  }
18831
19219
  }
19220
+ scheduleIndexWrite(rt);
18832
19221
  if (!interruptedBy) dispatchQueuedPrompt(rt);
18833
19222
  else if (interruptedBy.deliverQueueId) {
18834
19223
  dispatchQueuedPrompt(rt, interruptedBy.deliverQueueId);
@@ -19123,6 +19512,10 @@ ${message}`;
19123
19512
  };
19124
19513
  const registry = {
19125
19514
  bootId,
19515
+ transcriptBaseDir,
19516
+ flushSessionIndexes() {
19517
+ flushDirtyIndexes();
19518
+ },
19126
19519
  recordAutoContinue(id) {
19127
19520
  const rt = sessions.get(id);
19128
19521
  if (!rt) return;
@@ -19199,6 +19592,7 @@ ${message}`;
19199
19592
  emitExited(rt);
19200
19593
  });
19201
19594
  schedulePersist();
19595
+ writeIndexNow(rt);
19202
19596
  return desc;
19203
19597
  },
19204
19598
  register(input) {
@@ -19248,6 +19642,7 @@ ${message}`;
19248
19642
  emitExited(rt);
19249
19643
  });
19250
19644
  schedulePersist();
19645
+ writeIndexNow(rt);
19251
19646
  return desc;
19252
19647
  },
19253
19648
  spawnAgent(input) {
@@ -19349,7 +19744,6 @@ ${message}`;
19349
19744
  }
19350
19745
  const rt = {
19351
19746
  desc,
19352
- agentSession: input.agentSession,
19353
19747
  adapterSlug: input.adapterSlug,
19354
19748
  recentLines: [],
19355
19749
  recentBytes: [],
@@ -19366,6 +19760,7 @@ ${message}`;
19366
19760
  rt.emitter.setMaxListeners(50);
19367
19761
  sessions.set(id, rt);
19368
19762
  linkContinuedTo(input.resumedFrom, id);
19763
+ bindAgentSession(rt, input.agentSession);
19369
19764
  bindOutOfTurnEvents(rt);
19370
19765
  stampCapabilities(rt);
19371
19766
  armUsageRefresh(rt);
@@ -19383,6 +19778,7 @@ ${message}`;
19383
19778
  "stdout"
19384
19779
  );
19385
19780
  schedulePersist();
19781
+ writeIndexNow(rt);
19386
19782
  recordConversationLink(rt);
19387
19783
  desc.eventsPath = sessionEventsPath(desc.id, transcriptBaseDir);
19388
19784
  if (input.initialPrompt) {
@@ -19501,6 +19897,7 @@ ${message}`;
19501
19897
  "stdout"
19502
19898
  );
19503
19899
  schedulePersist();
19900
+ writeIndexNow(rt);
19504
19901
  desc.eventsPath = sessionEventsPath(desc.id, transcriptBaseDir);
19505
19902
  return desc;
19506
19903
  },
@@ -19556,7 +19953,7 @@ ${message}`;
19556
19953
  emitExited(rt);
19557
19954
  return;
19558
19955
  }
19559
- rt.agentSession = outcome.agentSession;
19956
+ bindAgentSession(rt, outcome.agentSession);
19560
19957
  bindOutOfTurnEvents(rt);
19561
19958
  stampCapabilities(rt);
19562
19959
  rt.readUsage = outcome.readUsage;
@@ -19578,6 +19975,7 @@ ${message}`;
19578
19975
  "stdout"
19579
19976
  );
19580
19977
  schedulePersist();
19978
+ writeIndexNow(rt);
19581
19979
  recordConversationLink(rt);
19582
19980
  if (outcome.initialPrompt) {
19583
19981
  void runAgentTurn(
@@ -19747,6 +20145,7 @@ ${message}`;
19747
20145
  emitExited(rt);
19748
20146
  });
19749
20147
  schedulePersist();
20148
+ writeIndexNow(rt);
19750
20149
  return desc;
19751
20150
  },
19752
20151
  recordCommand(input) {
@@ -20371,6 +20770,7 @@ ${message}`;
20371
20770
  stampCurrentStatus(rt);
20372
20771
  stampWatchers(desc);
20373
20772
  stampLiveModes(desc, rt);
20773
+ stampAdapterConnected(desc, rt);
20374
20774
  desc.childrenBusy = childrenBusy.get(desc.id) ?? 0;
20375
20775
  desc.queuedPrompts = desc.promptQueue?.length ?? 0;
20376
20776
  return desc;
@@ -20428,6 +20828,7 @@ ${message}`;
20428
20828
  stampCurrentStatus(rt);
20429
20829
  stampWatchers(desc);
20430
20830
  stampLiveModes(desc, rt);
20831
+ stampAdapterConnected(desc, rt);
20431
20832
  desc.childrenBusy = childrenBusyCounts().get(desc.id) ?? 0;
20432
20833
  desc.queuedPrompts = desc.promptQueue?.length ?? 0;
20433
20834
  }
@@ -20683,34 +21084,8 @@ ${message}`;
20683
21084
  emitExited(rt);
20684
21085
  return true;
20685
21086
  },
20686
- markCrashed(id) {
20687
- const rt = sessions.get(id);
20688
- if (!rt) return false;
20689
- if (rt.desc.kind !== "agent-cli" || rt.desc.status !== "running") {
20690
- return false;
20691
- }
20692
- const pid = rt.desc.pid;
20693
- const providerLimitMessage = isProviderLimitError(rt.lastErrorMessage) ? rt.lastErrorMessage : void 0;
20694
- rt.desc.status = "error";
20695
- rt.desc.endedAt = (/* @__PURE__ */ new Date()).toISOString();
20696
- rt.desc.endedReason = providerLimitMessage ? "provider-limit" : "crashed";
20697
- rt.desc.crashedAt = rt.desc.endedAt;
20698
- rt.desc.lastError = providerLimitMessage ? `provider usage limit hit: ${providerLimitMessage}` : `adapter process gone (pid ${pid}) \u2014 session crashed`;
20699
- if (rt.agentSession) {
20700
- releaseOutOfTurnEvents(rt);
20701
- delete rt.desc.backgroundTasks;
20702
- recordExitUsageSnapshot(rt);
20703
- void rt.agentSession.close().catch(() => void 0);
20704
- void transcriptWriter.close(rt.desc.id);
20705
- tracedSessions.delete(rt.desc.id);
20706
- rt.agentSession = void 0;
20707
- }
20708
- schedulePersist();
20709
- const banner = providerLimitMessage ? `[provider-limit] ${providerLimitMessage}` : `[crashed] adapter process gone (pid ${pid}) \u2014 session crashed`;
20710
- appendLine(rt, banner, "stderr");
20711
- transcriptWriter.recordEvent(rt.desc.id, { kind: "notice", text: banner });
20712
- emitExited(rt);
20713
- return true;
21087
+ markCrashed(id, reason = "process-gone") {
21088
+ return markCrashedInternal(id, reason);
20714
21089
  },
20715
21090
  markStalled(id, stalledSinceMs) {
20716
21091
  const rt = sessions.get(id);
@@ -20876,6 +21251,7 @@ ${message}`;
20876
21251
  ts: (/* @__PURE__ */ new Date()).toISOString()
20877
21252
  });
20878
21253
  stampReadLiveness(rt.desc);
21254
+ writeIndexNow(rt);
20879
21255
  return rt.desc;
20880
21256
  },
20881
21257
  setKeepAlive(id, keepAlive) {
@@ -20999,6 +21375,7 @@ ${message}`;
20999
21375
  if (shutdownDone) return;
21000
21376
  shutdownDone = true;
21001
21377
  if (persistTimer) clearTimeout(persistTimer);
21378
+ flushDirtyIndexes();
21002
21379
  if (persist) {
21003
21380
  try {
21004
21381
  process.off("exit", onProcessExit);
@@ -21151,6 +21528,13 @@ function loadHistorySnapshot(persistPath, sessions, sessionEvents, transcriptBas
21151
21528
  }
21152
21529
  }
21153
21530
  }
21531
+ function promptTextForIndex(message, system) {
21532
+ const text13 = typeof message === "string" ? message : (Array.isArray(message) ? message : [message]).map(
21533
+ (b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : ""
21534
+ ).filter(Boolean).join("\n");
21535
+ if (system && text13.startsWith(system)) return text13.slice(system.length).replace(/^\n\n/, "");
21536
+ return text13;
21537
+ }
21154
21538
  function quoteArg(arg) {
21155
21539
  if (arg === "") return '""';
21156
21540
  if (/^[a-zA-Z0-9._/=:@,+-]+$/.test(arg)) return arg;
@@ -21190,6 +21574,7 @@ var init_sessions = __esm({
21190
21574
  init_session_continue_fresh();
21191
21575
  init_session_outcome();
21192
21576
  init_session_end_reason();
21577
+ init_session_index();
21193
21578
  init_session_message();
21194
21579
  SESSIONS_FILE_PATH = () => resolve(homedir(), ".agentproto", "sessions.json");
21195
21580
  SESSION_ID_ENV = "AGENTPROTO_SESSION_ID";
@@ -22937,7 +23322,7 @@ init_resume_strategies();
22937
23322
  init_spawn_defaults();
22938
23323
  init_launch_config();
22939
23324
  init_resume_context_digest();
22940
- init_session_spawn();
23325
+ init_eligibility_manifest();
22941
23326
  init_providers_store();
22942
23327
  init_catalog_models();
22943
23328
  init_config();
@@ -23435,7 +23820,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
23435
23820
  desc = await spawnWithResume(strategy.resumeSessionId);
23436
23821
  } catch (err) {
23437
23822
  const msg = err instanceof Error ? err.message : String(err);
23438
- if (strategy.resumeSessionId && RESUME_ID_REJECTED_RE.test(msg)) {
23823
+ if (strategy.resumeSessionId && (RESUME_ID_REJECTED_RE.test(msg) || RESUME_CONNECTION_LOST_RE.test(msg))) {
23439
23824
  desc = await spawnWithResume(void 0);
23440
23825
  resumeFallback = true;
23441
23826
  } else {
@@ -23726,7 +24111,7 @@ var EMPTY_SKILLS = {
23726
24111
  byHarness: [],
23727
24112
  defaults: { global: [], byAdapter: {} }
23728
24113
  };
23729
- function isRecord5(value) {
24114
+ function isRecord6(value) {
23730
24115
  return typeof value === "object" && value !== null && !Array.isArray(value);
23731
24116
  }
23732
24117
  function classifyDaemonMount(adapter) {
@@ -23879,15 +24264,15 @@ var slugToCamel2 = (slug) => slug.replace(/-([a-z0-9])/g, (_m, c) => c.toUpperCa
23879
24264
  async function readAdapterManifest(adapter) {
23880
24265
  try {
23881
24266
  const mod = await import(adapter.packageName);
23882
- if (!isRecord5(mod)) return void 0;
24267
+ if (!isRecord6(mod)) return void 0;
23883
24268
  const candidate = mod[slugToCamel2(adapter.slug)] ?? mod.default ?? mod.handle;
23884
- return isRecord5(candidate) ? candidate : void 0;
24269
+ return isRecord6(candidate) ? candidate : void 0;
23885
24270
  } catch {
23886
24271
  return void 0;
23887
24272
  }
23888
24273
  }
23889
24274
  function asSkillsTarget(value) {
23890
- if (!isRecord5(value) || typeof value.format !== "string") return void 0;
24275
+ if (!isRecord6(value) || typeof value.format !== "string") return void 0;
23891
24276
  const out = { format: value.format };
23892
24277
  if (typeof value.dir === "string") out.dir = value.dir;
23893
24278
  if (typeof value.outDir === "string") out.outDir = value.outDir;
@@ -23904,10 +24289,10 @@ async function listNativeSkills(adapter) {
23904
24289
  }
23905
24290
  async function buildHarnessEntry(adapter) {
23906
24291
  const manifest = await readAdapterManifest(adapter);
23907
- const metadata = manifest && isRecord5(manifest.metadata) ? manifest.metadata : void 0;
24292
+ const metadata = manifest && isRecord6(manifest.metadata) ? manifest.metadata : void 0;
23908
24293
  const target = asSkillsTarget(metadata?.skills);
23909
24294
  const options = manifest && Array.isArray(manifest.options) ? manifest.options : [];
23910
- const spawnOption = options.some((o) => isRecord5(o) && o.id === "skills");
24295
+ const spawnOption = options.some((o) => isRecord6(o) && o.id === "skills");
23911
24296
  const [installed, native] = await Promise.all([
23912
24297
  target ? listInstalledForTarget(target) : Promise.resolve([]),
23913
24298
  listNativeSkills(adapter)
@@ -24293,6 +24678,8 @@ async function processTreeRss(rootPids, exec = defaultPsExecutor) {
24293
24678
  }
24294
24679
 
24295
24680
  // src/session-tools.ts
24681
+ init_transcript_writer();
24682
+ init_session_index();
24296
24683
  init_process_stats();
24297
24684
 
24298
24685
  // src/host-load.ts
@@ -26086,6 +26473,7 @@ var publicSessionDescriptor = (session) => {
26086
26473
  var compactSessionItem = (s) => ({
26087
26474
  id: s.id,
26088
26475
  kind: s.kind,
26476
+ ...s.origin ? { origin: s.origin } : {},
26089
26477
  name: s.name,
26090
26478
  label: s.label,
26091
26479
  status: s.status,
@@ -26374,6 +26762,66 @@ function registerSessionTools(rawServer, opts) {
26374
26762
  })
26375
26763
  ]
26376
26764
  });
26765
+ const sessionSearchSchema = z.object({
26766
+ query: z.string().describe(
26767
+ "Case-insensitive substring to match: a session id PREFIX, or a substring of label, title, cwd, or workspace slug. Empty matches all."
26768
+ ),
26769
+ status: z.enum(["starting", "running", "exited", "killed", "error"]).optional().describe("Filter by exact status."),
26770
+ ...pageParamsShape
26771
+ });
26772
+ registerBuiltinTool(server, {
26773
+ id: "session_search",
26774
+ description: "Find sessions by a case-insensitive query across id prefix, label, title, cwd and workspace slug \u2014 the daemon-side twin of `agentproto sessions find <query>`. Optional `status` filters to an exact lifecycle status; `limit` (default 20) caps the result. Each hit uses the same compact shape as `session_list`. Read-only; use `session_recap` on a hit to see where that session stopped.",
26775
+ inputSchema: sessionSearchSchema,
26776
+ handler: async (input) => {
26777
+ let rows = registry.list({ includeArchived: true });
26778
+ if (callerScope) {
26779
+ const subtree = collectSubtree(callerScope.ownerSessionId, rows);
26780
+ rows = rows.filter((s) => subtree.has(s.id));
26781
+ }
26782
+ rows = rows.filter((s) => matchesSessionQuery(s, input.query));
26783
+ if (input.status) rows = rows.filter((s) => s.status === input.status);
26784
+ const limit = Math.max(1, Math.min(200, input.limit ?? INDEX_DEFAULT_LIMIT));
26785
+ rows = rows.sort((a, b) => (b.lastActivityAt ?? b.startedAt).localeCompare(a.lastActivityAt ?? a.startedAt)).slice(0, limit);
26786
+ return rows.map(publicSessionDescriptor);
26787
+ },
26788
+ transformers: [
26789
+ paginated({
26790
+ project: compactSessionItem,
26791
+ keyOf: (s) => s.id,
26792
+ maxLimit: 200,
26793
+ itemKey: "sessions"
26794
+ })
26795
+ ]
26796
+ });
26797
+ registerBuiltinTool(server, {
26798
+ id: "session_recap",
26799
+ description: 'One-glance "where did we stop" for a session: the last K user prompts with timestamps, the final assistant text of the last turn, and meta (status/alive, model, adapter, cwd, parent, child ids, cost, queued prompts). Reads the session\'s index sidecar plus a bounded tail of its transcript \u2014 never the whole file. Use it to resume an interrupted session by id (find the id with `session_search`).',
26800
+ inputSchema: z.object({
26801
+ id: z.string().min(1).describe("Session id or name \u2014 from `session_list`/`session_search`."),
26802
+ last: z.number().int().min(1).max(50).optional().describe("How many recent user prompts to include (default 8).")
26803
+ }),
26804
+ handler: async (input) => {
26805
+ const resolved = registry.findByIdOrName(input.id);
26806
+ if (!resolved) throw new Error(`session_recap: no session "${input.id}"`);
26807
+ const desc = registry.get(resolved.id) ?? resolved;
26808
+ const baseDir = registry.transcriptBaseDir;
26809
+ const index = readSessionIndex(desc.id, baseDir);
26810
+ const entry = indexEntryFromDescriptor(desc, {
26811
+ ...index?.lastUserPrompt ? { lastUserPrompt: index.lastUserPrompt } : {},
26812
+ ...index?.lastOutputText ? { lastOutputText: index.lastOutputText } : {}
26813
+ });
26814
+ const children = registry.list({ includeArchived: true }).filter((s) => s.parentSessionId === desc.id).map((s) => s.id);
26815
+ return buildSessionRecap({
26816
+ entry,
26817
+ eventsPath: sessionEventsPath(desc.id, baseDir),
26818
+ last: input.last ?? 8,
26819
+ children,
26820
+ ...desc.queuedPrompts !== void 0 ? { queuedPrompts: desc.queuedPrompts } : {}
26821
+ });
26822
+ },
26823
+ transformers: [catchErrors()]
26824
+ });
26377
26825
  registerBuiltinTool(server, {
26378
26826
  id: "session_stats",
26379
26827
  description: 'Resource usage per session, sampled from the OS process table: RSS bytes, %CPU, process count and top commands by RSS for every live session, sorted by RSS (each row labelled with the session\'s `label`/`name`), plus `daemon` (the daemon process + its own children), `provisioning` (worktree setup work such as `pnpm install` not yet attached to a session, with `inFlight` provisions), `orphans` (processes that look agentproto-owned but belong to no live session - reported only, never killed), `totals`, and `host` (load average, free/total memory). `detail:"full"` adds every process (pid, command, RSS, CPU, elapsed) per row. Cached ~3s; `fresh:true` forces a new sample. A subtree-scoped caller sees only its own sessions (no daemon/provisioning/orphan buckets).',
@@ -28621,8 +29069,8 @@ function registerSessionTools(rawServer, opts) {
28621
29069
  'Record a verdict on specific sessions \u2014 the mutating half of `session_wrapup_plan`. Each id is RE-CLASSIFIED from scratch immediately before acting (a plan computed moments earlier can be stale) and is only acted on if it is STILL `close` or `stuck`. Pass `judgedBy` (a judge session id) to also accept a `judge`-class id \u2014 the judge\'s verdict is what makes it safe to act on. `keep`-class ids are ALWAYS refused, no exception. `verdict: "done"` or `"abandoned"` actually CLOSES the session (tagged `endedReason:\'steward-completed\'`/`\'steward-abandoned\'`) and leaves it resumable (same as the idle reaper) \u2014 this never deletes anything. `verdict: "blocked"` or `"needs-input"` is NOT a completion: the session is left running untouched and the verdict is recorded as a flag (`SessionDescriptor.wrapupFlag`) instead \u2014 the per-id result\'s `action` says `closed` vs `flagged`. Also refused (result `ok:false`, `error:\'refused_stale_or_busy\'`) if the session is busy/awaitingInput/awaitingPermission or has a background task outstanding at the moment of the call, for either kind of action. A scoped orchestrator may only act on its own subtree. Returns a per-id result. Only the requested sessions are re-checked (never a full plan). If it outlasts 25 s (`waitMs`) it returns `{ jobId, status: "running", followUp }` \u2014 poll `session_wrapup_status`; the apply keeps running regardless.',
28622
29070
  {
28623
29071
  sessionIds: z.array(z.string().min(1)).min(1).describe("Session ids or names, from `session_wrapup_plan`."),
28624
- verdict: z.enum(["done", "abandoned", "blocked", "needs-input"]).describe(
28625
- 'What the session\'s work amounted to. "done"/"abandoned" CLOSE the session (`endedReason:\'steward-completed\'`/`\'steward-abandoned\'`); "blocked"/"needs-input" only FLAG it (`SessionDescriptor.wrapupFlag`) \u2014 the session keeps running.'
29072
+ verdict: z.enum(["done", "abandoned", "partial", "failed", "blocked", "needs-input"]).describe(
29073
+ 'What the session\'s work amounted to. "done"/"abandoned"/"partial"/"failed" CLOSE the session (`endedReason:\'steward-completed\'`/`\'steward-abandoned\'` \u2014 done only), the verdict nuance kept on the outcome; "blocked"/"needs-input" only FLAG it (`SessionDescriptor.wrapupFlag`) \u2014 the session keeps running.'
28626
29074
  ),
28627
29075
  note: z.string().optional().describe("Free-text note recorded on the outcome or the flag."),
28628
29076
  judgedBy: z.string().optional().describe(
@@ -28697,6 +29145,70 @@ function registerSessionTools(rawServer, opts) {
28697
29145
  }
28698
29146
  }
28699
29147
  );
29148
+ server.tool(
29149
+ "session_mark_completed",
29150
+ "Mark a live session as completed \u2014 closes it with verdict:'done' and tags it as steward-completed. Use when a human (or an agent's judge) decides the session's work is done \u2014 the declared path: no wrapup plan, no classification. One call fuses what would otherwise be `session_wrapup_apply` without a plan: the verdict (with an optional summary + who judged it) is written onto the session's Level-2 `SessionOutcome` and the session is closed gracefully the same way wrapup-apply does, left lazy-resumable (never deleted). `verdict:'partial'`/'failed'/'abandoned' close as not-completed (`endedReason:'steward-abandoned'`); `'blocked'`/'needs-input' are NOT completions \u2014 the session stays running and the verdict is recorded as a flag instead. Refused (result ok:false) when the session is missing, not agent-cli, already terminal, mid-turn/awaiting input/permission, or has a background task in flight.",
29151
+ {
29152
+ sessionId: z.string().min(1).describe("Session id or name \u2014 from `session_list`, must be a live agent-cli session."),
29153
+ verdict: z.enum(["done", "abandoned", "partial", "failed", "blocked", "needs-input"]).optional().describe(
29154
+ "Default 'done'. 'done' closes as completed (`endedReason:'steward-completed'`); 'partial'/'failed'/'abandoned' close as not-completed (steward-abandoned) with the verdict nuance kept on the outcome; 'blocked'/'needs-input' flag instead \u2014 the session keeps running."
29155
+ ),
29156
+ summary: z.string().optional().describe("Overrides the outcome's derived summary (trimmed to the same cap as the derived one)."),
29157
+ note: z.string().optional().describe("Free-text note explaining the verdict."),
29158
+ judgedBy: z.string().optional().describe(
29159
+ "A judge session id \u2014 sets the outcome's source to 'judged'. Omitted \u21D2 'declared', judgedBy:'steward-rules'."
29160
+ )
29161
+ },
29162
+ async (input) => {
29163
+ const desc = registry.findByIdOrName(input.sessionId);
29164
+ if (!desc) {
29165
+ return { content: [{ type: "text", text: JSON.stringify({ ok: false, error: "not_found", sessionId: input.sessionId }) }] };
29166
+ }
29167
+ if (callerScope) {
29168
+ const subtree = collectSubtree(callerScope.ownerSessionId, registry.list({ includeArchived: true }));
29169
+ if (!subtree.has(desc.id)) {
29170
+ return {
29171
+ content: [
29172
+ {
29173
+ type: "text",
29174
+ text: JSON.stringify({
29175
+ ok: false,
29176
+ error: "orchestrator_session_out_of_scope",
29177
+ message: `session_mark_completed: session "${desc.id}" is not in your subtree \u2014 a scoped orchestrator can only mark sessions it (transitively) spawned.`,
29178
+ sessionId: desc.id
29179
+ })
29180
+ }
29181
+ ],
29182
+ isError: true
29183
+ };
29184
+ }
29185
+ }
29186
+ const verdict = input.verdict ?? "done";
29187
+ const status = registry.get(desc.id)?.status ?? desc.status;
29188
+ if (status !== "running" && status !== "starting") {
29189
+ return {
29190
+ content: [{ type: "text", text: JSON.stringify({ ok: false, error: "not_live", status, sessionId: desc.id }) }],
29191
+ isError: true
29192
+ };
29193
+ }
29194
+ const source = input.judgedBy ? "judged" : "declared";
29195
+ const applied = registry.closeWithOutcome(desc.id, {
29196
+ verdict,
29197
+ ...input.summary !== void 0 ? { summary: input.summary } : {},
29198
+ ...input.note !== void 0 ? { note: input.note } : {},
29199
+ judgedBy: input.judgedBy ?? "steward-rules",
29200
+ source
29201
+ });
29202
+ const out = {
29203
+ ok: applied,
29204
+ sessionId: desc.id,
29205
+ verdict,
29206
+ action: verdict === "blocked" || verdict === "needs-input" ? "flagged" : "closed",
29207
+ ...!applied ? { error: "refused_stale_or_busy" } : {}
29208
+ };
29209
+ return { content: [{ type: "text", text: JSON.stringify(applied ? { ...out, endedReason: registry.get(desc.id)?.endedReason } : out) }] };
29210
+ }
29211
+ );
28700
29212
  server.tool(
28701
29213
  "session_evidence",
28702
29214
  "READ-ONLY \u2014 a compact evidence object for ONE session, as fed to the session-steward judge: label, cwd, adapter, status, keepAlive, awaitingInput, busy, idle minutes, the last ~10 user/assistant turns (trimmed to ~3 KB total, newest kept first), and \u2014 for a session in a linked worktree \u2014 branch, dirty counts, ahead/behind and PR state/number. Mutates nothing.",
@@ -34924,11 +35436,11 @@ function verifySignatureV2(secret, headers, body, nowMs, skewMs = SIGNATURE_V2_S
34924
35436
  if (a.length !== b.length || !timingSafeEqual(a, b)) return { ok: false, reason: "signature_mismatch" };
34925
35437
  return { ok: true, timestampSec };
34926
35438
  }
34927
- var isRecord6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
35439
+ var isRecord7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
34928
35440
  function toSentinelEvent(raw, seq) {
34929
- if (!isRecord6(raw)) return null;
35441
+ if (!isRecord7(raw)) return null;
34930
35442
  const { id, source, type, subject, time, data, summary, subjects, terminal, consumerref } = raw;
34931
- if (raw.specversion !== "1.0" || typeof id !== "string" || id === "" || typeof source !== "string" || typeof type !== "string" || type === "" || typeof subject !== "string" || subject === "" || typeof time !== "string" || !isRecord6(data) || typeof summary !== "string" || !Array.isArray(subjects) || !subjects.every((s) => typeof s === "string") || typeof terminal !== "boolean") {
35443
+ if (raw.specversion !== "1.0" || typeof id !== "string" || id === "" || typeof source !== "string" || typeof type !== "string" || type === "" || typeof subject !== "string" || subject === "" || typeof time !== "string" || !isRecord7(data) || typeof summary !== "string" || !Array.isArray(subjects) || !subjects.every((s) => typeof s === "string") || typeof terminal !== "boolean") {
34932
35444
  return null;
34933
35445
  }
34934
35446
  const envSeq = typeof raw.seq === "number" ? raw.seq : seq;
@@ -35034,7 +35546,7 @@ function agentpushSentinelProvider(opts = {}) {
35034
35546
  json3 = void 0;
35035
35547
  }
35036
35548
  if (!res.ok) {
35037
- const detail = isRecord6(json3) && typeof json3.error === "string" ? json3.error : text13.slice(0, 200);
35549
+ const detail = isRecord7(json3) && typeof json3.error === "string" ? json3.error : text13.slice(0, 200);
35038
35550
  throw new AgentpushHttpError(res.status, `agentpush ${method} ${path} \u2192 HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
35039
35551
  }
35040
35552
  return json3;
@@ -35120,7 +35632,7 @@ function agentpushSentinelProvider(opts = {}) {
35120
35632
  ...push ? { callback_url: callbackUrl2(delivery.callbackUrl, hookKey), callback_secret: callbackSecret } : {}
35121
35633
  }
35122
35634
  });
35123
- const id = isRecord6(json3) && isRecord6(json3.subscription) ? json3.subscription.id : void 0;
35635
+ const id = isRecord7(json3) && isRecord7(json3.subscription) ? json3.subscription.id : void 0;
35124
35636
  if (typeof id !== "string" || id === "") throw new Error("agentpush: create returned no subscription id");
35125
35637
  return {
35126
35638
  provider: AGENTPUSH_SLUG,
@@ -35147,7 +35659,7 @@ function agentpushSentinelProvider(opts = {}) {
35147
35659
  let sub;
35148
35660
  try {
35149
35661
  const json3 = await api("GET", subPath(handle));
35150
- sub = isRecord6(json3) && isRecord6(json3.subscription) ? json3.subscription : void 0;
35662
+ sub = isRecord7(json3) && isRecord7(json3.subscription) ? json3.subscription : void 0;
35151
35663
  } catch (err) {
35152
35664
  if (isHttp(err, 404)) {
35153
35665
  throw new Error(`agentpush subscription ${handle.remoteId} no longer exists \u2014 re-create the sentinel`);
@@ -35178,7 +35690,7 @@ function agentpushSentinelProvider(opts = {}) {
35178
35690
  async status(handle) {
35179
35691
  try {
35180
35692
  const json3 = await api("GET", subPath(handle));
35181
- const sub = isRecord6(json3) && isRecord6(json3.subscription) ? json3.subscription : void 0;
35693
+ const sub = isRecord7(json3) && isRecord7(json3.subscription) ? json3.subscription : void 0;
35182
35694
  const status = sub && typeof sub.status === "string" ? sub.status : "unknown";
35183
35695
  if (status === "active") return { ok: true };
35184
35696
  return { ok: false, detail: `the agentpush subscription is ${status}` };
@@ -35190,12 +35702,12 @@ function agentpushSentinelProvider(opts = {}) {
35190
35702
  const after = handle.cursor ?? "0";
35191
35703
  if (stateOf(handle).mode === "push") return { events: [], cursor: after };
35192
35704
  const json3 = await api("GET", `${subPath(handle)}/events`, { query: { after, limit: Math.max(1, Math.min(limit, 1e3)) } });
35193
- const items = isRecord6(json3) && Array.isArray(json3.items) ? json3.items : [];
35705
+ const items = isRecord7(json3) && Array.isArray(json3.items) ? json3.items : [];
35194
35706
  const events = [];
35195
35707
  let cursor = Number(after);
35196
35708
  if (!Number.isFinite(cursor)) cursor = 0;
35197
35709
  for (const item of items) {
35198
- if (!isRecord6(item)) continue;
35710
+ if (!isRecord7(item)) continue;
35199
35711
  const seq = typeof item.seq === "number" ? item.seq : void 0;
35200
35712
  if (seq !== void 0 && seq > cursor) cursor = seq;
35201
35713
  const event = toSentinelEvent(item.envelope, seq);
@@ -37571,11 +38083,11 @@ function sanitize(cacheKey) {
37571
38083
  const cleaned = cacheKey.replace(/[^a-zA-Z0-9._-]/g, "_");
37572
38084
  return cleaned.length > 0 ? cleaned : "default";
37573
38085
  }
37574
- function isRecord7(v) {
38086
+ function isRecord8(v) {
37575
38087
  return typeof v === "object" && v !== null && !Array.isArray(v);
37576
38088
  }
37577
38089
  function isEntry2(v) {
37578
- return isRecord7(v) && typeof v.resolvedInputHash === "string" && "output" in v;
38090
+ return isRecord8(v) && typeof v.resolvedInputHash === "string" && "output" in v;
37579
38091
  }
37580
38092
  function createFileStepCache(cacheKey, opts) {
37581
38093
  const dir = opts?.dir ?? DEFAULT_CACHE_DIR();
@@ -37588,7 +38100,7 @@ function createFileStepCache(cacheKey, opts) {
37588
38100
  } catch {
37589
38101
  return {};
37590
38102
  }
37591
- if (!isRecord7(parsed)) return {};
38103
+ if (!isRecord8(parsed)) return {};
37592
38104
  const out = {};
37593
38105
  for (const [k, v] of Object.entries(parsed)) if (isEntry2(v)) out[k] = v;
37594
38106
  return out;
@@ -44722,12 +45234,12 @@ async function readBody2(req) {
44722
45234
  }
44723
45235
  return { raw: Buffer.concat(chunks).toString("utf8") };
44724
45236
  }
44725
- var isRecord8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
45237
+ var isRecord9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
44726
45238
  function parseParts(raw) {
44727
45239
  if (!Array.isArray(raw)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "message.parts must be an array");
44728
45240
  const parts = [];
44729
45241
  for (const p of raw) {
44730
- if (!isRecord8(p)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "each message part must be an object");
45242
+ if (!isRecord9(p)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "each message part must be an object");
44731
45243
  if ("text" in p) {
44732
45244
  if (typeof p.text !== "string") throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "part.text must be a string");
44733
45245
  parts.push({ text: p.text });
@@ -44760,13 +45272,13 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
44760
45272
  send(res, 200, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.parse, message: "parse error: body is not valid JSON" } });
44761
45273
  return true;
44762
45274
  }
44763
- if (!isRecord8(rpc) || rpc.jsonrpc !== "2.0" || typeof rpc.method !== "string") {
45275
+ if (!isRecord9(rpc) || rpc.jsonrpc !== "2.0" || typeof rpc.method !== "string") {
44764
45276
  send(res, 200, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.invalidRequest, message: 'invalid request: expected a JSON-RPC 2.0 object with a string "method" (batches are not supported)' } });
44765
45277
  return true;
44766
45278
  }
44767
45279
  const id = typeof rpc.id === "string" || typeof rpc.id === "number" ? rpc.id : null;
44768
45280
  const method = rpc.method;
44769
- const params = isRecord8(rpc.params) ? rpc.params : {};
45281
+ const params = isRecord9(rpc.params) ? rpc.params : {};
44770
45282
  const requested = negotiateVersion(
44771
45283
  typeof req.headers["a2a-version"] === "string" ? req.headers["a2a-version"] : void 0,
44772
45284
  new URL(req.url ?? "/", "http://localhost").searchParams.get("version")
@@ -44824,14 +45336,14 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
44824
45336
  throw new RpcFailure(JSON_RPC_ERROR.unsupportedOperation, `method "SendMessage" is not allowed for app "${appId}": its manifest does not declare accepts.tasks: true`);
44825
45337
  }
44826
45338
  const rawMessage = params.message;
44827
- if (!isRecord8(rawMessage) || rawMessage.role !== "ROLE_USER") {
45339
+ if (!isRecord9(rawMessage) || rawMessage.role !== "ROLE_USER") {
44828
45340
  throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'params.message must be an object with role "ROLE_USER"');
44829
45341
  }
44830
45342
  const parts = parseParts(rawMessage.parts);
44831
45343
  const { skill, prompt } = extractSkillRequest(
44832
- { parts, ...isRecord8(rawMessage.metadata) ? { metadata: rawMessage.metadata } : {} },
45344
+ { parts, ...isRecord9(rawMessage.metadata) ? { metadata: rawMessage.metadata } : {} },
44833
45345
  appId,
44834
- isRecord8(params.metadata) ? params.metadata : void 0
45346
+ isRecord9(params.metadata) ? params.metadata : void 0
44835
45347
  );
44836
45348
  if (!skill) {
44837
45349
  throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'no skill requested: set message.metadata.skill or start the first text part with "skill:<id>"');
@@ -52347,9 +52859,13 @@ function isCrashed(desc) {
52347
52859
  if (desc.kind !== "agent-cli") return false;
52348
52860
  if (desc.status !== "running") return false;
52349
52861
  if (desc.remote === true) return false;
52862
+ if (desc.adapterConnected === false) return true;
52350
52863
  if (desc.pid === null || desc.pid === void 0) return false;
52351
52864
  return desc.processAlive === false;
52352
52865
  }
52866
+ function crashReasonFor(desc) {
52867
+ return desc.adapterConnected === false ? "transport-closed" : "process-gone";
52868
+ }
52353
52869
  function runCrashDetectPass(opts) {
52354
52870
  const { registry, isServed } = opts;
52355
52871
  const intervalMs = opts.crashDetectIntervalMs;
@@ -52365,7 +52881,7 @@ function runCrashDetectPass(opts) {
52365
52881
  ids: []
52366
52882
  };
52367
52883
  for (const d of candidates) {
52368
- if (registry.markCrashed(d.id)) {
52884
+ if (registry.markCrashed(d.id, crashReasonFor(d))) {
52369
52885
  summary.crashed++;
52370
52886
  summary.ids.push(d.id);
52371
52887
  }
@@ -52535,6 +53051,7 @@ async function resolveMessagingDefaults(loadCfg = loadConfig) {
52535
53051
 
52536
53052
  // src/index.ts
52537
53053
  init_transcript_writer();
53054
+ init_session_index();
52538
53055
  var BINDINGS_FILE_PATH = () => resolve(homedir(), ".agentproto", "transmitter-bindings.json");
52539
53056
  var PERSIST_DEBOUNCE_MS5 = 1500;
52540
53057
  var keyOf = (alias, source, contactRef) => `${alias}:${source}:${contactRef}`;
@@ -52948,12 +53465,13 @@ async function ssrfFetch(url, init, io = {}) {
52948
53465
  if ((init.body?.byteLength ?? 0) > MAX_REQUEST_BODY_BYTES) {
52949
53466
  throw new SsrfFetchError("connect", `request body exceeds the ${MAX_REQUEST_BODY_BYTES}-byte clamp`);
52950
53467
  }
53468
+ const port = parsed.port ? Number(parsed.port) : 443;
52951
53469
  if (testDispatcher) {
52952
53470
  const literal = parseIpv4(parsed.hostname);
52953
53471
  if (literal && !ipv4Public(literal[0], literal[1], literal[2], literal[3])) {
52954
53472
  throw new SsrfFetchError("private_target", `literal non-public address ${parsed.hostname}`);
52955
53473
  }
52956
- return await testDispatcher({ url, headers: init.headers ?? {}, body: init.body, timeoutMs: init.timeoutMs });
53474
+ return await testDispatcher({ url, headers: finalHeaders(init.headers ?? {}, parsed.hostname, port), body: init.body, timeoutMs: init.timeoutMs });
52957
53475
  }
52958
53476
  const candidates = await resolveHost(parsed.hostname, io.resolvers ?? {});
52959
53477
  if (candidates.length === 0) {
@@ -52965,17 +53483,26 @@ async function ssrfFetch(url, init, io = {}) {
52965
53483
  }
52966
53484
  const connectIp = candidates[0] ?? candidates[candidates.length - 1];
52967
53485
  if (connectIp === void 0) throw new SsrfFetchError("connect", "no address available for connect");
52968
- const port = parsed.port ? Number(parsed.port) : 443;
52969
53486
  return await rawPost({
52970
53487
  connectIp,
52971
53488
  servername: parsed.hostname,
52972
53489
  port,
52973
53490
  path: `${parsed.pathname}${parsed.search}`,
52974
- headers: init.headers ?? {},
53491
+ // HTTP Host header must stay the ORIGINAL hostname — `request({host})`
53492
+ // below only picks the TCP destination; a literal-IP Host is answered
53493
+ // 403/421 by name-based vhosts (tested live against cloudflared+CF edge),
53494
+ // which would falsely categorise every virtual-hosted subscriber as
53495
+ // non_2xx. An explicit caller Host header wins (finalHeaders keeps it).
53496
+ headers: finalHeaders(init.headers ?? {}, parsed.hostname, port),
52975
53497
  body: init.body,
52976
53498
  timeoutMs: init.timeoutMs
52977
53499
  });
52978
53500
  }
53501
+ function finalHeaders(headers, hostname6, port) {
53502
+ const hasHost = "Host" in headers || "host" in headers;
53503
+ if (hasHost) return headers;
53504
+ return { ...headers, Host: port === 443 ? hostname6 : `${hostname6}:${port}` };
53505
+ }
52979
53506
  function rawPost(args) {
52980
53507
  return new Promise((resolve42, reject) => {
52981
53508
  const req = request({
@@ -52986,6 +53513,8 @@ function rawPost(args) {
52986
53513
  path: args.path,
52987
53514
  method: "POST",
52988
53515
  servername: args.servername,
53516
+ // Host already defaulted to the ORIGINAL hostname by finalHeaders —
53517
+ // the socket only carries `args.connectIp` as its TCP destination.
52989
53518
  headers: args.headers,
52990
53519
  rejectUnauthorized: true
52991
53520
  });
@@ -53103,6 +53632,562 @@ async function deliverEventEnvelope(replay, event, deps2 = {}) {
53103
53632
  }
53104
53633
  return { ok: false, reason: "retries_exhausted", delivery };
53105
53634
  }
53635
+ function challengeReasonForSsrf(reason) {
53636
+ if (reason === "non_https") return "non_https";
53637
+ if (reason === "private_target" || reason === "redirect") return "ssrf_blocked";
53638
+ return "timeout";
53639
+ }
53640
+ var CHALLENGE_TTL_MS = 10 * 60 * 1e3;
53641
+ var encoder = new TextEncoder();
53642
+ var cache = /* @__PURE__ */ new Map();
53643
+ function challengeCacheSize() {
53644
+ return cache.size;
53645
+ }
53646
+ function resetChallengeCacheForTests() {
53647
+ cache.clear();
53648
+ }
53649
+ function normalizeCallbackUrl(url) {
53650
+ try {
53651
+ const u = new URL(url);
53652
+ const port = u.port === "443" ? "" : `:${u.port}`;
53653
+ return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${u.pathname}${u.search}`;
53654
+ } catch {
53655
+ return null;
53656
+ }
53657
+ }
53658
+ function hashSecret(secret) {
53659
+ return createHash("sha256").update(secret, "utf8").digest("hex");
53660
+ }
53661
+ function constantTimeEq(a, b) {
53662
+ const da = createHash("sha256").update(a, "utf8").digest();
53663
+ const db = createHash("sha256").update(b, "utf8").digest();
53664
+ return timingSafeEqual(da, db);
53665
+ }
53666
+ async function verifyCallback(input, deps2 = {}) {
53667
+ const now = deps2.now ?? (() => Date.now());
53668
+ const normalized = normalizeCallbackUrl(input.url);
53669
+ if (normalized === null || !normalized.startsWith("https://")) {
53670
+ return { ok: false, reason: "non_https", detail: "callback URL must be an https URL" };
53671
+ }
53672
+ if (decodeWhsecSecret(input.secret) === null) {
53673
+ return {
53674
+ ok: false,
53675
+ reason: "challenge_failed",
53676
+ detail: "invalid secret format \u2014 must be whsec_ + base64 decoding to 24..64 bytes (the caller validates before this; a bad secret is never cached)"
53677
+ };
53678
+ }
53679
+ const cacheKey = `${input.principal}|${normalized}|${hashSecret(input.secret)}`;
53680
+ const cached = cache.get(cacheKey);
53681
+ if (cached && cached.expiresAt > now()) {
53682
+ return { ok: true, verificationBytes: cached.requestBytes };
53683
+ }
53684
+ if (cached) cache.delete(cacheKey);
53685
+ const challenge = randomBytes(32).toString("hex");
53686
+ const requestBytes = encoder.encode(JSON.stringify({ type: "verification", challenge }));
53687
+ const msgId = `msg_verification_${randomBytes(12).toString("hex")}`;
53688
+ const timestamp = Math.floor(now() / 1e3);
53689
+ let headers;
53690
+ try {
53691
+ const signed = signWebhook({ msgId, timestamp, payload: requestBytes, secrets: [input.secret] });
53692
+ headers = {
53693
+ ...signed,
53694
+ "content-type": "application/json",
53695
+ "X-MCP-Subscription-Id": input.subscriptionId
53696
+ };
53697
+ } catch (err) {
53698
+ return { ok: false, reason: "challenge_failed", detail: `signing failed: ${err.message}` };
53699
+ }
53700
+ let response;
53701
+ try {
53702
+ response = await (deps2.fetch ?? ssrfFetch)(input.url, {
53703
+ method: "POST",
53704
+ headers,
53705
+ body: requestBytes,
53706
+ timeoutMs: 1e4
53707
+ // default challenge timeout (delivery uses 15_000)
53708
+ });
53709
+ } catch (err) {
53710
+ if (err instanceof SsrfFetchError) {
53711
+ return { ok: false, reason: challengeReasonForSsrf(err.reason), detail: `${err.reason}: ${err.message}` };
53712
+ }
53713
+ return { ok: false, reason: "timeout", detail: err.message };
53714
+ }
53715
+ if (response.status < 200 || response.status > 299) {
53716
+ return { ok: false, reason: "non_2xx", detail: `callback responded with status ${response.status}` };
53717
+ }
53718
+ let parsedBody;
53719
+ try {
53720
+ parsedBody = JSON.parse(response.body);
53721
+ } catch {
53722
+ return { ok: false, reason: "challenge_failed", detail: "response body is not JSON" };
53723
+ }
53724
+ const echoed = parsedBody?.challenge;
53725
+ if (typeof echoed !== "string" || !constantTimeEq(echoed, challenge)) {
53726
+ return { ok: false, reason: "challenge_failed", detail: "response did not echo the fresh challenge" };
53727
+ }
53728
+ cache.set(cacheKey, { requestBytes, expiresAt: now() + CHALLENGE_TTL_MS });
53729
+ return { ok: true, verificationBytes: requestBytes };
53730
+ }
53731
+ function canonicalizeJcs(value) {
53732
+ if (value === null) return "null";
53733
+ switch (typeof value) {
53734
+ case "boolean":
53735
+ return value ? "true" : "false";
53736
+ case "number":
53737
+ if (!Number.isFinite(value)) throw new Error("JCS: non-finite number is not JSON");
53738
+ return JSON.stringify(value);
53739
+ case "string":
53740
+ return JSON.stringify(value);
53741
+ case "object":
53742
+ break;
53743
+ default:
53744
+ throw new Error(`JCS: unsupported value type "${typeof value}"`);
53745
+ }
53746
+ if (Array.isArray(value)) {
53747
+ return `[${value.map(canonicalizeJcs).join(",")}]`;
53748
+ }
53749
+ const obj = value;
53750
+ const keys = Object.keys(obj).sort();
53751
+ const members = keys.map((key) => `${JSON.stringify(key)}:${canonicalizeJcs(obj[key])}`);
53752
+ return `{${members.join(",")}}`;
53753
+ }
53754
+ function subscriptionId(identity) {
53755
+ const canonical3 = canonicalizeJcs({
53756
+ principal: identity.principal,
53757
+ callbackUrl: identity.callbackUrl,
53758
+ eventName: identity.eventName,
53759
+ args: identity.args
53760
+ });
53761
+ const digest = createHash("sha256").update(canonical3, "utf8").digest("hex");
53762
+ return `sub_${digest.slice(0, 32)}`;
53763
+ }
53764
+
53765
+ // src/mcp-events/events-registry.ts
53766
+ function daemonBearerPrincipal() {
53767
+ return "daemon-bearer";
53768
+ }
53769
+ function sessionPrincipal(sessionId) {
53770
+ return `session:${sessionId}`;
53771
+ }
53772
+ function typeMatches(type, value) {
53773
+ if (Array.isArray(type)) return type.some((t) => typeMatches(t, value));
53774
+ switch (type) {
53775
+ case "object":
53776
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53777
+ case "array":
53778
+ return Array.isArray(value);
53779
+ case "string":
53780
+ return typeof value === "string";
53781
+ case "number":
53782
+ return typeof value === "number" && Number.isFinite(value);
53783
+ case "integer":
53784
+ return typeof value === "number" && Number.isInteger(value);
53785
+ case "boolean":
53786
+ return typeof value === "boolean";
53787
+ case "null":
53788
+ return value === null;
53789
+ default:
53790
+ return true;
53791
+ }
53792
+ }
53793
+ function deepEqual2(a, b) {
53794
+ if (a === b) return true;
53795
+ if (typeof a !== typeof b) return false;
53796
+ if (Array.isArray(a) && Array.isArray(b)) {
53797
+ return a.length === b.length && a.every((v, i) => deepEqual2(v, b[i]));
53798
+ }
53799
+ if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) {
53800
+ const ao = a;
53801
+ const bo = b;
53802
+ const ak = Object.keys(ao);
53803
+ const bk = Object.keys(bo);
53804
+ return ak.length === bk.length && ak.every((k) => deepEqual2(ao[k], bo[k]));
53805
+ }
53806
+ return false;
53807
+ }
53808
+ function validateNode(schema, value, path) {
53809
+ const at = path || "$";
53810
+ if (schema.type !== void 0 && !typeMatches(schema.type, value)) {
53811
+ return { ok: false, reason: `${at}: expected ${String(schema.type)}` };
53812
+ }
53813
+ if (schema.const !== void 0 && !deepEqual2(schema.const, value)) {
53814
+ return { ok: false, reason: `${at}: must equal the const value` };
53815
+ }
53816
+ if (Array.isArray(schema.enum) && !schema.enum.some((e) => deepEqual2(e, value))) {
53817
+ return { ok: false, reason: `${at}: not one of the allowed values` };
53818
+ }
53819
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
53820
+ const obj = value;
53821
+ const required = Array.isArray(schema.required) ? schema.required : [];
53822
+ for (const key of required) {
53823
+ if (!(key in obj)) return { ok: false, reason: `${at}: missing required property "${key}"` };
53824
+ }
53825
+ const properties = schema.properties ?? {};
53826
+ if (schema.additionalProperties === false) {
53827
+ for (const key of Object.keys(obj)) {
53828
+ if (!(key in properties)) return { ok: false, reason: `${at}: unexpected property "${key}"` };
53829
+ }
53830
+ }
53831
+ for (const [key, sub] of Object.entries(properties)) {
53832
+ if (key in obj) {
53833
+ const result = validateNode(sub, obj[key], `${path}.${key}`);
53834
+ if (!result.ok) return result;
53835
+ }
53836
+ }
53837
+ }
53838
+ if (Array.isArray(value) && schema.items !== void 0) {
53839
+ const items = schema.items;
53840
+ for (let i = 0; i < value.length; i++) {
53841
+ const result = validateNode(items, value[i], `${path}[${i}]`);
53842
+ if (!result.ok) return result;
53843
+ }
53844
+ }
53845
+ if (typeof value === "string") {
53846
+ if (typeof schema.minLength === "number" && value.length < schema.minLength) {
53847
+ return { ok: false, reason: `${at}: shorter than ${schema.minLength}` };
53848
+ }
53849
+ if (typeof schema.pattern === "string" && !new RegExp(schema.pattern).test(value)) {
53850
+ return { ok: false, reason: `${at}: does not match the required pattern` };
53851
+ }
53852
+ }
53853
+ if (typeof value === "number" && typeof schema.minimum === "number" && value < schema.minimum) {
53854
+ return { ok: false, reason: `${at}: below the minimum ${schema.minimum}` };
53855
+ }
53856
+ return { ok: true };
53857
+ }
53858
+ function validateAgainstInputSchema(schema, args) {
53859
+ return validateNode(schema, args, "");
53860
+ }
53861
+ var PR_ARGS_INPUT_SCHEMA = {
53862
+ type: "object",
53863
+ properties: {
53864
+ repo: {
53865
+ type: "string",
53866
+ description: "Repository in `owner/name` form, e.g. `agentproto/ts`.",
53867
+ pattern: "^[^/\\s]+/[^/\\s]+$"
53868
+ },
53869
+ number: {
53870
+ type: "integer",
53871
+ description: "Pull request number.",
53872
+ minimum: 1
53873
+ }
53874
+ },
53875
+ required: ["repo", "number"],
53876
+ additionalProperties: false
53877
+ };
53878
+ var ENVELOPE_DATA_PROPERTIES = {
53879
+ subject: { type: "string", description: "CloudEvents subject the event is filed under." },
53880
+ summary: { type: "string", description: "One-line human-readable summary." }
53881
+ };
53882
+ function payloadSchema2(properties, required) {
53883
+ return {
53884
+ type: "object",
53885
+ properties: { ...properties, ...ENVELOPE_DATA_PROPERTIES },
53886
+ required: [...required, "subject", "summary"],
53887
+ additionalProperties: false
53888
+ };
53889
+ }
53890
+ function githubPrArgumentsToMatch(eventName) {
53891
+ return (args) => {
53892
+ const repo = args.repo;
53893
+ const number = args.number;
53894
+ if (typeof repo !== "string" || !/^[^/\s]+\/[^/\s]+$/.test(repo)) {
53895
+ return { ok: false, reason: "`repo` must be `owner/name`" };
53896
+ }
53897
+ if (typeof number !== "number" || !Number.isInteger(number) || number <= 0) {
53898
+ return { ok: false, reason: "`number` must be a positive integer" };
53899
+ }
53900
+ return {
53901
+ ok: true,
53902
+ matchClauses: [{ subject: `github:${repo}#${number}`, types: [eventName] }],
53903
+ providerSlug: "local-gh"
53904
+ };
53905
+ };
53906
+ }
53907
+ function githubAuthorize(_args, _principal) {
53908
+ return true;
53909
+ }
53910
+ function githubEvent(input) {
53911
+ return {
53912
+ definition: {
53913
+ name: input.name,
53914
+ description: input.description,
53915
+ delivery: ["webhook"],
53916
+ inputSchema: PR_ARGS_INPUT_SCHEMA,
53917
+ payloadSchema: input.payloadSchema
53918
+ },
53919
+ meta: {
53920
+ // v1 REALITY: no provider has historical replay (plan §2) — every
53921
+ // definition is non-replayable and always returns `cursor: null`.
53922
+ replayable: false,
53923
+ argumentsToMatch: githubPrArgumentsToMatch(input.name),
53924
+ authorize: githubAuthorize
53925
+ }
53926
+ };
53927
+ }
53928
+ var GITHUB_SCHEME = {
53929
+ scheme: "github",
53930
+ events: [
53931
+ githubEvent({
53932
+ name: "github.pull_request.closed",
53933
+ description: "A pull request was closed or merged.",
53934
+ payloadSchema: payloadSchema2(
53935
+ {
53936
+ action: { type: "string", const: "closed" },
53937
+ merged: { type: "boolean", description: "True when the PR was merged rather than closed unmerged." },
53938
+ repo: { type: "string" },
53939
+ number: { type: "integer" }
53940
+ },
53941
+ ["action", "merged", "repo", "number"]
53942
+ )
53943
+ }),
53944
+ githubEvent({
53945
+ name: "github.pull_request.synchronize",
53946
+ description: "New commits were pushed to a pull request's head branch.",
53947
+ payloadSchema: payloadSchema2(
53948
+ {
53949
+ action: { type: "string", const: "synchronize" },
53950
+ repo: { type: "string" },
53951
+ number: { type: "integer" },
53952
+ headSha: { type: "string" }
53953
+ },
53954
+ ["action", "repo", "number", "headSha"]
53955
+ )
53956
+ }),
53957
+ githubEvent({
53958
+ name: "github.pull_request_review.submitted",
53959
+ description: "A review was submitted on a pull request.",
53960
+ payloadSchema: payloadSchema2(
53961
+ {
53962
+ action: { type: "string", const: "submitted" },
53963
+ state: { type: "string", description: "Review state, e.g. `approved`, `changes_requested`." },
53964
+ repo: { type: "string" },
53965
+ number: { type: "integer" },
53966
+ actor: { type: "string" }
53967
+ },
53968
+ ["action", "state", "repo", "number", "actor"]
53969
+ )
53970
+ }),
53971
+ githubEvent({
53972
+ name: "github.check_suite.completed",
53973
+ description: "The checks on a pull request finished (a `failure` conclusion is the build-failed signal).",
53974
+ payloadSchema: payloadSchema2(
53975
+ {
53976
+ action: { type: "string", const: "completed" },
53977
+ conclusion: { type: "string", description: "Rolled-up worst-of conclusion across the completed checks." },
53978
+ repo: { type: "string" },
53979
+ number: { type: "integer" },
53980
+ checks: {
53981
+ type: "array",
53982
+ items: {
53983
+ type: "object",
53984
+ properties: {
53985
+ name: { type: "string" },
53986
+ conclusion: { type: ["string", "null"] }
53987
+ },
53988
+ required: ["name", "conclusion"],
53989
+ additionalProperties: true
53990
+ }
53991
+ }
53992
+ },
53993
+ ["action", "conclusion", "repo", "number", "checks"]
53994
+ )
53995
+ })
53996
+ ]
53997
+ };
53998
+ var EVENT_REGISTRY = [GITHUB_SCHEME];
53999
+ function tenantScope(_principal) {
54000
+ return EVENT_REGISTRY;
54001
+ }
54002
+ function findEventDefinition(name) {
54003
+ for (const scheme of EVENT_REGISTRY) {
54004
+ for (const event of scheme.events) {
54005
+ if (event.definition.name === name) return event;
54006
+ }
54007
+ }
54008
+ return void 0;
54009
+ }
54010
+
54011
+ // src/mcp-events/adapter.ts
54012
+ var DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
54013
+ var MAX_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
54014
+ var MIN_TTL_MS = 60 * 1e3;
54015
+ function computeRefreshBefore(until) {
54016
+ return until.kind === "at" ? new Date(until.ms).toISOString() : null;
54017
+ }
54018
+ function untilForTtl(ttlMs, now) {
54019
+ if (ttlMs === null) return { kind: "never" };
54020
+ const requested = ttlMs === void 0 ? DEFAULT_TTL_MS : ttlMs;
54021
+ const granted = Math.max(MIN_TTL_MS, Math.min(requested, MAX_TTL_MS));
54022
+ return { kind: "at", ms: now + granted };
54023
+ }
54024
+ var McpEventsError = class extends Error {
54025
+ code;
54026
+ data;
54027
+ constructor(code, message, data) {
54028
+ super(message);
54029
+ this.name = "McpEventsError";
54030
+ this.code = code;
54031
+ this.data = data;
54032
+ }
54033
+ };
54034
+ function invalidParams(reason, detail) {
54035
+ return new McpEventsError(-32602, `Invalid params: ${detail}`, { reason });
54036
+ }
54037
+ function callbackEndpointError(reason, detail) {
54038
+ return new McpEventsError(-32015, `CallbackEndpointError: ${detail}`, { reason });
54039
+ }
54040
+ var mutationChain = Promise.resolve();
54041
+ function withSubscribeMutex(fn) {
54042
+ const run = mutationChain.then(fn, fn);
54043
+ mutationChain = run.catch(() => void 0);
54044
+ return run;
54045
+ }
54046
+ function isRecord10(value) {
54047
+ return typeof value === "object" && value !== null && !Array.isArray(value);
54048
+ }
54049
+ var LIST_PAGE_DEFAULT = 50;
54050
+ var LIST_PAGE_MAX = 200;
54051
+ function encodeListCursor(offset, scheme) {
54052
+ return Buffer.from(JSON.stringify({ offset, scheme }), "utf8").toString("base64url");
54053
+ }
54054
+ function decodeListCursor(cursor) {
54055
+ if (cursor === void 0 || cursor === "") return 0;
54056
+ try {
54057
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
54058
+ if (typeof parsed.offset !== "number" || !Number.isInteger(parsed.offset) || parsed.offset < 0) {
54059
+ throw new Error("bad offset");
54060
+ }
54061
+ return parsed.offset;
54062
+ } catch {
54063
+ throw invalidParams("invalid_cursor", "`cursor` is not a cursor issued by a previous events/list response");
54064
+ }
54065
+ }
54066
+ function requireWebhookDelivery(delivery) {
54067
+ if (!isRecord10(delivery) || delivery.mode !== "webhook") {
54068
+ throw invalidParams("unsupported_delivery_mode", 'delivery.mode must be "webhook"');
54069
+ }
54070
+ if (typeof delivery.url !== "string" || delivery.url.length === 0) {
54071
+ throw invalidParams("invalid_callback_url", "delivery.url is required");
54072
+ }
54073
+ return { mode: "webhook", url: delivery.url, ...typeof delivery.secret === "string" ? { secret: delivery.secret } : {} };
54074
+ }
54075
+ function validateArgsOrThrow(name, registration, args) {
54076
+ if (!isRecord10(args)) throw invalidParams("invalid_arguments", "`arguments` must be an object");
54077
+ const schemaCheck = validateAgainstInputSchema(registration.definition.inputSchema, args);
54078
+ if (!schemaCheck.ok) {
54079
+ throw invalidParams("invalid_arguments", `arguments for "${name}": ${schemaCheck.reason}`);
54080
+ }
54081
+ return args;
54082
+ }
54083
+ async function eventsList(r, ctx) {
54084
+ const schemes = tenantScope(ctx.principal);
54085
+ const definitions = schemes.flatMap((scheme2) => scheme2.events.map((event) => event.definition));
54086
+ const requested = r.pageSize ?? LIST_PAGE_DEFAULT;
54087
+ const pageSize = Math.max(1, Math.min(Number.isFinite(requested) ? Math.floor(requested) : LIST_PAGE_DEFAULT, LIST_PAGE_MAX));
54088
+ const offset = decodeListCursor(r.cursor);
54089
+ const page = definitions.slice(offset, offset + pageSize);
54090
+ const next = offset + page.length;
54091
+ const scheme = schemes[0]?.scheme ?? "*";
54092
+ return { events: page, nextCursor: next < definitions.length ? encodeListCursor(next, scheme) : null };
54093
+ }
54094
+ async function eventsSubscribe(input, ctx) {
54095
+ const now = ctx.nowMs ?? Date.now;
54096
+ const registration = findEventDefinition(input.name);
54097
+ if (!registration) throw invalidParams("unknown_event", `unknown event "${input.name}"`);
54098
+ const args = validateArgsOrThrow(input.name, registration, input.arguments);
54099
+ if (!registration.meta.authorize(args, ctx.principal)) {
54100
+ throw invalidParams("unauthorized", `not authorized to subscribe to "${input.name}" with the given arguments`);
54101
+ }
54102
+ const mapped = registration.meta.argumentsToMatch(args);
54103
+ if (!mapped.ok) throw invalidParams("invalid_arguments", `arguments for "${input.name}": ${mapped.reason}`);
54104
+ const delivery = requireWebhookDelivery(input.delivery);
54105
+ const secret = delivery.secret;
54106
+ if (secret === void 0 || decodeWhsecSecret(secret) === null) {
54107
+ throw invalidParams(
54108
+ "invalid_secret",
54109
+ "delivery.secret must be `whsec_` followed by base64 that decodes to 24..64 bytes"
54110
+ );
54111
+ }
54112
+ if (input.ttlMs !== void 0 && input.ttlMs !== null && (typeof input.ttlMs !== "number" || !Number.isFinite(input.ttlMs))) {
54113
+ throw invalidParams("invalid_ttl", "`ttlMs` must be a finite number or null");
54114
+ }
54115
+ const id = subscriptionId({
54116
+ principal: ctx.principal,
54117
+ callbackUrl: delivery.url,
54118
+ eventName: input.name,
54119
+ args
54120
+ });
54121
+ const verify = ctx.verify ?? verifyCallback;
54122
+ const outcome = await verify({ principal: ctx.principal, url: delivery.url, subscriptionId: id, secret });
54123
+ if (!outcome.ok) throw callbackEndpointError(outcome.reason, outcome.detail);
54124
+ return withSubscribeMutex(async () => {
54125
+ const until = untilForTtl(input.ttlMs, now());
54126
+ const existing = ctx.store.get(id);
54127
+ if (existing) {
54128
+ const target = existing.spec.target;
54129
+ if (target.kind !== "webhook") {
54130
+ throw invalidParams("identity_conflict", `subscription ${id} exists with a non-webhook target`);
54131
+ }
54132
+ const ref = ctx.store.materializeWebhookTargetRef(target);
54133
+ if (ref !== void 0) ctx.store.putSentinelSecret(ref, { secret });
54134
+ ctx.store.update(id, { spec: { ...existing.spec, until }, status: "active" });
54135
+ return { id, refreshBefore: computeRefreshBefore(until), cursor: null };
54136
+ }
54137
+ const provider = await ctx.resolveProvider(mapped.providerSlug);
54138
+ if (!provider) {
54139
+ throw invalidParams("provider_unavailable", `sentinel provider "${mapped.providerSlug}" is not available`);
54140
+ }
54141
+ const spec = {
54142
+ match: mapped.matchClauses,
54143
+ until,
54144
+ target: { kind: "webhook", url: delivery.url, secret },
54145
+ provider: mapped.providerSlug
54146
+ };
54147
+ const handle = await provider.create(spec, deliveryPreferenceFor(provider, ctx.activeIntervalMs ?? 15e3), {
54148
+ sentinelId: id
54149
+ });
54150
+ ctx.store.create({ id, spec, provider: mapped.providerSlug, handle });
54151
+ return { id, refreshBefore: computeRefreshBefore(until), cursor: null };
54152
+ });
54153
+ }
54154
+ async function eventsUnsubscribe(input, ctx) {
54155
+ const registration = findEventDefinition(input.name);
54156
+ if (!registration) throw invalidParams("unknown_event", `unknown event "${input.name}"`);
54157
+ const args = validateArgsOrThrow(input.name, registration, input.arguments);
54158
+ if (!registration.meta.authorize(args, ctx.principal)) {
54159
+ throw invalidParams("unauthorized", `not authorized to unsubscribe from "${input.name}" with the given arguments`);
54160
+ }
54161
+ const delivery = requireWebhookDelivery(input.delivery);
54162
+ const id = subscriptionId({
54163
+ principal: ctx.principal,
54164
+ callbackUrl: delivery.url,
54165
+ eventName: input.name,
54166
+ args
54167
+ });
54168
+ await withSubscribeMutex(async () => {
54169
+ const existing = ctx.store.get(id);
54170
+ if (!existing) return;
54171
+ const provider = await ctx.resolveProvider(existing.provider);
54172
+ if (provider) {
54173
+ try {
54174
+ await provider.cancel(existing.handle);
54175
+ } catch {
54176
+ }
54177
+ }
54178
+ ctx.store.remove(id);
54179
+ });
54180
+ return {};
54181
+ }
54182
+ function toMcpEvent(event) {
54183
+ return {
54184
+ eventId: event.id,
54185
+ name: event.type,
54186
+ timestamp: event.time,
54187
+ data: { ...event.data, subject: event.subject, summary: event.summary },
54188
+ cursor: null
54189
+ };
54190
+ }
53106
54191
 
53107
54192
  // src/sentinel-webhook-outbox.ts
53108
54193
  function agentprotoHome3() {
@@ -53118,13 +54203,7 @@ function webhookRequestId(sentinelId, eventId) {
53118
54203
  return `sub_${digest.slice(0, 32)}`;
53119
54204
  }
53120
54205
  function toWebhookEnvelope(event) {
53121
- return {
53122
- eventId: event.id,
53123
- name: event.type,
53124
- timestamp: event.time,
53125
- data: { ...event.data, subject: event.subject, summary: event.summary },
53126
- cursor: null
53127
- };
54206
+ return toMcpEvent(event);
53128
54207
  }
53129
54208
  function createSentinelWebhookOutbox(opts) {
53130
54209
  const filePath = opts.filePath ?? resolve(agentprotoHome3(), "sentinel-webhook-outbox.json");
@@ -55026,12 +56105,12 @@ function canonicalJson(value) {
55026
56105
  }
55027
56106
  function isToolAppVisible(tool) {
55028
56107
  const meta = tool._meta;
55029
- const ui = isRecord9(meta?.ui) ? meta.ui : void 0;
56108
+ const ui = isRecord11(meta?.ui) ? meta.ui : void 0;
55030
56109
  const visibility = ui?.visibility;
55031
56110
  if (!Array.isArray(visibility)) return true;
55032
56111
  return visibility.includes("app");
55033
56112
  }
55034
- function isRecord9(v) {
56113
+ function isRecord11(v) {
55035
56114
  return typeof v === "object" && v !== null && !Array.isArray(v);
55036
56115
  }
55037
56116
  function buildUiToolIndex(tools) {
@@ -55556,19 +56635,19 @@ function classifyMcpError(err) {
55556
56635
  return { status: "unreachable", error };
55557
56636
  }
55558
56637
  function uiMetaOf(meta) {
55559
- if (!isRecord10(meta) || !isRecord10(meta.ui)) return {};
56638
+ if (!isRecord12(meta) || !isRecord12(meta.ui)) return {};
55560
56639
  const ui = meta.ui;
55561
56640
  const csp = cspOf(ui.csp);
55562
56641
  return {
55563
56642
  ...csp ? { csp } : {},
55564
- ...isRecord10(ui.permissions) ? { permissions: ui.permissions } : {},
56643
+ ...isRecord12(ui.permissions) ? { permissions: ui.permissions } : {},
55565
56644
  ...typeof ui.prefersBorder === "boolean" ? { prefersBorder: ui.prefersBorder } : {},
55566
56645
  ...typeof ui.domain === "string" ? { domain: ui.domain } : {}
55567
56646
  };
55568
56647
  }
55569
56648
  var CSP_DOMAIN_KEYS = ["connectDomains", "resourceDomains", "frameDomains", "baseUriDomains"];
55570
56649
  function cspOf(raw) {
55571
- if (!isRecord10(raw)) return void 0;
56650
+ if (!isRecord12(raw)) return void 0;
55572
56651
  const csp = {};
55573
56652
  for (const key of CSP_DOMAIN_KEYS) {
55574
56653
  const value = raw[key];
@@ -55578,7 +56657,7 @@ function cspOf(raw) {
55578
56657
  }
55579
56658
  return Object.keys(csp).length > 0 ? csp : void 0;
55580
56659
  }
55581
- function isRecord10(v) {
56660
+ function isRecord12(v) {
55582
56661
  return typeof v === "object" && v !== null && !Array.isArray(v);
55583
56662
  }
55584
56663
  function errorMessage(err) {
@@ -58087,7 +59166,7 @@ function humanizeKey(key) {
58087
59166
  const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-.]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
58088
59167
  return words.length === 0 ? "Value" : words.charAt(0).toUpperCase() + words.slice(1);
58089
59168
  }
58090
- function isRecord11(value) {
59169
+ function isRecord13(value) {
58091
59170
  return value !== null && typeof value === "object" && !Array.isArray(value);
58092
59171
  }
58093
59172
  function scalarText(value) {
@@ -58117,7 +59196,7 @@ function flattenRows(value, label, depth, out) {
58117
59196
  });
58118
59197
  return;
58119
59198
  }
58120
- if (isRecord11(value)) {
59199
+ if (isRecord13(value)) {
58121
59200
  if (depth >= MAX_DEPTH) return;
58122
59201
  for (const [key, child] of Object.entries(value)) {
58123
59202
  flattenRows(child, label ? `${label} / ${humanizeKey(key)}` : humanizeKey(key), depth + 1, out);
@@ -58139,7 +59218,7 @@ function renderMail(preview2) {
58139
59218
  const cc = addressList(preview2.cc);
58140
59219
  const subject = scalarText(preview2.subject) ?? "";
58141
59220
  const body = scalarText(preview2.body) ?? "";
58142
- const attachments = Array.isArray(preview2.attachments) ? preview2.attachments.filter(isRecord11).map((a) => {
59221
+ const attachments = Array.isArray(preview2.attachments) ? preview2.attachments.filter(isRecord13).map((a) => {
58143
59222
  const name = scalarText(a.fileName) ?? "";
58144
59223
  const size = typeof a.sizeBytes === "number" ? humanSize(a.sizeBytes) : "";
58145
59224
  return size ? `${name} (${size})` : name;
@@ -58154,9 +59233,9 @@ function renderMail(preview2) {
58154
59233
  }
58155
59234
  function renderPreview(record2) {
58156
59235
  const preview2 = record2.preview;
58157
- if (isRecord11(preview2) && preview2.kind === "mail") return renderMail(preview2);
59236
+ if (isRecord13(preview2) && preview2.kind === "mail") return renderMail(preview2);
58158
59237
  const rows = [];
58159
- if (isRecord11(preview2)) {
59238
+ if (isRecord13(preview2)) {
58160
59239
  for (const [key, value] of Object.entries(preview2)) flattenRows(value, humanizeKey(key), 1, rows);
58161
59240
  } else {
58162
59241
  flattenRows(preview2, "Details", 1, rows);
@@ -59608,6 +60687,8 @@ async function reconcileSandboxLedger(deps2) {
59608
60687
  // src/index.ts
59609
60688
  init_session_outcome();
59610
60689
  init_session_end_reason();
60690
+ init_transcript_writer();
60691
+ init_session_index();
59611
60692
  init_sessions();
59612
60693
  init_sessions();
59613
60694
  init_process_stats();
@@ -59627,12 +60708,12 @@ function measureToolList(tools) {
59627
60708
  }
59628
60709
  var packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
59629
60710
  var helpDir = join(packageRoot, "docs", "mcp-tools");
59630
- var cache = /* @__PURE__ */ new Map();
60711
+ var cache2 = /* @__PURE__ */ new Map();
59631
60712
  function getToolHelp(name) {
59632
- if (cache.has(name)) return cache.get(name);
60713
+ if (cache2.has(name)) return cache2.get(name);
59633
60714
  const path = join(helpDir, `${name}.md`);
59634
60715
  const content = existsSync(path) ? readFileSync(path, "utf8") : void 0;
59635
- cache.set(name, content);
60716
+ cache2.set(name, content);
59636
60717
  return content;
59637
60718
  }
59638
60719
  function listToolHelpTopics() {
@@ -61925,7 +63006,7 @@ async function registerSentinelAdapterTools(server, opts = {}) {
61925
63006
  makeListTool({
61926
63007
  server,
61927
63008
  toolName: "list_sentinel_adapters",
61928
- description: "List known sentinel providers with their status (supported/available/ready), version, and declared capabilities (subjects, push, poll, durable, needsPublicUrl, requiresAuth, typicalLatencyMs). Credentials are never returned. Use `setup_sentinel_provider` to configure a provider that needs creds.",
63009
+ description: "List known sentinel providers with their status (supported/available/ready), version, and declared capabilities (subjects, push, poll, durable, needsPublicUrl, requiresAuth, typicalLatencyMs). Credentials are never returned. Use `setup_sentinel_provider` to configure a provider that needs creds. A `webhook` sentinel target (Standard Webhooks signed delivery to an external HTTP callback, with persisted outbox and resume post-restart) is accepted since W-B; the `mcp-events` MCP surface (events/list, events/subscribe, events/unsubscribe) ships with W-C.",
61929
63010
  lister: makeSentinelLister({ credsStore, ledger })
61930
63011
  });
61931
63012
  const { validSlugs, fields } = await collectSetupSchema3();
@@ -62050,102 +63131,6 @@ function createWorkspaceFs(opts) {
62050
63131
 
62051
63132
  // src/index.ts
62052
63133
  init_role();
62053
- function challengeReasonForSsrf(reason) {
62054
- if (reason === "non_https") return "non_https";
62055
- if (reason === "private_target" || reason === "redirect") return "ssrf_blocked";
62056
- return "timeout";
62057
- }
62058
- var CHALLENGE_TTL_MS = 10 * 60 * 1e3;
62059
- var encoder = new TextEncoder();
62060
- var cache2 = /* @__PURE__ */ new Map();
62061
- function challengeCacheSize() {
62062
- return cache2.size;
62063
- }
62064
- function resetChallengeCacheForTests() {
62065
- cache2.clear();
62066
- }
62067
- function normalizeCallbackUrl(url) {
62068
- try {
62069
- const u = new URL(url);
62070
- const port = u.port === "443" ? "" : `:${u.port}`;
62071
- return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${u.pathname}${u.search}`;
62072
- } catch {
62073
- return null;
62074
- }
62075
- }
62076
- function hashSecret(secret) {
62077
- return createHash("sha256").update(secret, "utf8").digest("hex");
62078
- }
62079
- function constantTimeEq(a, b) {
62080
- const da = createHash("sha256").update(a, "utf8").digest();
62081
- const db = createHash("sha256").update(b, "utf8").digest();
62082
- return timingSafeEqual(da, db);
62083
- }
62084
- async function verifyCallback(input, deps2 = {}) {
62085
- const now = deps2.now ?? (() => Date.now());
62086
- const normalized = normalizeCallbackUrl(input.url);
62087
- if (normalized === null || !normalized.startsWith("https://")) {
62088
- return { ok: false, reason: "non_https", detail: "callback URL must be an https URL" };
62089
- }
62090
- if (decodeWhsecSecret(input.secret) === null) {
62091
- return {
62092
- ok: false,
62093
- reason: "challenge_failed",
62094
- detail: "invalid secret format \u2014 must be whsec_ + base64 decoding to 24..64 bytes (the caller validates before this; a bad secret is never cached)"
62095
- };
62096
- }
62097
- const cacheKey = `${input.principal}|${normalized}|${hashSecret(input.secret)}`;
62098
- const cached = cache2.get(cacheKey);
62099
- if (cached && cached.expiresAt > now()) {
62100
- return { ok: true, verificationBytes: cached.requestBytes };
62101
- }
62102
- if (cached) cache2.delete(cacheKey);
62103
- const challenge = randomBytes(32).toString("hex");
62104
- const requestBytes = encoder.encode(JSON.stringify({ type: "verification", challenge }));
62105
- const msgId = `msg_verification_${randomBytes(12).toString("hex")}`;
62106
- const timestamp = Math.floor(now() / 1e3);
62107
- let headers;
62108
- try {
62109
- const signed = signWebhook({ msgId, timestamp, payload: requestBytes, secrets: [input.secret] });
62110
- headers = {
62111
- ...signed,
62112
- "content-type": "application/json",
62113
- "X-MCP-Subscription-Id": input.subscriptionId
62114
- };
62115
- } catch (err) {
62116
- return { ok: false, reason: "challenge_failed", detail: `signing failed: ${err.message}` };
62117
- }
62118
- let response;
62119
- try {
62120
- response = await (deps2.fetch ?? ssrfFetch)(input.url, {
62121
- method: "POST",
62122
- headers,
62123
- body: requestBytes,
62124
- timeoutMs: 1e4
62125
- // default challenge timeout (delivery uses 15_000)
62126
- });
62127
- } catch (err) {
62128
- if (err instanceof SsrfFetchError) {
62129
- return { ok: false, reason: challengeReasonForSsrf(err.reason), detail: `${err.reason}: ${err.message}` };
62130
- }
62131
- return { ok: false, reason: "timeout", detail: err.message };
62132
- }
62133
- if (response.status < 200 || response.status > 299) {
62134
- return { ok: false, reason: "non_2xx", detail: `callback responded with status ${response.status}` };
62135
- }
62136
- let parsedBody;
62137
- try {
62138
- parsedBody = JSON.parse(response.body);
62139
- } catch {
62140
- return { ok: false, reason: "challenge_failed", detail: "response body is not JSON" };
62141
- }
62142
- const echoed = parsedBody?.challenge;
62143
- if (typeof echoed !== "string" || !constantTimeEq(echoed, challenge)) {
62144
- return { ok: false, reason: "challenge_failed", detail: "response did not echo the fresh challenge" };
62145
- }
62146
- cache2.set(cacheKey, { requestBytes, expiresAt: now() + CHALLENGE_TTL_MS });
62147
- return { ok: true, verificationBytes: requestBytes };
62148
- }
62149
63134
  async function hasNamedLlmEndpoints() {
62150
63135
  const override = process.env.LLM_ENDPOINT_ENDPOINTS_FILE?.trim();
62151
63136
  const path = override && override.length > 0 ? override : join(homedir(), ".agentproto", "llm-endpoints.json");
@@ -62174,7 +63159,7 @@ init_worktree_isolation();
62174
63159
  var pairing_registry_exports = {};
62175
63160
  __reExport(pairing_registry_exports, pairing_host_star);
62176
63161
  var JOIN_TOKENS_VERSION = 1;
62177
- var DEFAULT_TTL_MS = 90 * 864e5;
63162
+ var DEFAULT_TTL_MS2 = 90 * 864e5;
62178
63163
  var DEFAULT_RECONNECT_MIN_MS = 1e3;
62179
63164
  var DEFAULT_RECONNECT_MAX_MS = 3e4;
62180
63165
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 11e4;
@@ -62284,7 +63269,7 @@ function createJoinTokenRegistry(deps2) {
62284
63269
  const secret = b64url(randomBytes(16));
62285
63270
  const id = randomBytes(6).toString("hex");
62286
63271
  const nowIso2 = new Date(now()).toISOString();
62287
- const ttlMs = input.ttlMs ?? DEFAULT_TTL_MS;
63272
+ const ttlMs = input.ttlMs ?? DEFAULT_TTL_MS2;
62288
63273
  const expiresAt = new Date(now() + ttlMs).toISOString();
62289
63274
  const record2 = {
62290
63275
  id,
@@ -63456,6 +64441,20 @@ async function createGateway(opts) {
63456
64441
  });
63457
64442
  registerEvalReporterTools(server);
63458
64443
  registerPresetTools(server);
64444
+ const eventsPrincipal = callerSessionId ? sessionPrincipal(callerSessionId) : daemonBearerPrincipal();
64445
+ registerEventsMethods(server, {
64446
+ list: (params) => eventsList(params, { principal: eventsPrincipal }),
64447
+ subscribe: (params) => eventsSubscribe(params, {
64448
+ principal: eventsPrincipal,
64449
+ store: sentinelStore,
64450
+ resolveProvider: resolveSentinelProviderResolved
64451
+ }),
64452
+ unsubscribe: (params) => eventsUnsubscribe(params, {
64453
+ principal: eventsPrincipal,
64454
+ store: sentinelStore,
64455
+ resolveProvider: resolveSentinelProviderResolved
64456
+ })
64457
+ });
63459
64458
  return server;
63460
64459
  };
63461
64460
  let internalToolServerPromise;
@@ -63780,6 +64779,15 @@ async function createGateway(opts) {
63780
64779
  ...passOpts?.isServed ? { isServed: passOpts.isServed } : {}
63781
64780
  });
63782
64781
  },
64782
+ backfillSessionIndexes() {
64783
+ try {
64784
+ return backfillSessionIndexes(sessions.transcriptBaseDir, {
64785
+ descriptors: sessions.list({ includeArchived: true })
64786
+ });
64787
+ } catch {
64788
+ return { scanned: 0, created: 0, skipped: 0 };
64789
+ }
64790
+ },
63783
64791
  async stop() {
63784
64792
  heartbeat.stop();
63785
64793
  if (idleReapTimer) clearInterval(idleReapTimer);
@@ -63852,6 +64860,6 @@ var export_readPairingsSnapshot = pairing_registry_exports.readPairingsSnapshot;
63852
64860
  var export_removeProviderKey = providers_store_exports.removeProviderKey;
63853
64861
  var export_setProviderKey = providers_store_exports.setProviderKey;
63854
64862
 
63855
- export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAMP_LIMIT_BYTES, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEVICE_PROMPT_PATH_PREFIX, GC_REAPABLE_STATES, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_ATTEMPTS, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MODEL_ROLE_REF_PREFIX, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SIGN_NAMESPACE, SsrfFetchError, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, challengeCacheSize, challengeReasonForSsrf, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSentinelWebhookOutbox, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decodeWhsecSecret, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deliverEventEnvelope, deliveryBackoffDelay, deriveSessionOutcome, deriveSessionUsage, describeOwner, encodeWhsecSecret, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, envelopeClampedBytes, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, extractHelpSection, fetchHostTurns, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hostPlaceholders, hostRecordToMirrorRecord, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isNoSessionPromptFailure, isProviderLimitError, isPubliclyRoutable, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, mirrorCursorForSession, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCallbackUrl, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, promptDeviceSession, promptHostSession, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resetChallengeCacheForTests, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveDevicePromptMapping, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, seedWalkForTranscript, serializeEnvelope, setDefaultPreset, setEgressDispatcherForTests, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, signWebhook, ssrfFetch, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, syncDeviceMirror, toWebhookEnvelope, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, verifyCallback, verifyInboundSignature, verifySignedAttestation, webSearchTool, webhookRequestId, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
64863
+ export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAMP_LIMIT_BYTES, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_TTL_MS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEVICE_PROMPT_PATH_PREFIX, EVENT_REGISTRY, GC_REAPABLE_STATES, GITHUB_SCHEME, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, INDEX_DEFAULT_LIMIT, INDEX_MAX_OUTPUT, INDEX_MAX_PROMPT, INDEX_TAIL_BYTES, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_ATTEMPTS, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MAX_TTL_MS, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MIN_TTL_MS, MODEL_ROLE_REF_PREFIX, McpEventsError, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SIGN_NAMESPACE, SsrfFetchError, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, attachSandbox, backfillSessionIndexes, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, buildSessionRecap, canonicalForModeId, canonicalizeJcs, challengeCacheSize, challengeReasonForSsrf, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, computeRefreshBefore, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSentinelWebhookOutbox, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonBearerPrincipal, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decodeWhsecSecret, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defaultTranscriptBaseDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deliverEventEnvelope, deliveryBackoffDelay, deriveIndexFromTranscript, deriveSessionOutcome, deriveSessionUsage, describeOwner, encodeWhsecSecret, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, envelopeClampedBytes, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, eventsList, eventsSubscribe, eventsUnsubscribe, execGh, extractHelpSection, fetchHostTurns, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findEventDefinition, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hostPlaceholders, hostRecordToMirrorRecord, indexEntryFromDescriptor, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isNoSessionPromptFailure, isProviderLimitError, isPubliclyRoutable, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, matchesSessionQuery, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, mirrorCursorForSession, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCallbackUrl, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, promptDeviceSession, promptHostSession, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readAllSessionIndexes, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readSessionIndex, readTranscriptTail, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resetChallengeCacheForTests, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveDevicePromptMapping, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, searchSessionIndexes, seedWalkForTranscript, serializeEnvelope, sessionEventsPath, sessionIndexPath, sessionPrincipal, sessionTranscriptDir, setDefaultPreset, setDefaultSessionsBaseDir, setEgressDispatcherForTests, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, signWebhook, ssrfFetch, subscriptionId, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, syncDeviceMirror, tenantScope, toMcpEvent, toWebhookEnvelope, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, untilForTtl, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, validateAgainstInputSchema, verifyCallback, verifyInboundSignature, verifySignedAttestation, webSearchTool, webhookRequestId, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry, writeSessionIndex };
63856
64864
  //# sourceMappingURL=index.mjs.map
63857
64865
  //# sourceMappingURL=index.mjs.map