@agentproto/runtime 5.7.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",
@@ -9046,6 +9085,20 @@ var init_mcp_credential_deps = __esm({
9046
9085
  });
9047
9086
 
9048
9087
  // src/sandbox-agent-session-proxy.ts
9088
+ function stripAnsiCodes(s) {
9089
+ return s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
9090
+ }
9091
+ async function* harvestHostErrorNotice(host, remoteSessionId) {
9092
+ let tail;
9093
+ try {
9094
+ tail = await host.output(remoteSessionId, MAX_OUTPUT_LINES);
9095
+ } catch {
9096
+ return;
9097
+ }
9098
+ const lines = tail.split(/\r?\n/).map((l) => stripAnsiCodes(l).trim()).filter((l) => l.length > 0 && HOST_ERROR_LINE_RE.test(l));
9099
+ if (lines.length === 0) return;
9100
+ yield { kind: "notice", text: lines.join("\n") };
9101
+ }
9049
9102
  function extractPromptText(message) {
9050
9103
  if (typeof message === "string") return message;
9051
9104
  if (message && typeof message === "object" && "text" in message) {
@@ -9168,13 +9221,18 @@ function createSandboxAgentSessionProxy(opts) {
9168
9221
  let lastPrompt;
9169
9222
  let closed = false;
9170
9223
  let transcriptCursor = 0;
9224
+ let turnsSent = 0;
9171
9225
  return {
9172
9226
  sessionId: remoteSessionId,
9173
9227
  async *send(message) {
9174
9228
  const prompt = extractPromptText(message);
9175
9229
  lastPrompt = prompt;
9230
+ const isFirstTurn = turnsSent === 0;
9231
+ turnsSent++;
9176
9232
  const since = await host.currentEventsCursor();
9177
- await host.prompt(remoteSessionId, prompt);
9233
+ if (!(opts.promptCarriedInStart && isFirstTurn)) {
9234
+ await host.prompt(remoteSessionId, prompt);
9235
+ }
9178
9236
  const controller = new AbortController();
9179
9237
  let sseBody;
9180
9238
  try {
@@ -9193,15 +9251,23 @@ function createSandboxAgentSessionProxy(opts) {
9193
9251
  }
9194
9252
  try {
9195
9253
  let sawTurnEnd = false;
9254
+ let sawAssistantText = false;
9255
+ let sawToolCall = false;
9196
9256
  for await (const record2 of readSseRecords(sseBody)) {
9197
9257
  if (typeof record2.seq === "number") transcriptCursor = record2.seq;
9198
9258
  const evt = recordToStreamEvent(record2);
9199
9259
  if (!evt) continue;
9200
- yield evt;
9260
+ if (evt.kind === "text-delta" && evt.text?.trim()) sawAssistantText = true;
9261
+ else if (evt.kind === "tool-call") sawToolCall = true;
9201
9262
  if (evt.kind === "turn-end") {
9202
9263
  sawTurnEnd = true;
9264
+ if (opts.surfaceHostTurnErrors && isFirstTurn && (evt.reason === "error" || !sawAssistantText && !sawToolCall)) {
9265
+ yield* harvestHostErrorNotice(host, remoteSessionId);
9266
+ }
9267
+ yield evt;
9203
9268
  break;
9204
9269
  }
9270
+ yield evt;
9205
9271
  }
9206
9272
  if (!sawTurnEnd) {
9207
9273
  throw new Error(
@@ -9249,7 +9315,7 @@ function createSandboxAgentSessionProxy(opts) {
9249
9315
  }
9250
9316
  };
9251
9317
  }
9252
- var MAX_POLL_MS, MAX_OUTPUT_LINES, MAX_CONSECUTIVE_POLL_FAILURES, POLL_RETRY_DELAY_MS, STREAM_EVENT_RECORD_KINDS;
9318
+ var MAX_POLL_MS, MAX_OUTPUT_LINES, MAX_CONSECUTIVE_POLL_FAILURES, POLL_RETRY_DELAY_MS, HOST_ERROR_LINE_RE, STREAM_EVENT_RECORD_KINDS;
9253
9319
  var init_sandbox_agent_session_proxy = __esm({
9254
9320
  "src/sandbox-agent-session-proxy.ts"() {
9255
9321
  init_sandbox_ledger();
@@ -9257,6 +9323,7 @@ var init_sandbox_agent_session_proxy = __esm({
9257
9323
  MAX_OUTPUT_LINES = 500;
9258
9324
  MAX_CONSECUTIVE_POLL_FAILURES = 6;
9259
9325
  POLL_RETRY_DELAY_MS = 5e3;
9326
+ HOST_ERROR_LINE_RE = /\[(?:error|warning)\]|\berror\b|\bwarning\b|invalid model|model not found|unknown model|unsupported model/i;
9260
9327
  STREAM_EVENT_RECORD_KINDS = /* @__PURE__ */ new Set([
9261
9328
  "text-delta",
9262
9329
  "thought",
@@ -10077,35 +10144,10 @@ function findWorktreeLabelCwdCollision(registry, excludeId, label, cwd) {
10077
10144
  function profileMethodToAuthMode(method) {
10078
10145
  return method === "oauth-bearer" ? "subscription" : "api-key";
10079
10146
  }
10080
- function directAuthMethods(descriptor, endpoint) {
10081
- const methods = [];
10082
- if (subscriptionSurfaceFor(descriptor?.authSubscription, endpoint) !== void 0) {
10083
- methods.push("oauth-bearer");
10084
- }
10085
- if (descriptor?.provider || descriptor?.modelDerivedApiKey) methods.push("api-key");
10086
- return methods;
10087
- }
10088
10147
  function subscriptionOauthMethodId(descriptor, provider) {
10089
10148
  if (!Array.isArray(descriptor?.authSubscription) || !provider) return void 0;
10090
10149
  return `${provider}-oauth`;
10091
10150
  }
10092
- function spawnEligibilityManifest(adapter, descriptor, route, model) {
10093
- const directEndpoint = descriptor?.provider ?? (model ? descriptor?.modelProviders?.[model] : void 0) ?? (model && descriptor?.modelDerivedApiKey ? modelIdPrefixProvider(model) : void 0) ?? (model ? getModelProvider(model) : void 0);
10094
- const routeId = route?.gateway ?? directEndpoint;
10095
- if (!routeId) return void 0;
10096
- const direct = directEndpoint !== void 0 && routeId === directEndpoint;
10097
- return {
10098
- manifest: {
10099
- id: adapter,
10100
- endpointByRoute: { [routeId]: direct ? directEndpoint : routeId },
10101
- methodsByRoute: {
10102
- [routeId]: direct ? directAuthMethods(descriptor, directEndpoint) : ["api-key"]
10103
- }
10104
- },
10105
- routeId,
10106
- direct
10107
- };
10108
- }
10109
10151
  function resolveProfileAwareRoute(adapter, authDescriptor, profile, model) {
10110
10152
  if (!model) return void 0;
10111
10153
  const eligibleCandidates = serviceableModelRoutes(model).map((gateway) => spawnEligibilityManifest(adapter, authDescriptor, { gateway }, model)).filter(
@@ -10428,6 +10470,7 @@ async function spawnAgentSession(deps2, input) {
10428
10470
  const {
10429
10471
  registry,
10430
10472
  resolveAgentAdapter,
10473
+ listAgentAdapters,
10431
10474
  buildOrchestratorMcp,
10432
10475
  daemonMcpUrl,
10433
10476
  callerScope,
@@ -10914,10 +10957,16 @@ async function spawnAgentSession(deps2, input) {
10914
10957
  if (input.sandbox === void 0 && !resolved) {
10915
10958
  const profileHint = await authProfileAsAdapterHint(input.adapter, getAuthProfile);
10916
10959
  const gatewayHint = profileHint ? void 0 : gatewayAsAdapterHint(input.adapter);
10960
+ let installedHint;
10961
+ if (!profileHint && !gatewayHint) {
10962
+ const installed = await listAgentAdapters?.().catch(() => void 0);
10963
+ installedHint = installed && installed.length > 0 ? `Installed adapters: ${installed.map((a) => a.slug).sort().join(", ")}.` : void 0;
10964
+ }
10965
+ const genericHint = `If it was working a moment ago, something may be mid-rebuild \u2014 wait and retry. ` + (installedHint ?? `If it has never been installed, run \`agentproto install ${input.adapter}\` first.`);
10917
10966
  return {
10918
10967
  ok: false,
10919
10968
  code: "adapter_not_found",
10920
- message: profileHint ? `agent_start: adapter "${input.adapter}" could not be resolved. ${profileHint}` : gatewayHint ? `agent_start: adapter "${input.adapter}" could not be resolved. ${gatewayHint}` : `agent_start: adapter "${input.adapter}" could not be resolved. If it was working a moment ago, something may be mid-rebuild \u2014 wait and retry. If it has never been installed, run \`agentproto install ${input.adapter}\` first.`
10969
+ message: profileHint ? `agent_start: adapter "${input.adapter}" could not be resolved. ${profileHint}` : gatewayHint ? `agent_start: adapter "${input.adapter}" could not be resolved. ${gatewayHint}` : `agent_start: adapter "${input.adapter}" could not be resolved. ${genericHint}`
10921
10970
  };
10922
10971
  }
10923
10972
  if (resolveHostAuth && !resolved) {
@@ -11528,7 +11577,15 @@ ${asyncPrompt}`;
11528
11577
  // (never the value) before the box boots.
11529
11578
  ...authSpec ? { authSpec } : {},
11530
11579
  ...descriptorRoute ? { route: descriptorRoute } : {},
11531
- ...input.appServe ? { appServe: input.appServe } : {}
11580
+ ...input.appServe ? { appServe: input.appServe } : {},
11581
+ // F2 single-dial spawn — DEVICE ONLY: carry the composed initial
11582
+ // prompt INSIDE the inner `agent_start` (see `bootSandboxAgentSession`'s
11583
+ // `initialPrompt`) so the device bridge makes ONE dial, not two. The
11584
+ // controller still drives/records the first turn through the proxy
11585
+ // below (`registry.spawnAgent`'s `initialPrompt` / the `wait`
11586
+ // `sendPrompt`); `promptCarriedInStart` stops the proxy from
11587
+ // re-sending it. Non-device sandboxes are untouched (two-dial shape).
11588
+ ...isDeviceSpawn && effectivePrompt !== void 0 ? { initialPrompt: effectivePrompt } : {}
11532
11589
  });
11533
11590
  if (!booted.ok) return booted;
11534
11591
  agentSession = booted.agentSession;
@@ -11902,6 +11959,29 @@ function withSandboxInstallAdapters(spec) {
11902
11959
  function looksLikeHostOnlyPath(p) {
11903
11960
  return HOST_ONLY_PATH_PATTERNS.some((re) => re.test(p));
11904
11961
  }
11962
+ function isDeviceTransportError(err) {
11963
+ return DEVICE_TRANSPORT_ERROR_RE.test(err instanceof Error ? err.message : String(err));
11964
+ }
11965
+ function deviceTargetOf(sandbox) {
11966
+ const slug = typeof sandbox === "string" ? sandbox : sandbox.provider;
11967
+ return slug.startsWith("device:") ? slug.slice("device:".length) : void 0;
11968
+ }
11969
+ async function startDeviceSessionWithRetry(host, args, target) {
11970
+ const attempts = 2;
11971
+ let lastErr;
11972
+ for (let attempt = 1; attempt <= attempts; attempt++) {
11973
+ try {
11974
+ return await host.start(args);
11975
+ } catch (err) {
11976
+ lastErr = err;
11977
+ if (!isDeviceTransportError(err)) throw err;
11978
+ if (attempt < attempts) {
11979
+ await new Promise((resolve42) => setTimeout(resolve42, DEVICE_SPAWN_RETRY_DELAY_MS));
11980
+ }
11981
+ }
11982
+ }
11983
+ throw new DeviceSpawnUnreachableError(target, attempts, lastErr);
11984
+ }
11905
11985
  async function bootSandboxAgentSession(opts) {
11906
11986
  const providerSlug = typeof opts.sandbox === "string" ? opts.sandbox : opts.sandbox.provider;
11907
11987
  if (!opts.resolveSandboxProvider) {
@@ -12014,10 +12094,12 @@ async function bootSandboxAgentSession(opts) {
12014
12094
  ...lifecyclePolicy.pauseAfterIdleMs !== void 0 ? { expiresAt: new Date(Date.now() + lifecyclePolicy.pauseAfterIdleMs).toISOString() } : {}
12015
12095
  });
12016
12096
  const omitCwd = handle.omitCwdWhenImplicit === true && !opts.explicitCwd;
12097
+ const deviceTarget = deviceTargetOf(opts.sandbox);
12098
+ const carriedPrompt = deviceTarget !== void 0 && opts.initialPrompt !== void 0 ? typeof opts.initialPrompt === "string" ? [{ type: "text", text: opts.initialPrompt }] : opts.initialPrompt : void 0;
12017
12099
  let remoteSessionId;
12018
12100
  let resolvedCwd = boxCwd;
12019
12101
  try {
12020
- const remoteDesc = await host.start({
12102
+ const startArgs = {
12021
12103
  adapter: opts.adapter,
12022
12104
  ...omitCwd ? {} : { cwd: boxCwd },
12023
12105
  // Issue #1647 — DEVICE-BRIDGE ONLY: forward an EXPLICIT
@@ -12035,13 +12117,24 @@ async function bootSandboxAgentSession(opts) {
12035
12117
  ...opts.route ? { route: opts.route } : {},
12036
12118
  ...opts.effort ? { effort: opts.effort } : {},
12037
12119
  ...opts.label ? { label: opts.label } : {},
12038
- ...opts.auth ? { auth: opts.auth } : {}
12039
- });
12120
+ ...opts.auth ? { auth: opts.auth } : {},
12121
+ // F2 — the initial prompt rides the SAME dial as the spawn (device only).
12122
+ ...carriedPrompt !== void 0 ? { prompt: carriedPrompt } : {}
12123
+ };
12124
+ const remoteDesc = deviceTarget !== void 0 ? await startDeviceSessionWithRetry(host, startArgs, deviceTarget) : await host.start(startArgs);
12040
12125
  remoteSessionId = remoteDesc.id;
12041
12126
  if (omitCwd && remoteDesc.cwd) resolvedCwd = remoteDesc.cwd;
12042
12127
  } catch (err) {
12043
12128
  await host.stop().catch(() => void 0);
12044
12129
  recordSandboxState(host.sandboxId, "stopped");
12130
+ if (err instanceof DeviceSpawnUnreachableError) {
12131
+ return {
12132
+ ok: false,
12133
+ code: "device_spawn_unreachable",
12134
+ message: err.message,
12135
+ details: { target: err.target, attempts: err.attempts }
12136
+ };
12137
+ }
12045
12138
  return {
12046
12139
  ok: false,
12047
12140
  code: "sandbox_proxy_failed",
@@ -12068,7 +12161,13 @@ async function bootSandboxAgentSession(opts) {
12068
12161
  host,
12069
12162
  remoteSessionId,
12070
12163
  lifecyclePolicy,
12071
- ledger: { sandboxId: host.sandboxId, provider: providerSlug }
12164
+ ledger: { sandboxId: host.sandboxId, provider: providerSlug },
12165
+ // Device spawns only: surface a first-turn failure's raw host line
12166
+ // (see `surfaceHostTurnErrors`).
12167
+ ...deviceTarget !== void 0 ? { surfaceHostTurnErrors: true } : {},
12168
+ // F2 — when the first prompt rode the spawn dial, don't re-send it as a
12169
+ // second `agent_prompt` dial (see `promptCarriedInStart`).
12170
+ ...carriedPrompt !== void 0 ? { promptCarriedInStart: true } : {}
12072
12171
  }),
12073
12172
  commandPreview: `sandbox:${providerSlug} \u2192 ${opts.adapter}`,
12074
12173
  sandboxId: host.sandboxId,
@@ -12148,7 +12247,7 @@ async function resolveEffectiveCommandSandbox(requested, cwd) {
12148
12247
  return void 0;
12149
12248
  }
12150
12249
  }
12151
- var SPAWN_CLAIM_WINDOW_MS, IMPLICIT_SPAWN_CLAIM_WINDOW_MS, MAX_RESOLVED_CLAIMS, spawnClaimsByRegistry, HOST_ONLY_PATH_PATTERNS;
12250
+ var SPAWN_CLAIM_WINDOW_MS, IMPLICIT_SPAWN_CLAIM_WINDOW_MS, MAX_RESOLVED_CLAIMS, spawnClaimsByRegistry, HOST_ONLY_PATH_PATTERNS, DEVICE_TRANSPORT_ERROR_RE, DEVICE_SPAWN_RETRY_DELAY_MS, DeviceSpawnUnreachableError;
12152
12251
  var init_session_spawn = __esm({
12153
12252
  "src/session-spawn.ts"() {
12154
12253
  init_process_stats();
@@ -12161,6 +12260,7 @@ var init_session_spawn = __esm({
12161
12260
  init_bundles();
12162
12261
  init_mcp_imports();
12163
12262
  init_launch_config();
12263
+ init_eligibility_manifest();
12164
12264
  init_claude_code_oauth_source();
12165
12265
  init_providers_store();
12166
12266
  init_catalog_models();
@@ -12197,6 +12297,21 @@ var init_session_spawn = __esm({
12197
12297
  /^\/Users\//,
12198
12298
  /^[A-Za-z]:[\\/]/
12199
12299
  ];
12300
+ DEVICE_TRANSPORT_ERROR_RE = /device_unreachable|transport closed|handshake timed out|did not send hello|closed the channel after handshake|socket hang up|other side closed|ECONNRESET|ECONNREFUSED|EPIPE|ETIMEDOUT|fetch failed/i;
12301
+ DEVICE_SPAWN_RETRY_DELAY_MS = 2e3;
12302
+ DeviceSpawnUnreachableError = class extends Error {
12303
+ target;
12304
+ attempts;
12305
+ constructor(target, attempts, cause) {
12306
+ const detail = cause instanceof Error ? cause.message : String(cause);
12307
+ super(
12308
+ `agent_start: could not reach paired device "${target}" over its host channel after ${attempts} attempt${attempts === 1 ? "" : "s"} \u2014 the host channel may be flapping; retry in a few seconds and check \`agentproto devices status ${target}\`. Last error: ${detail}`
12309
+ );
12310
+ this.name = "DeviceSpawnUnreachableError";
12311
+ this.target = target;
12312
+ this.attempts = attempts;
12313
+ }
12314
+ };
12200
12315
  }
12201
12316
  });
12202
12317
  function tryParseJson(value) {
@@ -13667,7 +13782,10 @@ function registerAgentTools(server, opts) {
13667
13782
  ...provisionWorktree ? { provisionWorktree } : {},
13668
13783
  ...resolveWorktreeIsolation ? { resolveWorktreeIsolation } : {},
13669
13784
  ...listCatalogModels ? { listCatalogModels } : {},
13670
- ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {}
13785
+ ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {},
13786
+ // Lets the generic `adapter_not_found` error name the installed
13787
+ // adapters (session-spawn.ts) instead of only saying "install it".
13788
+ ...listAgentAdapters ? { listAgentAdapters } : {}
13671
13789
  },
13672
13790
  {
13673
13791
  ...spawnInput,
@@ -13973,6 +14091,11 @@ function registerAgentTools(server, opts) {
13973
14091
  // Surfaced so a caller can distinguish "idle" from "mid tool
13974
14092
  // call" without guessing from empty output.
13975
14093
  ...desc.blockedOn ? { blockedOn: desc.blockedOn } : {},
14094
+ // Device-spawn first-turn failure (bad model id / host-side
14095
+ // adapter error) — see `SessionDescriptor.firstTurnFailed`.
14096
+ // The underlying host error line rides the `lines` tail above
14097
+ // (surfaced as a `notice` by the sandbox proxy).
14098
+ ...desc.firstTurnFailed ? { firstTurnFailed: true } : {},
13976
14099
  ...activityFallback ? { activityFallback: true } : {},
13977
14100
  // An ended session's derived outcome (what it produced) —
13978
14101
  // the ring above is empty for a row reloaded after a restart.
@@ -14209,6 +14332,7 @@ function registerAgentTools(server, opts) {
14209
14332
  ...s.lastTurnErrorMessage !== void 0 ? { lastTurnErrorMessage: s.lastTurnErrorMessage } : {},
14210
14333
  ...s.lastTurnReason !== void 0 ? { lastTurnReason: s.lastTurnReason } : {},
14211
14334
  ...s.lastTurnEmpty !== void 0 ? { lastTurnEmpty: s.lastTurnEmpty } : {},
14335
+ ...s.firstTurnFailed ? { firstTurnFailed: true } : {},
14212
14336
  ...s.stats !== void 0 ? { stats: s.stats } : {}
14213
14337
  });
14214
14338
  const agentSessionsListSchema = z.object({
@@ -16529,6 +16653,267 @@ var init_session_end_reason = __esm({
16529
16653
  PROVIDER_LIMIT_ERROR_RE = /\bhit your (?:session|usage) limit\b/i;
16530
16654
  }
16531
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
+ });
16532
16917
  function isBackgroundToolCallArguments(args) {
16533
16918
  return typeof args === "object" && args !== null && args.run_in_background === true;
16534
16919
  }
@@ -16708,6 +17093,7 @@ function toSessionSummary(desc) {
16708
17093
  blockedOn: desc.blockedOn,
16709
17094
  stalledSinceMs: desc.stalledSinceMs,
16710
17095
  pendingBgTasks: desc.pendingBgTasks,
17096
+ ...desc.wrapupFlag ? { wrapupFlag: desc.wrapupFlag } : {},
16711
17097
  backgroundTasks: desc.backgroundTasks,
16712
17098
  lastTurnErroredAt: desc.lastTurnErroredAt,
16713
17099
  ...desc.lastError ? { lastError: desc.lastError.slice(0, 2e3) } : {},
@@ -16836,6 +17222,18 @@ function stampInterrupted(desc) {
16836
17222
  delete desc.interrupted;
16837
17223
  }
16838
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
+ }
16839
17237
  function stampLiveModes(desc, rt) {
16840
17238
  const modes = rt.agentSession?.availableModes;
16841
17239
  if (modes && modes.length > 0) {
@@ -16847,6 +17245,9 @@ function stampLiveModes(desc, rt) {
16847
17245
  function isResumable(desc) {
16848
17246
  return desc.kind === "agent-cli" && !!desc.adapterSlug && !!desc.adapterSessionId && !!desc.cwd && !desc.archived;
16849
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
+ }
16850
17251
  function canResume(desc) {
16851
17252
  return isResumable(desc) && (desc.resumeAttempts ?? 0) < MAX_RESUME_ATTEMPTS;
16852
17253
  }
@@ -16872,18 +17273,18 @@ function worktreeFields(cwd) {
16872
17273
  ...identity.mainRepoPath === void 0 ? {} : { mainRepoPath: identity.mainRepoPath }
16873
17274
  };
16874
17275
  }
16875
- function stripAnsiCodes(s) {
17276
+ function stripAnsiCodes2(s) {
16876
17277
  return s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, "");
16877
17278
  }
16878
17279
  function terminalOutputTail(rt, maxChars = 500) {
16879
17280
  if (rt.recentBytes.length === 0) return "";
16880
17281
  const text13 = Buffer.concat(rt.recentBytes, rt.recentBytesSize).toString("utf8");
16881
- const plain = stripAnsiCodes(text13).replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1b[()][A-Za-z0-9]/g, "").replace(/\x1b[0-9A-Za-z=>]/g, "").replace(/[\x00-\x09\x0b-\x1f]/g, "").trim();
17282
+ const plain = stripAnsiCodes2(text13).replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "").replace(/\x1b[()][A-Za-z0-9]/g, "").replace(/\x1b[0-9A-Za-z=>]/g, "").replace(/[\x00-\x09\x0b-\x1f]/g, "").trim();
16882
17283
  if (!plain) return "";
16883
17284
  return plain.length > maxChars ? plain.slice(-maxChars) : plain;
16884
17285
  }
16885
17286
  function deriveHeuristicQuestion(recentLines) {
16886
- const visible = recentLines.map(stripAnsiCodes).map((l) => l.trim()).filter((l) => l.length > 0 && !SYNTHETIC_LINE.test(l));
17287
+ const visible = recentLines.map(stripAnsiCodes2).map((l) => l.trim()).filter((l) => l.length > 0 && !SYNTHETIC_LINE.test(l));
16887
17288
  if (visible.length === 0) return void 0;
16888
17289
  const options = [];
16889
17290
  let i = visible.length - 1;
@@ -17085,6 +17486,7 @@ function createSessionsRegistry(opts) {
17085
17486
  }
17086
17487
  }
17087
17488
  recordOutcome(rt);
17489
+ writeIndexNow(rt);
17088
17490
  rt.linkProbeStop?.();
17089
17491
  cancelPendingPermissionsForSession(rt);
17090
17492
  if (runWorktreeAutoReclaim && rt.desc.worktreeAutoProvisioned && rt.desc.worktreePath) {
@@ -17319,6 +17721,53 @@ function createSessionsRegistry(opts) {
17319
17721
  void persistSnapshot();
17320
17722
  }, PERSIST_DEBOUNCE_MS2);
17321
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
+ };
17322
17771
  const conversationLinkProbeMs = opts?.conversationLinkProbeMs ?? {
17323
17772
  initialMs: 3e3,
17324
17773
  intervalMs: 15e3
@@ -17481,7 +17930,7 @@ function createSessionsRegistry(opts) {
17481
17930
  const strategySlug = rt.desc.adapterSlug ?? conversationTerminalSlugFor(rt.desc);
17482
17931
  const strategy = strategySlug ? RESUME_STRATEGIES[strategySlug] : void 0;
17483
17932
  if (!strategy?.outputHint) return;
17484
- const plain = stripAnsiCodes(line);
17933
+ const plain = stripAnsiCodes2(line);
17485
17934
  const m = plain.match(strategy.outputHint);
17486
17935
  if (m && m[1]) {
17487
17936
  rt.desc.resumeMetadata = {
@@ -17719,6 +18168,14 @@ function createSessionsRegistry(opts) {
17719
18168
  }
17720
18169
  break;
17721
18170
  }
18171
+ case "notice": {
18172
+ if (evt.text) {
18173
+ for (const line of evt.text.split(/\r?\n/)) {
18174
+ if (line.trim()) appendLine(rt, `\x1B[2m[notice] ${line}\x1B[0m`, "stderr");
18175
+ }
18176
+ }
18177
+ break;
18178
+ }
17722
18179
  case "plan": {
17723
18180
  const entries = evt.entries ?? [];
17724
18181
  const done = entries.filter((e) => e.status === "completed").length;
@@ -17913,7 +18370,12 @@ function createSessionsRegistry(opts) {
17913
18370
  schedulePersist();
17914
18371
  };
17915
18372
  const maybeResumeAgent = async (rt, resumedFrom = "daemon-restart") => {
17916
- 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
+ }
17917
18379
  if (!resumeAgent) return;
17918
18380
  if (!isResumable(rt.desc)) return;
17919
18381
  if (!canResume(rt.desc)) {
@@ -17959,7 +18421,7 @@ function createSessionsRegistry(opts) {
17959
18421
  recordFailedResume(rt);
17960
18422
  return;
17961
18423
  }
17962
- rt.agentSession = fresh;
18424
+ bindAgentSession(rt, fresh);
17963
18425
  bindOutOfTurnEvents(rt);
17964
18426
  stampCapabilities(rt);
17965
18427
  rt.adapterSlug = adapterSlug;
@@ -18016,6 +18478,48 @@ function createSessionsRegistry(opts) {
18016
18478
  rt.resumePromise = void 0;
18017
18479
  }
18018
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
+ };
18019
18523
  const buildUsageSnapshot = (rt) => deriveSessionUsage({
18020
18524
  ...rt.desc.model !== void 0 ? { model: rt.desc.model } : {},
18021
18525
  ...rt.adapterReportedCost && rt.desc.costUsd !== void 0 ? { adapterCostUsd: rt.desc.costUsd } : {},
@@ -18475,6 +18979,7 @@ ${message}`;
18475
18979
  ...messageTurn ? { messages: turnOpts.messages } : {}
18476
18980
  } : void 0
18477
18981
  );
18982
+ rt.lastUserPrompt = { ts: (/* @__PURE__ */ new Date()).toISOString(), text: promptTextForIndex(message, turnOpts?.system) };
18478
18983
  const wrapped = typeof message === "string" ? { type: "text", text: message } : message;
18479
18984
  const switchCandidate = typeof message === "string" ? parseModelSwitchCommand(message) : void 0;
18480
18985
  let switchLearned = false;
@@ -18638,6 +19143,10 @@ ${message}`;
18638
19143
  } else if (rt.desc.lastTurnReason !== void 0) {
18639
19144
  delete rt.desc.lastTurnReason;
18640
19145
  }
19146
+ if (rt.desc.remote === true && rt.desc.sandboxProvider?.startsWith("device:") && rt.desc.turnsCompleted === 1 && (emptyTurn || turnEndReason === "error")) {
19147
+ rt.desc.firstTurnFailed = true;
19148
+ schedulePersist();
19149
+ }
18641
19150
  const nextActivity = regenerateActivitySummary(rt.desc, rt.recentLines, Date.now());
18642
19151
  if (nextActivity) {
18643
19152
  rt.desc.activitySummary = nextActivity;
@@ -18692,6 +19201,10 @@ ${message}`;
18692
19201
  } else if (rt.desc.lastTurnReason !== void 0) {
18693
19202
  delete rt.desc.lastTurnReason;
18694
19203
  }
19204
+ if (rt.desc.remote === true && rt.desc.sandboxProvider?.startsWith("device:") && rt.desc.turnsCompleted === 1 && turnEndReason === "error") {
19205
+ rt.desc.firstTurnFailed = true;
19206
+ schedulePersist();
19207
+ }
18695
19208
  if (sessionEvents) {
18696
19209
  sessionEvents.emit({
18697
19210
  type: "session:turn-end",
@@ -18704,6 +19217,7 @@ ${message}`;
18704
19217
  });
18705
19218
  }
18706
19219
  }
19220
+ scheduleIndexWrite(rt);
18707
19221
  if (!interruptedBy) dispatchQueuedPrompt(rt);
18708
19222
  else if (interruptedBy.deliverQueueId) {
18709
19223
  dispatchQueuedPrompt(rt, interruptedBy.deliverQueueId);
@@ -18998,6 +19512,10 @@ ${message}`;
18998
19512
  };
18999
19513
  const registry = {
19000
19514
  bootId,
19515
+ transcriptBaseDir,
19516
+ flushSessionIndexes() {
19517
+ flushDirtyIndexes();
19518
+ },
19001
19519
  recordAutoContinue(id) {
19002
19520
  const rt = sessions.get(id);
19003
19521
  if (!rt) return;
@@ -19074,6 +19592,7 @@ ${message}`;
19074
19592
  emitExited(rt);
19075
19593
  });
19076
19594
  schedulePersist();
19595
+ writeIndexNow(rt);
19077
19596
  return desc;
19078
19597
  },
19079
19598
  register(input) {
@@ -19123,6 +19642,7 @@ ${message}`;
19123
19642
  emitExited(rt);
19124
19643
  });
19125
19644
  schedulePersist();
19645
+ writeIndexNow(rt);
19126
19646
  return desc;
19127
19647
  },
19128
19648
  spawnAgent(input) {
@@ -19224,7 +19744,6 @@ ${message}`;
19224
19744
  }
19225
19745
  const rt = {
19226
19746
  desc,
19227
- agentSession: input.agentSession,
19228
19747
  adapterSlug: input.adapterSlug,
19229
19748
  recentLines: [],
19230
19749
  recentBytes: [],
@@ -19241,6 +19760,7 @@ ${message}`;
19241
19760
  rt.emitter.setMaxListeners(50);
19242
19761
  sessions.set(id, rt);
19243
19762
  linkContinuedTo(input.resumedFrom, id);
19763
+ bindAgentSession(rt, input.agentSession);
19244
19764
  bindOutOfTurnEvents(rt);
19245
19765
  stampCapabilities(rt);
19246
19766
  armUsageRefresh(rt);
@@ -19258,6 +19778,7 @@ ${message}`;
19258
19778
  "stdout"
19259
19779
  );
19260
19780
  schedulePersist();
19781
+ writeIndexNow(rt);
19261
19782
  recordConversationLink(rt);
19262
19783
  desc.eventsPath = sessionEventsPath(desc.id, transcriptBaseDir);
19263
19784
  if (input.initialPrompt) {
@@ -19376,6 +19897,7 @@ ${message}`;
19376
19897
  "stdout"
19377
19898
  );
19378
19899
  schedulePersist();
19900
+ writeIndexNow(rt);
19379
19901
  desc.eventsPath = sessionEventsPath(desc.id, transcriptBaseDir);
19380
19902
  return desc;
19381
19903
  },
@@ -19431,7 +19953,7 @@ ${message}`;
19431
19953
  emitExited(rt);
19432
19954
  return;
19433
19955
  }
19434
- rt.agentSession = outcome.agentSession;
19956
+ bindAgentSession(rt, outcome.agentSession);
19435
19957
  bindOutOfTurnEvents(rt);
19436
19958
  stampCapabilities(rt);
19437
19959
  rt.readUsage = outcome.readUsage;
@@ -19453,6 +19975,7 @@ ${message}`;
19453
19975
  "stdout"
19454
19976
  );
19455
19977
  schedulePersist();
19978
+ writeIndexNow(rt);
19456
19979
  recordConversationLink(rt);
19457
19980
  if (outcome.initialPrompt) {
19458
19981
  void runAgentTurn(
@@ -19622,6 +20145,7 @@ ${message}`;
19622
20145
  emitExited(rt);
19623
20146
  });
19624
20147
  schedulePersist();
20148
+ writeIndexNow(rt);
19625
20149
  return desc;
19626
20150
  },
19627
20151
  recordCommand(input) {
@@ -20246,6 +20770,7 @@ ${message}`;
20246
20770
  stampCurrentStatus(rt);
20247
20771
  stampWatchers(desc);
20248
20772
  stampLiveModes(desc, rt);
20773
+ stampAdapterConnected(desc, rt);
20249
20774
  desc.childrenBusy = childrenBusy.get(desc.id) ?? 0;
20250
20775
  desc.queuedPrompts = desc.promptQueue?.length ?? 0;
20251
20776
  return desc;
@@ -20303,6 +20828,7 @@ ${message}`;
20303
20828
  stampCurrentStatus(rt);
20304
20829
  stampWatchers(desc);
20305
20830
  stampLiveModes(desc, rt);
20831
+ stampAdapterConnected(desc, rt);
20306
20832
  desc.childrenBusy = childrenBusyCounts().get(desc.id) ?? 0;
20307
20833
  desc.queuedPrompts = desc.promptQueue?.length ?? 0;
20308
20834
  }
@@ -20558,34 +21084,8 @@ ${message}`;
20558
21084
  emitExited(rt);
20559
21085
  return true;
20560
21086
  },
20561
- markCrashed(id) {
20562
- const rt = sessions.get(id);
20563
- if (!rt) return false;
20564
- if (rt.desc.kind !== "agent-cli" || rt.desc.status !== "running") {
20565
- return false;
20566
- }
20567
- const pid = rt.desc.pid;
20568
- const providerLimitMessage = isProviderLimitError(rt.lastErrorMessage) ? rt.lastErrorMessage : void 0;
20569
- rt.desc.status = "error";
20570
- rt.desc.endedAt = (/* @__PURE__ */ new Date()).toISOString();
20571
- rt.desc.endedReason = providerLimitMessage ? "provider-limit" : "crashed";
20572
- rt.desc.crashedAt = rt.desc.endedAt;
20573
- rt.desc.lastError = providerLimitMessage ? `provider usage limit hit: ${providerLimitMessage}` : `adapter process gone (pid ${pid}) \u2014 session crashed`;
20574
- if (rt.agentSession) {
20575
- releaseOutOfTurnEvents(rt);
20576
- delete rt.desc.backgroundTasks;
20577
- recordExitUsageSnapshot(rt);
20578
- void rt.agentSession.close().catch(() => void 0);
20579
- void transcriptWriter.close(rt.desc.id);
20580
- tracedSessions.delete(rt.desc.id);
20581
- rt.agentSession = void 0;
20582
- }
20583
- schedulePersist();
20584
- const banner = providerLimitMessage ? `[provider-limit] ${providerLimitMessage}` : `[crashed] adapter process gone (pid ${pid}) \u2014 session crashed`;
20585
- appendLine(rt, banner, "stderr");
20586
- transcriptWriter.recordEvent(rt.desc.id, { kind: "notice", text: banner });
20587
- emitExited(rt);
20588
- return true;
21087
+ markCrashed(id, reason = "process-gone") {
21088
+ return markCrashedInternal(id, reason);
20589
21089
  },
20590
21090
  markStalled(id, stalledSinceMs) {
20591
21091
  const rt = sessions.get(id);
@@ -20751,6 +21251,7 @@ ${message}`;
20751
21251
  ts: (/* @__PURE__ */ new Date()).toISOString()
20752
21252
  });
20753
21253
  stampReadLiveness(rt.desc);
21254
+ writeIndexNow(rt);
20754
21255
  return rt.desc;
20755
21256
  },
20756
21257
  setKeepAlive(id, keepAlive) {
@@ -20874,6 +21375,7 @@ ${message}`;
20874
21375
  if (shutdownDone) return;
20875
21376
  shutdownDone = true;
20876
21377
  if (persistTimer) clearTimeout(persistTimer);
21378
+ flushDirtyIndexes();
20877
21379
  if (persist) {
20878
21380
  try {
20879
21381
  process.off("exit", onProcessExit);
@@ -21026,6 +21528,13 @@ function loadHistorySnapshot(persistPath, sessions, sessionEvents, transcriptBas
21026
21528
  }
21027
21529
  }
21028
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
+ }
21029
21538
  function quoteArg(arg) {
21030
21539
  if (arg === "") return '""';
21031
21540
  if (/^[a-zA-Z0-9._/=:@,+-]+$/.test(arg)) return arg;
@@ -21065,6 +21574,7 @@ var init_sessions = __esm({
21065
21574
  init_session_continue_fresh();
21066
21575
  init_session_outcome();
21067
21576
  init_session_end_reason();
21577
+ init_session_index();
21068
21578
  init_session_message();
21069
21579
  SESSIONS_FILE_PATH = () => resolve(homedir(), ".agentproto", "sessions.json");
21070
21580
  SESSION_ID_ENV = "AGENTPROTO_SESSION_ID";
@@ -22812,7 +23322,7 @@ init_resume_strategies();
22812
23322
  init_spawn_defaults();
22813
23323
  init_launch_config();
22814
23324
  init_resume_context_digest();
22815
- init_session_spawn();
23325
+ init_eligibility_manifest();
22816
23326
  init_providers_store();
22817
23327
  init_catalog_models();
22818
23328
  init_config();
@@ -23310,7 +23820,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
23310
23820
  desc = await spawnWithResume(strategy.resumeSessionId);
23311
23821
  } catch (err) {
23312
23822
  const msg = err instanceof Error ? err.message : String(err);
23313
- 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))) {
23314
23824
  desc = await spawnWithResume(void 0);
23315
23825
  resumeFallback = true;
23316
23826
  } else {
@@ -23601,7 +24111,7 @@ var EMPTY_SKILLS = {
23601
24111
  byHarness: [],
23602
24112
  defaults: { global: [], byAdapter: {} }
23603
24113
  };
23604
- function isRecord5(value) {
24114
+ function isRecord6(value) {
23605
24115
  return typeof value === "object" && value !== null && !Array.isArray(value);
23606
24116
  }
23607
24117
  function classifyDaemonMount(adapter) {
@@ -23754,15 +24264,15 @@ var slugToCamel2 = (slug) => slug.replace(/-([a-z0-9])/g, (_m, c) => c.toUpperCa
23754
24264
  async function readAdapterManifest(adapter) {
23755
24265
  try {
23756
24266
  const mod = await import(adapter.packageName);
23757
- if (!isRecord5(mod)) return void 0;
24267
+ if (!isRecord6(mod)) return void 0;
23758
24268
  const candidate = mod[slugToCamel2(adapter.slug)] ?? mod.default ?? mod.handle;
23759
- return isRecord5(candidate) ? candidate : void 0;
24269
+ return isRecord6(candidate) ? candidate : void 0;
23760
24270
  } catch {
23761
24271
  return void 0;
23762
24272
  }
23763
24273
  }
23764
24274
  function asSkillsTarget(value) {
23765
- if (!isRecord5(value) || typeof value.format !== "string") return void 0;
24275
+ if (!isRecord6(value) || typeof value.format !== "string") return void 0;
23766
24276
  const out = { format: value.format };
23767
24277
  if (typeof value.dir === "string") out.dir = value.dir;
23768
24278
  if (typeof value.outDir === "string") out.outDir = value.outDir;
@@ -23779,10 +24289,10 @@ async function listNativeSkills(adapter) {
23779
24289
  }
23780
24290
  async function buildHarnessEntry(adapter) {
23781
24291
  const manifest = await readAdapterManifest(adapter);
23782
- const metadata = manifest && isRecord5(manifest.metadata) ? manifest.metadata : void 0;
24292
+ const metadata = manifest && isRecord6(manifest.metadata) ? manifest.metadata : void 0;
23783
24293
  const target = asSkillsTarget(metadata?.skills);
23784
24294
  const options = manifest && Array.isArray(manifest.options) ? manifest.options : [];
23785
- const spawnOption = options.some((o) => isRecord5(o) && o.id === "skills");
24295
+ const spawnOption = options.some((o) => isRecord6(o) && o.id === "skills");
23786
24296
  const [installed, native] = await Promise.all([
23787
24297
  target ? listInstalledForTarget(target) : Promise.resolve([]),
23788
24298
  listNativeSkills(adapter)
@@ -24168,6 +24678,8 @@ async function processTreeRss(rootPids, exec = defaultPsExecutor) {
24168
24678
  }
24169
24679
 
24170
24680
  // src/session-tools.ts
24681
+ init_transcript_writer();
24682
+ init_session_index();
24171
24683
  init_process_stats();
24172
24684
 
24173
24685
  // src/host-load.ts
@@ -25961,6 +26473,7 @@ var publicSessionDescriptor = (session) => {
25961
26473
  var compactSessionItem = (s) => ({
25962
26474
  id: s.id,
25963
26475
  kind: s.kind,
26476
+ ...s.origin ? { origin: s.origin } : {},
25964
26477
  name: s.name,
25965
26478
  label: s.label,
25966
26479
  status: s.status,
@@ -25995,6 +26508,7 @@ var compactSessionItem = (s) => ({
25995
26508
  ...s.lastTurnErrorMessage !== void 0 ? { lastTurnErrorMessage: s.lastTurnErrorMessage } : {},
25996
26509
  ...s.lastTurnReason !== void 0 ? { lastTurnReason: s.lastTurnReason } : {},
25997
26510
  ...s.lastTurnEmpty !== void 0 ? { lastTurnEmpty: s.lastTurnEmpty } : {},
26511
+ ...s.firstTurnFailed ? { firstTurnFailed: true } : {},
25998
26512
  ...s.interrupted ? { interrupted: true } : {},
25999
26513
  ...s.rssBytes !== void 0 ? { rssBytes: s.rssBytes } : {},
26000
26514
  ...s.stats !== void 0 ? { stats: s.stats } : {},
@@ -26248,6 +26762,66 @@ function registerSessionTools(rawServer, opts) {
26248
26762
  })
26249
26763
  ]
26250
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
+ });
26251
26825
  registerBuiltinTool(server, {
26252
26826
  id: "session_stats",
26253
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).',
@@ -28495,8 +29069,8 @@ function registerSessionTools(rawServer, opts) {
28495
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.',
28496
29070
  {
28497
29071
  sessionIds: z.array(z.string().min(1)).min(1).describe("Session ids or names, from `session_wrapup_plan`."),
28498
- verdict: z.enum(["done", "abandoned", "blocked", "needs-input"]).describe(
28499
- '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.'
28500
29074
  ),
28501
29075
  note: z.string().optional().describe("Free-text note recorded on the outcome or the flag."),
28502
29076
  judgedBy: z.string().optional().describe(
@@ -28571,6 +29145,70 @@ function registerSessionTools(rawServer, opts) {
28571
29145
  }
28572
29146
  }
28573
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
+ );
28574
29212
  server.tool(
28575
29213
  "session_evidence",
28576
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.",
@@ -31672,12 +32310,18 @@ function registerMcpApps(server, apps) {
31672
32310
  uri: resourceUri,
31673
32311
  html,
31674
32312
  description: app.description,
31675
- csp: app.csp
32313
+ csp: app.csp,
32314
+ ...app.openai?.resource !== void 0 ? {
32315
+ // Namespaced under `openai/ui` per §3.2 — a sibling of the
32316
+ // canonical `_meta.ui`, never an overwrite of it.
32317
+ meta: { "openai/ui": JSON.parse(JSON.stringify(app.openai.resource)) }
32318
+ } : {}
31676
32319
  });
31677
32320
  server.registerTool(
31678
32321
  app.id,
31679
32322
  {
31680
32323
  description: app.description ?? app.title,
32324
+ ...app.openai !== void 0 ? { title: app.title } : {},
31681
32325
  inputSchema: app.inputSchema.shape,
31682
32326
  annotations: {
31683
32327
  readOnlyHint: true,
@@ -31687,7 +32331,13 @@ function registerMcpApps(server, apps) {
31687
32331
  ui: {
31688
32332
  resourceUri,
31689
32333
  visibility: ["model", "app"]
31690
- }
32334
+ },
32335
+ ...app.openai?.tool?.entrypoints !== void 0 || app.openai?.icons !== void 0 ? {
32336
+ "openai/ui": {
32337
+ ...app.openai.tool?.entrypoints !== void 0 ? { entrypoints: JSON.parse(JSON.stringify(app.openai.tool.entrypoints)) } : {},
32338
+ ...app.openai.icons !== void 0 ? { icons: JSON.parse(JSON.stringify(app.openai.icons)) } : {}
32339
+ }
32340
+ } : {}
31691
32341
  }
31692
32342
  },
31693
32343
  async (args) => {
@@ -34786,11 +35436,11 @@ function verifySignatureV2(secret, headers, body, nowMs, skewMs = SIGNATURE_V2_S
34786
35436
  if (a.length !== b.length || !timingSafeEqual(a, b)) return { ok: false, reason: "signature_mismatch" };
34787
35437
  return { ok: true, timestampSec };
34788
35438
  }
34789
- var isRecord6 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
35439
+ var isRecord7 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
34790
35440
  function toSentinelEvent(raw, seq) {
34791
- if (!isRecord6(raw)) return null;
35441
+ if (!isRecord7(raw)) return null;
34792
35442
  const { id, source, type, subject, time, data, summary, subjects, terminal, consumerref } = raw;
34793
- 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") {
34794
35444
  return null;
34795
35445
  }
34796
35446
  const envSeq = typeof raw.seq === "number" ? raw.seq : seq;
@@ -34896,7 +35546,7 @@ function agentpushSentinelProvider(opts = {}) {
34896
35546
  json3 = void 0;
34897
35547
  }
34898
35548
  if (!res.ok) {
34899
- 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);
34900
35550
  throw new AgentpushHttpError(res.status, `agentpush ${method} ${path} \u2192 HTTP ${res.status}${detail ? `: ${detail}` : ""}`);
34901
35551
  }
34902
35552
  return json3;
@@ -34982,7 +35632,7 @@ function agentpushSentinelProvider(opts = {}) {
34982
35632
  ...push ? { callback_url: callbackUrl2(delivery.callbackUrl, hookKey), callback_secret: callbackSecret } : {}
34983
35633
  }
34984
35634
  });
34985
- 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;
34986
35636
  if (typeof id !== "string" || id === "") throw new Error("agentpush: create returned no subscription id");
34987
35637
  return {
34988
35638
  provider: AGENTPUSH_SLUG,
@@ -35009,7 +35659,7 @@ function agentpushSentinelProvider(opts = {}) {
35009
35659
  let sub;
35010
35660
  try {
35011
35661
  const json3 = await api("GET", subPath(handle));
35012
- sub = isRecord6(json3) && isRecord6(json3.subscription) ? json3.subscription : void 0;
35662
+ sub = isRecord7(json3) && isRecord7(json3.subscription) ? json3.subscription : void 0;
35013
35663
  } catch (err) {
35014
35664
  if (isHttp(err, 404)) {
35015
35665
  throw new Error(`agentpush subscription ${handle.remoteId} no longer exists \u2014 re-create the sentinel`);
@@ -35040,7 +35690,7 @@ function agentpushSentinelProvider(opts = {}) {
35040
35690
  async status(handle) {
35041
35691
  try {
35042
35692
  const json3 = await api("GET", subPath(handle));
35043
- const sub = isRecord6(json3) && isRecord6(json3.subscription) ? json3.subscription : void 0;
35693
+ const sub = isRecord7(json3) && isRecord7(json3.subscription) ? json3.subscription : void 0;
35044
35694
  const status = sub && typeof sub.status === "string" ? sub.status : "unknown";
35045
35695
  if (status === "active") return { ok: true };
35046
35696
  return { ok: false, detail: `the agentpush subscription is ${status}` };
@@ -35052,12 +35702,12 @@ function agentpushSentinelProvider(opts = {}) {
35052
35702
  const after = handle.cursor ?? "0";
35053
35703
  if (stateOf(handle).mode === "push") return { events: [], cursor: after };
35054
35704
  const json3 = await api("GET", `${subPath(handle)}/events`, { query: { after, limit: Math.max(1, Math.min(limit, 1e3)) } });
35055
- const items = isRecord6(json3) && Array.isArray(json3.items) ? json3.items : [];
35705
+ const items = isRecord7(json3) && Array.isArray(json3.items) ? json3.items : [];
35056
35706
  const events = [];
35057
35707
  let cursor = Number(after);
35058
35708
  if (!Number.isFinite(cursor)) cursor = 0;
35059
35709
  for (const item of items) {
35060
- if (!isRecord6(item)) continue;
35710
+ if (!isRecord7(item)) continue;
35061
35711
  const seq = typeof item.seq === "number" ? item.seq : void 0;
35062
35712
  if (seq !== void 0 && seq > cursor) cursor = seq;
35063
35713
  const event = toSentinelEvent(item.envelope, seq);
@@ -37433,11 +38083,11 @@ function sanitize(cacheKey) {
37433
38083
  const cleaned = cacheKey.replace(/[^a-zA-Z0-9._-]/g, "_");
37434
38084
  return cleaned.length > 0 ? cleaned : "default";
37435
38085
  }
37436
- function isRecord7(v) {
38086
+ function isRecord8(v) {
37437
38087
  return typeof v === "object" && v !== null && !Array.isArray(v);
37438
38088
  }
37439
38089
  function isEntry2(v) {
37440
- return isRecord7(v) && typeof v.resolvedInputHash === "string" && "output" in v;
38090
+ return isRecord8(v) && typeof v.resolvedInputHash === "string" && "output" in v;
37441
38091
  }
37442
38092
  function createFileStepCache(cacheKey, opts) {
37443
38093
  const dir = opts?.dir ?? DEFAULT_CACHE_DIR();
@@ -37450,7 +38100,7 @@ function createFileStepCache(cacheKey, opts) {
37450
38100
  } catch {
37451
38101
  return {};
37452
38102
  }
37453
- if (!isRecord7(parsed)) return {};
38103
+ if (!isRecord8(parsed)) return {};
37454
38104
  const out = {};
37455
38105
  for (const [k, v] of Object.entries(parsed)) if (isEntry2(v)) out[k] = v;
37456
38106
  return out;
@@ -43534,7 +44184,15 @@ async function performInstall(dir, appRegistry, listRegisteredToolIds, resolveAg
43534
44184
  ...handle.ui?.description !== void 0 ? { description: handle.ui.description } : {},
43535
44185
  ...handle.ui?.tools !== void 0 ? { tools: handle.ui.tools } : {},
43536
44186
  ...handle.ui?.csp !== void 0 ? { csp: handle.ui.csp } : {},
43537
- ...handle.ui?.build !== void 0 ? { build: handle.ui.build } : {}
44187
+ ...handle.ui?.build !== void 0 ? { build: handle.ui.build } : {},
44188
+ // OpenAI MCP-extensions carrier (plan W-B): the normalized,
44189
+ // app-kit-validated `ui.extensions` block persists verbatim on the
44190
+ // installed record — `makeInstalledAppUiApps` (W-C) is the only
44191
+ // consumer. Frozen already by app-kit; spread-cloned here so the
44192
+ // persisted shape stays a plain JSON-safe object (apps.json is
44193
+ // what actually freezes it). Apps without the block install
44194
+ // identically to before (plan I1).
44195
+ ...handle.ui?.extensions !== void 0 ? { extensions: handle.ui.extensions } : {}
43538
44196
  } : void 0;
43539
44197
  const artifact = refs.artifact ? {
43540
44198
  path: refs.artifact.path,
@@ -44576,12 +45234,12 @@ async function readBody2(req) {
44576
45234
  }
44577
45235
  return { raw: Buffer.concat(chunks).toString("utf8") };
44578
45236
  }
44579
- var isRecord8 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
45237
+ var isRecord9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
44580
45238
  function parseParts(raw) {
44581
45239
  if (!Array.isArray(raw)) throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "message.parts must be an array");
44582
45240
  const parts = [];
44583
45241
  for (const p of raw) {
44584
- 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");
44585
45243
  if ("text" in p) {
44586
45244
  if (typeof p.text !== "string") throw new RpcFailure(JSON_RPC_ERROR.invalidParams, "part.text must be a string");
44587
45245
  parts.push({ text: p.text });
@@ -44614,13 +45272,13 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
44614
45272
  send(res, 200, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.parse, message: "parse error: body is not valid JSON" } });
44615
45273
  return true;
44616
45274
  }
44617
- if (!isRecord8(rpc) || rpc.jsonrpc !== "2.0" || typeof rpc.method !== "string") {
45275
+ if (!isRecord9(rpc) || rpc.jsonrpc !== "2.0" || typeof rpc.method !== "string") {
44618
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)' } });
44619
45277
  return true;
44620
45278
  }
44621
45279
  const id = typeof rpc.id === "string" || typeof rpc.id === "number" ? rpc.id : null;
44622
45280
  const method = rpc.method;
44623
- const params = isRecord8(rpc.params) ? rpc.params : {};
45281
+ const params = isRecord9(rpc.params) ? rpc.params : {};
44624
45282
  const requested = negotiateVersion(
44625
45283
  typeof req.headers["a2a-version"] === "string" ? req.headers["a2a-version"] : void 0,
44626
45284
  new URL(req.url ?? "/", "http://localhost").searchParams.get("version")
@@ -44678,14 +45336,14 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
44678
45336
  throw new RpcFailure(JSON_RPC_ERROR.unsupportedOperation, `method "SendMessage" is not allowed for app "${appId}": its manifest does not declare accepts.tasks: true`);
44679
45337
  }
44680
45338
  const rawMessage = params.message;
44681
- if (!isRecord8(rawMessage) || rawMessage.role !== "ROLE_USER") {
45339
+ if (!isRecord9(rawMessage) || rawMessage.role !== "ROLE_USER") {
44682
45340
  throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'params.message must be an object with role "ROLE_USER"');
44683
45341
  }
44684
45342
  const parts = parseParts(rawMessage.parts);
44685
45343
  const { skill, prompt } = extractSkillRequest(
44686
- { parts, ...isRecord8(rawMessage.metadata) ? { metadata: rawMessage.metadata } : {} },
45344
+ { parts, ...isRecord9(rawMessage.metadata) ? { metadata: rawMessage.metadata } : {} },
44687
45345
  appId,
44688
- isRecord8(params.metadata) ? params.metadata : void 0
45346
+ isRecord9(params.metadata) ? params.metadata : void 0
44689
45347
  );
44690
45348
  if (!skill) {
44691
45349
  throw new RpcFailure(JSON_RPC_ERROR.invalidParams, 'no skill requested: set message.metadata.skill or start the first text part with "skill:<id>"');
@@ -46565,6 +47223,7 @@ async function startHttpServer(opts) {
46565
47223
  opts.resolveSandboxProvider,
46566
47224
  opts.webhookNotifier,
46567
47225
  opts.ensureLlmEndpointRunning,
47226
+ opts.listAgentAdapters,
46568
47227
  opts.deviceMirrorSync
46569
47228
  );
46570
47229
  if (handled) return;
@@ -48617,7 +49276,7 @@ async function handleSandboxes(_req, res, path, resolveSandboxProvider2) {
48617
49276
  json3(200, { alive: true, ...probe.state ? { state: probe.state } : { state: row.state }, checkedAt });
48618
49277
  return true;
48619
49278
  }
48620
- async function handleSessions(req, res, path, registry, resolveAgentAdapter, ptyEnabled = false, resolveBrowserAdapter, listBrowserAdapters, sessionEvents, eventRing, buildOrchestratorMcp, daemonMcpUrl, provisionWorktree, listCatalogModels, resolveSandboxProvider2, webhookNotifier, ensureLlmEndpointRunning, deviceMirrorSync) {
49279
+ async function handleSessions(req, res, path, registry, resolveAgentAdapter, ptyEnabled = false, resolveBrowserAdapter, listBrowserAdapters, sessionEvents, eventRing, buildOrchestratorMcp, daemonMcpUrl, provisionWorktree, listCatalogModels, resolveSandboxProvider2, webhookNotifier, ensureLlmEndpointRunning, listAgentAdapters, deviceMirrorSync) {
48621
49280
  const json3 = (status, body) => {
48622
49281
  res.writeHead(status, { "content-type": "application/json" });
48623
49282
  res.end(JSON.stringify(body));
@@ -48775,12 +49434,13 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
48775
49434
  ...listCatalogModels ? { listCatalogModels } : {},
48776
49435
  ...resolveSandboxProvider2 ? { resolveSandboxProvider: resolveSandboxProvider2 } : {},
48777
49436
  ...webhookNotifier ? { webhookNotifier } : {},
48778
- ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {}
49437
+ ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {},
49438
+ ...listAgentAdapters ? { listAgentAdapters } : {}
48779
49439
  },
48780
49440
  spawnArgs
48781
49441
  );
48782
49442
  if (!result.ok) {
48783
- const status = result.code === "adapter_not_found" || result.code === "no_cwd" ? 404 : result.code === "orchestrator_not_enabled" ? 501 : result.code === "orchestrator_max_depth_exceeded" || result.code === "orchestrator_child_quota_exceeded" || result.code === "role_spawn_denied" ? 409 : result.code === "invalid_role" || result.code === "browser_unsupported" || result.code === "worktree_requires_explicit_repo" || result.code === "device_spawn_requires_repo_identity" || result.code === "device_bridge_workspace_unknown" || result.code === "access_profile_not_found" || result.code === "access_profile_ineligible" || result.code === "sandbox_cwd_invalid" ? 400 : 500;
49443
+ const status = result.code === "adapter_not_found" || result.code === "no_cwd" ? 404 : result.code === "orchestrator_not_enabled" ? 501 : result.code === "orchestrator_max_depth_exceeded" || result.code === "orchestrator_child_quota_exceeded" || result.code === "role_spawn_denied" ? 409 : result.code === "invalid_role" || result.code === "browser_unsupported" || result.code === "worktree_requires_explicit_repo" || result.code === "device_spawn_requires_repo_identity" || result.code === "device_bridge_workspace_unknown" || result.code === "access_profile_not_found" || result.code === "access_profile_ineligible" || result.code === "sandbox_cwd_invalid" || result.code === "device_spawn_unreachable" ? 400 : 500;
48784
49444
  json3(status, {
48785
49445
  error: result.code,
48786
49446
  message: result.message,
@@ -48842,12 +49502,13 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
48842
49502
  ...listCatalogModels ? { listCatalogModels } : {},
48843
49503
  ...resolveSandboxProvider2 ? { resolveSandboxProvider: resolveSandboxProvider2 } : {},
48844
49504
  ...webhookNotifier ? { webhookNotifier } : {},
48845
- ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {}
49505
+ ...ensureLlmEndpointRunning ? { ensureLlmEndpointRunning } : {},
49506
+ ...listAgentAdapters ? { listAgentAdapters } : {}
48846
49507
  },
48847
49508
  spawnArgs
48848
49509
  );
48849
49510
  if (!result.ok) {
48850
- const status = result.code === "adapter_not_found" || result.code === "no_cwd" ? 404 : result.code === "orchestrator_not_enabled" ? 501 : result.code === "orchestrator_max_depth_exceeded" || result.code === "orchestrator_child_quota_exceeded" || result.code === "role_spawn_denied" ? 409 : result.code === "invalid_role" || result.code === "browser_unsupported" || result.code === "worktree_requires_explicit_repo" || result.code === "device_spawn_requires_repo_identity" || result.code === "device_bridge_workspace_unknown" || result.code === "access_profile_not_found" || result.code === "access_profile_ineligible" || result.code === "sandbox_cwd_invalid" ? 400 : 500;
49511
+ const status = result.code === "adapter_not_found" || result.code === "no_cwd" ? 404 : result.code === "orchestrator_not_enabled" ? 501 : result.code === "orchestrator_max_depth_exceeded" || result.code === "orchestrator_child_quota_exceeded" || result.code === "role_spawn_denied" ? 409 : result.code === "invalid_role" || result.code === "browser_unsupported" || result.code === "worktree_requires_explicit_repo" || result.code === "device_spawn_requires_repo_identity" || result.code === "device_bridge_workspace_unknown" || result.code === "access_profile_not_found" || result.code === "access_profile_ineligible" || result.code === "sandbox_cwd_invalid" || result.code === "device_spawn_unreachable" ? 400 : 500;
48851
49512
  json3(status, {
48852
49513
  error: result.code,
48853
49514
  message: result.message,
@@ -52198,9 +52859,13 @@ function isCrashed(desc) {
52198
52859
  if (desc.kind !== "agent-cli") return false;
52199
52860
  if (desc.status !== "running") return false;
52200
52861
  if (desc.remote === true) return false;
52862
+ if (desc.adapterConnected === false) return true;
52201
52863
  if (desc.pid === null || desc.pid === void 0) return false;
52202
52864
  return desc.processAlive === false;
52203
52865
  }
52866
+ function crashReasonFor(desc) {
52867
+ return desc.adapterConnected === false ? "transport-closed" : "process-gone";
52868
+ }
52204
52869
  function runCrashDetectPass(opts) {
52205
52870
  const { registry, isServed } = opts;
52206
52871
  const intervalMs = opts.crashDetectIntervalMs;
@@ -52216,7 +52881,7 @@ function runCrashDetectPass(opts) {
52216
52881
  ids: []
52217
52882
  };
52218
52883
  for (const d of candidates) {
52219
- if (registry.markCrashed(d.id)) {
52884
+ if (registry.markCrashed(d.id, crashReasonFor(d))) {
52220
52885
  summary.crashed++;
52221
52886
  summary.ids.push(d.id);
52222
52887
  }
@@ -52386,6 +53051,7 @@ async function resolveMessagingDefaults(loadCfg = loadConfig) {
52386
53051
 
52387
53052
  // src/index.ts
52388
53053
  init_transcript_writer();
53054
+ init_session_index();
52389
53055
  var BINDINGS_FILE_PATH = () => resolve(homedir(), ".agentproto", "transmitter-bindings.json");
52390
53056
  var PERSIST_DEBOUNCE_MS5 = 1500;
52391
53057
  var keyOf = (alias, source, contactRef) => `${alias}:${source}:${contactRef}`;
@@ -52799,12 +53465,13 @@ async function ssrfFetch(url, init, io = {}) {
52799
53465
  if ((init.body?.byteLength ?? 0) > MAX_REQUEST_BODY_BYTES) {
52800
53466
  throw new SsrfFetchError("connect", `request body exceeds the ${MAX_REQUEST_BODY_BYTES}-byte clamp`);
52801
53467
  }
53468
+ const port = parsed.port ? Number(parsed.port) : 443;
52802
53469
  if (testDispatcher) {
52803
53470
  const literal = parseIpv4(parsed.hostname);
52804
53471
  if (literal && !ipv4Public(literal[0], literal[1], literal[2], literal[3])) {
52805
53472
  throw new SsrfFetchError("private_target", `literal non-public address ${parsed.hostname}`);
52806
53473
  }
52807
- 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 });
52808
53475
  }
52809
53476
  const candidates = await resolveHost(parsed.hostname, io.resolvers ?? {});
52810
53477
  if (candidates.length === 0) {
@@ -52816,17 +53483,26 @@ async function ssrfFetch(url, init, io = {}) {
52816
53483
  }
52817
53484
  const connectIp = candidates[0] ?? candidates[candidates.length - 1];
52818
53485
  if (connectIp === void 0) throw new SsrfFetchError("connect", "no address available for connect");
52819
- const port = parsed.port ? Number(parsed.port) : 443;
52820
53486
  return await rawPost({
52821
53487
  connectIp,
52822
53488
  servername: parsed.hostname,
52823
53489
  port,
52824
53490
  path: `${parsed.pathname}${parsed.search}`,
52825
- 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),
52826
53497
  body: init.body,
52827
53498
  timeoutMs: init.timeoutMs
52828
53499
  });
52829
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
+ }
52830
53506
  function rawPost(args) {
52831
53507
  return new Promise((resolve42, reject) => {
52832
53508
  const req = request({
@@ -52837,6 +53513,8 @@ function rawPost(args) {
52837
53513
  path: args.path,
52838
53514
  method: "POST",
52839
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.
52840
53518
  headers: args.headers,
52841
53519
  rejectUnauthorized: true
52842
53520
  });
@@ -52954,6 +53632,562 @@ async function deliverEventEnvelope(replay, event, deps2 = {}) {
52954
53632
  }
52955
53633
  return { ok: false, reason: "retries_exhausted", delivery };
52956
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
+ }
52957
54191
 
52958
54192
  // src/sentinel-webhook-outbox.ts
52959
54193
  function agentprotoHome3() {
@@ -52969,13 +54203,7 @@ function webhookRequestId(sentinelId, eventId) {
52969
54203
  return `sub_${digest.slice(0, 32)}`;
52970
54204
  }
52971
54205
  function toWebhookEnvelope(event) {
52972
- return {
52973
- eventId: event.id,
52974
- name: event.type,
52975
- timestamp: event.time,
52976
- data: { ...event.data, subject: event.subject, summary: event.summary },
52977
- cursor: null
52978
- };
54206
+ return toMcpEvent(event);
52979
54207
  }
52980
54208
  function createSentinelWebhookOutbox(opts) {
52981
54209
  const filePath = opts.filePath ?? resolve(agentprotoHome3(), "sentinel-webhook-outbox.json");
@@ -54877,12 +56105,12 @@ function canonicalJson(value) {
54877
56105
  }
54878
56106
  function isToolAppVisible(tool) {
54879
56107
  const meta = tool._meta;
54880
- const ui = isRecord9(meta?.ui) ? meta.ui : void 0;
56108
+ const ui = isRecord11(meta?.ui) ? meta.ui : void 0;
54881
56109
  const visibility = ui?.visibility;
54882
56110
  if (!Array.isArray(visibility)) return true;
54883
56111
  return visibility.includes("app");
54884
56112
  }
54885
- function isRecord9(v) {
56113
+ function isRecord11(v) {
54886
56114
  return typeof v === "object" && v !== null && !Array.isArray(v);
54887
56115
  }
54888
56116
  function buildUiToolIndex(tools) {
@@ -55407,19 +56635,19 @@ function classifyMcpError(err) {
55407
56635
  return { status: "unreachable", error };
55408
56636
  }
55409
56637
  function uiMetaOf(meta) {
55410
- if (!isRecord10(meta) || !isRecord10(meta.ui)) return {};
56638
+ if (!isRecord12(meta) || !isRecord12(meta.ui)) return {};
55411
56639
  const ui = meta.ui;
55412
56640
  const csp = cspOf(ui.csp);
55413
56641
  return {
55414
56642
  ...csp ? { csp } : {},
55415
- ...isRecord10(ui.permissions) ? { permissions: ui.permissions } : {},
56643
+ ...isRecord12(ui.permissions) ? { permissions: ui.permissions } : {},
55416
56644
  ...typeof ui.prefersBorder === "boolean" ? { prefersBorder: ui.prefersBorder } : {},
55417
56645
  ...typeof ui.domain === "string" ? { domain: ui.domain } : {}
55418
56646
  };
55419
56647
  }
55420
56648
  var CSP_DOMAIN_KEYS = ["connectDomains", "resourceDomains", "frameDomains", "baseUriDomains"];
55421
56649
  function cspOf(raw) {
55422
- if (!isRecord10(raw)) return void 0;
56650
+ if (!isRecord12(raw)) return void 0;
55423
56651
  const csp = {};
55424
56652
  for (const key of CSP_DOMAIN_KEYS) {
55425
56653
  const value = raw[key];
@@ -55429,7 +56657,7 @@ function cspOf(raw) {
55429
56657
  }
55430
56658
  return Object.keys(csp).length > 0 ? csp : void 0;
55431
56659
  }
55432
- function isRecord10(v) {
56660
+ function isRecord12(v) {
55433
56661
  return typeof v === "object" && v !== null && !Array.isArray(v);
55434
56662
  }
55435
56663
  function errorMessage(err) {
@@ -57218,6 +58446,7 @@ function createPrProvenanceReconciler(opts) {
57218
58446
  const stampedPrUrls = /* @__PURE__ */ new Set();
57219
58447
  const costRefreshedPrUrls = /* @__PURE__ */ new Set();
57220
58448
  const lastPollAt = /* @__PURE__ */ new Map();
58449
+ const scannedPrUrls = /* @__PURE__ */ new Set();
57221
58450
  const reconcile = async (sessionId, terminal) => {
57222
58451
  const desc = opts.registry.get(sessionId);
57223
58452
  if (!desc || desc.kind !== "agent-cli") return;
@@ -57256,6 +58485,29 @@ function createPrProvenanceReconciler(opts) {
57256
58485
  if (!shouldStamp(record2.createdPrUrl)) continue;
57257
58486
  await stamp({ number: record2.createdPrNumber, url: record2.createdPrUrl });
57258
58487
  }
58488
+ if (desc.eventsPath) {
58489
+ for (const turn of readRecentTurnsSync(desc.eventsPath)) {
58490
+ if (turn.role !== "assistant") continue;
58491
+ const re = /https?:\/\/[^\s<>"]+/g;
58492
+ let m;
58493
+ while ((m = re.exec(turn.text)) !== null) {
58494
+ const url = m[0].replace(/[.,;:)\]]+$/, "");
58495
+ const parsed = parsePrUrl(url);
58496
+ if (!parsed) continue;
58497
+ if (desc.openedPrs?.some((known) => known.url === url)) continue;
58498
+ if (scannedPrUrls.has(url)) continue;
58499
+ scannedPrUrls.add(url);
58500
+ try {
58501
+ opts.registry.recordOpenedPr(sessionId, {
58502
+ adapter: "output-scan",
58503
+ number: parsed.number,
58504
+ url
58505
+ });
58506
+ } catch {
58507
+ }
58508
+ }
58509
+ }
58510
+ }
57259
58511
  const pr = await opts.resolveOpenPr(cwd);
57260
58512
  if (pr && shouldStamp(pr.url)) await stamp(pr);
57261
58513
  await refreshCost(desc, supervisor, cwd);
@@ -57914,7 +59166,7 @@ function humanizeKey(key) {
57914
59166
  const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-.]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
57915
59167
  return words.length === 0 ? "Value" : words.charAt(0).toUpperCase() + words.slice(1);
57916
59168
  }
57917
- function isRecord11(value) {
59169
+ function isRecord13(value) {
57918
59170
  return value !== null && typeof value === "object" && !Array.isArray(value);
57919
59171
  }
57920
59172
  function scalarText(value) {
@@ -57944,7 +59196,7 @@ function flattenRows(value, label, depth, out) {
57944
59196
  });
57945
59197
  return;
57946
59198
  }
57947
- if (isRecord11(value)) {
59199
+ if (isRecord13(value)) {
57948
59200
  if (depth >= MAX_DEPTH) return;
57949
59201
  for (const [key, child] of Object.entries(value)) {
57950
59202
  flattenRows(child, label ? `${label} / ${humanizeKey(key)}` : humanizeKey(key), depth + 1, out);
@@ -57966,7 +59218,7 @@ function renderMail(preview2) {
57966
59218
  const cc = addressList(preview2.cc);
57967
59219
  const subject = scalarText(preview2.subject) ?? "";
57968
59220
  const body = scalarText(preview2.body) ?? "";
57969
- 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) => {
57970
59222
  const name = scalarText(a.fileName) ?? "";
57971
59223
  const size = typeof a.sizeBytes === "number" ? humanSize(a.sizeBytes) : "";
57972
59224
  return size ? `${name} (${size})` : name;
@@ -57981,9 +59233,9 @@ function renderMail(preview2) {
57981
59233
  }
57982
59234
  function renderPreview(record2) {
57983
59235
  const preview2 = record2.preview;
57984
- if (isRecord11(preview2) && preview2.kind === "mail") return renderMail(preview2);
59236
+ if (isRecord13(preview2) && preview2.kind === "mail") return renderMail(preview2);
57985
59237
  const rows = [];
57986
- if (isRecord11(preview2)) {
59238
+ if (isRecord13(preview2)) {
57987
59239
  for (const [key, value] of Object.entries(preview2)) flattenRows(value, humanizeKey(key), 1, rows);
57988
59240
  } else {
57989
59241
  flattenRows(preview2, "Details", 1, rows);
@@ -59435,6 +60687,8 @@ async function reconcileSandboxLedger(deps2) {
59435
60687
  // src/index.ts
59436
60688
  init_session_outcome();
59437
60689
  init_session_end_reason();
60690
+ init_transcript_writer();
60691
+ init_session_index();
59438
60692
  init_sessions();
59439
60693
  init_sessions();
59440
60694
  init_process_stats();
@@ -59454,12 +60708,12 @@ function measureToolList(tools) {
59454
60708
  }
59455
60709
  var packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
59456
60710
  var helpDir = join(packageRoot, "docs", "mcp-tools");
59457
- var cache = /* @__PURE__ */ new Map();
60711
+ var cache2 = /* @__PURE__ */ new Map();
59458
60712
  function getToolHelp(name) {
59459
- if (cache.has(name)) return cache.get(name);
60713
+ if (cache2.has(name)) return cache2.get(name);
59460
60714
  const path = join(helpDir, `${name}.md`);
59461
60715
  const content = existsSync(path) ? readFileSync(path, "utf8") : void 0;
59462
- cache.set(name, content);
60716
+ cache2.set(name, content);
59463
60717
  return content;
59464
60718
  }
59465
60719
  function listToolHelpTopics() {
@@ -61752,7 +63006,7 @@ async function registerSentinelAdapterTools(server, opts = {}) {
61752
63006
  makeListTool({
61753
63007
  server,
61754
63008
  toolName: "list_sentinel_adapters",
61755
- 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.",
61756
63010
  lister: makeSentinelLister({ credsStore, ledger })
61757
63011
  });
61758
63012
  const { validSlugs, fields } = await collectSetupSchema3();
@@ -61877,102 +63131,6 @@ function createWorkspaceFs(opts) {
61877
63131
 
61878
63132
  // src/index.ts
61879
63133
  init_role();
61880
- function challengeReasonForSsrf(reason) {
61881
- if (reason === "non_https") return "non_https";
61882
- if (reason === "private_target" || reason === "redirect") return "ssrf_blocked";
61883
- return "timeout";
61884
- }
61885
- var CHALLENGE_TTL_MS = 10 * 60 * 1e3;
61886
- var encoder = new TextEncoder();
61887
- var cache2 = /* @__PURE__ */ new Map();
61888
- function challengeCacheSize() {
61889
- return cache2.size;
61890
- }
61891
- function resetChallengeCacheForTests() {
61892
- cache2.clear();
61893
- }
61894
- function normalizeCallbackUrl(url) {
61895
- try {
61896
- const u = new URL(url);
61897
- const port = u.port === "443" ? "" : `:${u.port}`;
61898
- return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${u.pathname}${u.search}`;
61899
- } catch {
61900
- return null;
61901
- }
61902
- }
61903
- function hashSecret(secret) {
61904
- return createHash("sha256").update(secret, "utf8").digest("hex");
61905
- }
61906
- function constantTimeEq(a, b) {
61907
- const da = createHash("sha256").update(a, "utf8").digest();
61908
- const db = createHash("sha256").update(b, "utf8").digest();
61909
- return timingSafeEqual(da, db);
61910
- }
61911
- async function verifyCallback(input, deps2 = {}) {
61912
- const now = deps2.now ?? (() => Date.now());
61913
- const normalized = normalizeCallbackUrl(input.url);
61914
- if (normalized === null || !normalized.startsWith("https://")) {
61915
- return { ok: false, reason: "non_https", detail: "callback URL must be an https URL" };
61916
- }
61917
- if (decodeWhsecSecret(input.secret) === null) {
61918
- return {
61919
- ok: false,
61920
- reason: "challenge_failed",
61921
- 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)"
61922
- };
61923
- }
61924
- const cacheKey = `${input.principal}|${normalized}|${hashSecret(input.secret)}`;
61925
- const cached = cache2.get(cacheKey);
61926
- if (cached && cached.expiresAt > now()) {
61927
- return { ok: true, verificationBytes: cached.requestBytes };
61928
- }
61929
- if (cached) cache2.delete(cacheKey);
61930
- const challenge = randomBytes(32).toString("hex");
61931
- const requestBytes = encoder.encode(JSON.stringify({ type: "verification", challenge }));
61932
- const msgId = `msg_verification_${randomBytes(12).toString("hex")}`;
61933
- const timestamp = Math.floor(now() / 1e3);
61934
- let headers;
61935
- try {
61936
- const signed = signWebhook({ msgId, timestamp, payload: requestBytes, secrets: [input.secret] });
61937
- headers = {
61938
- ...signed,
61939
- "content-type": "application/json",
61940
- "X-MCP-Subscription-Id": input.subscriptionId
61941
- };
61942
- } catch (err) {
61943
- return { ok: false, reason: "challenge_failed", detail: `signing failed: ${err.message}` };
61944
- }
61945
- let response;
61946
- try {
61947
- response = await (deps2.fetch ?? ssrfFetch)(input.url, {
61948
- method: "POST",
61949
- headers,
61950
- body: requestBytes,
61951
- timeoutMs: 1e4
61952
- // default challenge timeout (delivery uses 15_000)
61953
- });
61954
- } catch (err) {
61955
- if (err instanceof SsrfFetchError) {
61956
- return { ok: false, reason: challengeReasonForSsrf(err.reason), detail: `${err.reason}: ${err.message}` };
61957
- }
61958
- return { ok: false, reason: "timeout", detail: err.message };
61959
- }
61960
- if (response.status < 200 || response.status > 299) {
61961
- return { ok: false, reason: "non_2xx", detail: `callback responded with status ${response.status}` };
61962
- }
61963
- let parsedBody;
61964
- try {
61965
- parsedBody = JSON.parse(response.body);
61966
- } catch {
61967
- return { ok: false, reason: "challenge_failed", detail: "response body is not JSON" };
61968
- }
61969
- const echoed = parsedBody?.challenge;
61970
- if (typeof echoed !== "string" || !constantTimeEq(echoed, challenge)) {
61971
- return { ok: false, reason: "challenge_failed", detail: "response did not echo the fresh challenge" };
61972
- }
61973
- cache2.set(cacheKey, { requestBytes, expiresAt: now() + CHALLENGE_TTL_MS });
61974
- return { ok: true, verificationBytes: requestBytes };
61975
- }
61976
63134
  async function hasNamedLlmEndpoints() {
61977
63135
  const override = process.env.LLM_ENDPOINT_ENDPOINTS_FILE?.trim();
61978
63136
  const path = override && override.length > 0 ? override : join(homedir(), ".agentproto", "llm-endpoints.json");
@@ -62001,7 +63159,7 @@ init_worktree_isolation();
62001
63159
  var pairing_registry_exports = {};
62002
63160
  __reExport(pairing_registry_exports, pairing_host_star);
62003
63161
  var JOIN_TOKENS_VERSION = 1;
62004
- var DEFAULT_TTL_MS = 90 * 864e5;
63162
+ var DEFAULT_TTL_MS2 = 90 * 864e5;
62005
63163
  var DEFAULT_RECONNECT_MIN_MS = 1e3;
62006
63164
  var DEFAULT_RECONNECT_MAX_MS = 3e4;
62007
63165
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 11e4;
@@ -62111,7 +63269,7 @@ function createJoinTokenRegistry(deps2) {
62111
63269
  const secret = b64url(randomBytes(16));
62112
63270
  const id = randomBytes(6).toString("hex");
62113
63271
  const nowIso2 = new Date(now()).toISOString();
62114
- const ttlMs = input.ttlMs ?? DEFAULT_TTL_MS;
63272
+ const ttlMs = input.ttlMs ?? DEFAULT_TTL_MS2;
62115
63273
  const expiresAt = new Date(now() + ttlMs).toISOString();
62116
63274
  const record2 = {
62117
63275
  id,
@@ -63283,6 +64441,20 @@ async function createGateway(opts) {
63283
64441
  });
63284
64442
  registerEvalReporterTools(server);
63285
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
+ });
63286
64458
  return server;
63287
64459
  };
63288
64460
  let internalToolServerPromise;
@@ -63607,6 +64779,15 @@ async function createGateway(opts) {
63607
64779
  ...passOpts?.isServed ? { isServed: passOpts.isServed } : {}
63608
64780
  });
63609
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
+ },
63610
64791
  async stop() {
63611
64792
  heartbeat.stop();
63612
64793
  if (idleReapTimer) clearInterval(idleReapTimer);
@@ -63679,6 +64860,6 @@ var export_readPairingsSnapshot = pairing_registry_exports.readPairingsSnapshot;
63679
64860
  var export_removeProviderKey = providers_store_exports.removeProviderKey;
63680
64861
  var export_setProviderKey = providers_store_exports.setProviderKey;
63681
64862
 
63682
- 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 };
63683
64864
  //# sourceMappingURL=index.mjs.map
63684
64865
  //# sourceMappingURL=index.mjs.map