@agentproto/runtime 5.11.0 → 5.12.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
@@ -10389,6 +10389,20 @@ function buildDaemonSelfMountEntry(daemonMcpUrl, opts) {
10389
10389
  ref += `${ref.includes("?") ? "&" : "?"}callerSessionId=${encodeURIComponent(opts.callerSessionId)}`;
10390
10390
  return { name: "agentproto", transport: "http", ref };
10391
10391
  }
10392
+ function stampDaemonCallerSessionId(mcpServers, opts) {
10393
+ if (!mcpServers) return void 0;
10394
+ const daemonQ = `${opts.daemonMcpUrl}?`;
10395
+ return mcpServers.map((entry) => {
10396
+ if (entry.transport !== "http" || typeof entry.ref !== "string") return entry;
10397
+ const targetsDaemon = entry.ref === opts.daemonMcpUrl || entry.ref.startsWith(daemonQ);
10398
+ if (!targetsDaemon || entry.ref.includes("callerSessionId=")) return entry;
10399
+ const sep7 = entry.ref.includes("?") ? "&" : "?";
10400
+ return {
10401
+ ...entry,
10402
+ ref: `${entry.ref}${sep7}callerSessionId=${encodeURIComponent(opts.callerSessionId)}`
10403
+ };
10404
+ });
10405
+ }
10392
10406
  function slugifyMcpImportName(alias, fallbackId) {
10393
10407
  const slug = alias.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
10394
10408
  return slug || fallbackId;
@@ -10935,16 +10949,9 @@ async function spawnAgentSession(deps2, input) {
10935
10949
  }
10936
10950
  }
10937
10951
  if (daemonMcpUrl && mcpServers) {
10938
- const daemonQ = `${daemonMcpUrl}?`;
10939
- mcpServers = mcpServers.map((entry) => {
10940
- if (entry.transport !== "http" || typeof entry.ref !== "string") return entry;
10941
- const targetsDaemon = entry.ref === daemonMcpUrl || entry.ref.startsWith(daemonQ);
10942
- if (!targetsDaemon || entry.ref.includes("callerSessionId=")) return entry;
10943
- const sep7 = entry.ref.includes("?") ? "&" : "?";
10944
- return {
10945
- ...entry,
10946
- ref: `${entry.ref}${sep7}callerSessionId=${encodeURIComponent(mintedSessionId)}`
10947
- };
10952
+ mcpServers = stampDaemonCallerSessionId(mcpServers, {
10953
+ daemonMcpUrl,
10954
+ callerSessionId: mintedSessionId
10948
10955
  });
10949
10956
  }
10950
10957
  if (mcpServers === void 0 && parentSessionId && buildOrchestratorMcp && input.orchestrator !== false && input.sandbox === void 0) {
@@ -17288,8 +17295,16 @@ var init_session_end_reason = __esm({
17288
17295
  // `registry.closeWithOutcome`, never a plain `kill()`.
17289
17296
  "steward-completed",
17290
17297
  // verdict:"done" — the steward closed it as finished work.
17291
- "steward-abandoned"
17298
+ "steward-abandoned",
17292
17299
  // verdict other than "done" — the steward closed it as not completed.
17300
+ // `restartAgentSession` (session-restart-core.ts) minted a fresh-id
17301
+ // continuation while this row was still alive (running/starting) — the
17302
+ // OLD row is superseded (see its `continuedTo`) and closed cleanly here,
17303
+ // never left running alongside its replacement. Deliberate, not a crash:
17304
+ // never eligible for `restart-scheduler.ts`'s crash-restart (status is
17305
+ // "killed", not "error") and never triggers `notifyParentOnCrash`
17306
+ // (`supervisor-notify.ts` gates on `status:"error"`/`reason:"crashed"`).
17307
+ "restarted"
17293
17308
  ];
17294
17309
  PROVIDER_LIMIT_ERROR_RE = /\bhit your (?:session|usage) limit\b/i;
17295
17310
  }
@@ -26383,7 +26398,11 @@ var DELIBERATE_END_REASONS = /* @__PURE__ */ new Set([
26383
26398
  "operator-completed",
26384
26399
  "operator-stopped",
26385
26400
  "steward-completed",
26386
- "steward-abandoned"
26401
+ "steward-abandoned",
26402
+ // Superseded by a fresh-id restart continuation (session-restart-core.ts)
26403
+ // while still alive — never auto-revive the OLD row, it already has a
26404
+ // living replacement (`continuedTo`).
26405
+ "restarted"
26387
26406
  ]);
26388
26407
  var POLLABLE_STATUSES = /* @__PURE__ */ new Set(["active", "orphaned"]);
26389
26408
  function agentprotoHome3() {
@@ -26601,8 +26620,16 @@ function createSentinelRuntime(opts) {
26601
26620
  try {
26602
26621
  await opts.registry.sendMessage(msg, { source: "sentinel", origin: sentinel.id });
26603
26622
  } catch (err) {
26604
- if (!(err instanceof SessionNotAliveError)) throw err;
26605
- await handleDeadSession(sentinel, event, msg);
26623
+ if (err instanceof SessionNotAliveError) {
26624
+ await handleDeadSession(sentinel, event, msg);
26625
+ return;
26626
+ }
26627
+ if (opts.sessionInfo && !opts.isSessionAlive(target.sessionId) && !opts.sessionInfo(target.sessionId)) {
26628
+ parkEvent(sentinel, event, `target session ${target.sessionId} no longer exists`);
26629
+ markOrphaned(sentinel);
26630
+ return;
26631
+ }
26632
+ throw err;
26606
26633
  }
26607
26634
  }
26608
26635
  function checkSubjectTerminalExpiry(sentinel, event) {
@@ -26820,6 +26847,8 @@ function createSentinelRuntime(opts) {
26820
26847
  // src/session-restart-core.ts
26821
26848
  init_resume_strategies();
26822
26849
  init_spawn_defaults();
26850
+ init_session_spawn();
26851
+ init_session_continue_fresh();
26823
26852
  init_launch_config();
26824
26853
  init_resume_context_digest();
26825
26854
  init_eligibility_manifest();
@@ -27123,9 +27152,25 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27123
27152
  authSpec = resumeAuth.authSpec;
27124
27153
  authEcho = resumeAuth.authEcho;
27125
27154
  }
27155
+ const buildRestartMcpServers = (newSessionId) => {
27156
+ if (!opts.daemonMcpUrl) {
27157
+ if (prev.mcpServers && prev.mcpServers.length > 0) {
27158
+ console.warn(
27159
+ `[restartAgentSession] no daemonMcpUrl wired for restart of ${prev.id} \u2014 carrying mcpServers forward WITHOUT re-stamping callerSessionId (the restarted session ${newSessionId} will keep speaking to the daemon under the OLD session's identity).`
27160
+ );
27161
+ }
27162
+ return prev.mcpServers;
27163
+ }
27164
+ const stripped = stripOwnCallerStamp(prev.mcpServers, prev.id);
27165
+ return stampDaemonCallerSessionId(stripped, {
27166
+ daemonMcpUrl: opts.daemonMcpUrl,
27167
+ callerSessionId: newSessionId
27168
+ });
27169
+ };
27126
27170
  const spawnWithResume = async (resumeSessionId) => {
27127
27171
  let liveSessionId;
27128
27172
  const restartedSessionId = mintSessionId();
27173
+ const restartMcpServers = buildRestartMcpServers(restartedSessionId);
27129
27174
  if (sandboxId !== void 0) {
27130
27175
  return spawnInSandbox(sandboxId, restartedSessionId);
27131
27176
  }
@@ -27168,7 +27213,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27168
27213
  // Legacy AIP-45 mode: prefer an explicit override; otherwise carry the
27169
27214
  // prior descriptor's mode forward so a plain restart preserves it.
27170
27215
  ...effMode ? { mode: effMode } : {},
27171
- ...prev.mcpServers ? { mcpServers: prev.mcpServers } : {},
27216
+ ...restartMcpServers ? { mcpServers: restartMcpServers } : {},
27172
27217
  ...authSpec ? { auth: authSpec } : {},
27173
27218
  ...launchConfig.options ? { options: launchConfig.options } : {},
27174
27219
  // Spawn-time guarantees survive a restart: the adapter runs under the
@@ -27202,7 +27247,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27202
27247
  ...resolved.authDescriptor?.provider !== void 0 ? { adapterProvider: resolved.authDescriptor.provider } : {},
27203
27248
  ...resolved.authDescriptor?.modelDerivedApiKey !== void 0 ? { modelDerivedApiKey: resolved.authDescriptor.modelDerivedApiKey } : {},
27204
27249
  ...prev.label ? { label: prev.label } : {},
27205
- ...prev.mcpServers ? { mcpServers: prev.mcpServers } : {},
27250
+ ...restartMcpServers ? { mcpServers: restartMcpServers } : {},
27206
27251
  ...prevBoundary ? { meta: boundaryMeta(prevBoundary) } : {},
27207
27252
  ...effModel ? { model: effModel } : {},
27208
27253
  // Decomposed config-axis echoes (SPEC §3.7) — carried forward from `prev`
@@ -27228,6 +27273,16 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27228
27273
  ...prev.origin ? { origin: prev.origin } : {},
27229
27274
  ...prev.parentSessionId ? { parentSessionId: prev.parentSessionId } : {},
27230
27275
  ...prev.depth !== void 0 ? { depth: prev.depth } : {},
27276
+ // Immutable spawn-time flags survive a restart exactly like
27277
+ // `continueAgentSessionFresh` carries `keepAlive`/`notifyParentOnCrash`
27278
+ // forward (session-continue-fresh.ts) — these are never axes a restart
27279
+ // overrides, only inherited, so dropping them here is what let a
27280
+ // persistent supervisor's restart silently become idle-reapable and
27281
+ // lose its crash-detect opt-ins.
27282
+ ...prev.keepAlive ? { keepAlive: true } : {},
27283
+ ...prev.notifyParentOnCrash ? { notifyParentOnCrash: true } : {},
27284
+ ...prev.sentinelAutoWatch === false ? { sentinelAutoWatch: false } : {},
27285
+ ...prev.restartPolicy ? { restartPolicy: prev.restartPolicy } : {},
27231
27286
  // Verifiability echo (never the credential) — see the auth
27232
27287
  // resolution block above. Absent when no credential resolved,
27233
27288
  // same as session-spawn.ts.
@@ -27264,6 +27319,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27264
27319
  `restartAgentSession: sandbox provider "${providerSlug}" not found \u2014 cannot re-attach sandbox "${boxSandboxId}".`
27265
27320
  );
27266
27321
  }
27322
+ const sandboxMcpServers = buildRestartMcpServers(restartedSessionId);
27267
27323
  const spec = { provider: providerSlug, config: {} };
27268
27324
  const lifecyclePolicy = resolveLifecyclePolicy(spec, true);
27269
27325
  let host;
@@ -27293,7 +27349,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27293
27349
  } : {},
27294
27350
  ...effEffort ? { effort: effEffort } : {},
27295
27351
  ...prev.label ? { label: prev.label } : {},
27296
- ...prev.mcpServers ? { mcpServers: toBoxMcpServerMounts(prev.mcpServers) } : {},
27352
+ ...sandboxMcpServers ? { mcpServers: toBoxMcpServerMounts(sandboxMcpServers) } : {},
27297
27353
  ...authSpec ? { auth: sandboxAuthForBox(authSpec) } : {}
27298
27354
  });
27299
27355
  remoteSessionId = remoteDesc.id;
@@ -27322,7 +27378,7 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27322
27378
  ...resolved?.authDescriptor?.provider !== void 0 ? { adapterProvider: resolved.authDescriptor.provider } : {},
27323
27379
  ...resolved?.authDescriptor?.modelDerivedApiKey !== void 0 ? { modelDerivedApiKey: resolved.authDescriptor.modelDerivedApiKey } : {},
27324
27380
  ...prev.label ? { label: prev.label } : {},
27325
- ...prev.mcpServers ? { mcpServers: prev.mcpServers } : {},
27381
+ ...sandboxMcpServers ? { mcpServers: sandboxMcpServers } : {},
27326
27382
  ...effModel ? { model: effModel } : {},
27327
27383
  ...effEffort ? { effort: effEffort } : {},
27328
27384
  ...effPosture !== void 0 ? { posture: effPosture } : {},
@@ -27333,6 +27389,10 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27333
27389
  ...prev.origin ? { origin: prev.origin } : {},
27334
27390
  ...prev.parentSessionId ? { parentSessionId: prev.parentSessionId } : {},
27335
27391
  ...prev.depth !== void 0 ? { depth: prev.depth } : {},
27392
+ ...prev.keepAlive ? { keepAlive: true } : {},
27393
+ ...prev.notifyParentOnCrash ? { notifyParentOnCrash: true } : {},
27394
+ ...prev.sentinelAutoWatch === false ? { sentinelAutoWatch: false } : {},
27395
+ ...prev.restartPolicy ? { restartPolicy: prev.restartPolicy } : {},
27336
27396
  ...authEcho?.fingerprint ? {
27337
27397
  auth: {
27338
27398
  mode: authEcho.authMode,
@@ -27374,6 +27434,9 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
27374
27434
  digestRecovered = result.hasContent;
27375
27435
  }
27376
27436
  }
27437
+ if (sandboxId === void 0) {
27438
+ registry.kill(prev.id, "SIGTERM", "restarted");
27439
+ }
27377
27440
  const ts = (/* @__PURE__ */ new Date()).toISOString();
27378
27441
  const emit = (axis, value) => {
27379
27442
  registry.emitConfigChanged({
@@ -27959,7 +28022,8 @@ function createMcpObservationStore(opts = {}) {
27959
28022
  const obs = touch(sessionId);
27960
28023
  const at = obs.lastSeenAt;
27961
28024
  const result = message.result && typeof message.result === "object" ? message.result : void 0;
27962
- const error = message.error ? `${String(message.error.code ?? "error")}: ${String(message.error.message ?? "")}` : void 0;
28025
+ const isExpectedDiscoverMiss = method === "server/discover" && message.error?.code === -32601;
28026
+ const error = message.error && !isExpectedDiscoverMiss ? `${String(message.error.code ?? "error")}: ${String(message.error.message ?? "")}` : void 0;
27963
28027
  const answeredVersion = typeof result?.["protocolVersion"] === "string" ? result["protocolVersion"] : void 0;
27964
28028
  const protocolVersion = method === "initialize" ? answeredVersion : clientProtocolVersion;
27965
28029
  const base = {
@@ -28149,7 +28213,7 @@ function buildSessionCapabilities(desc, pendingPermissions, mcpObservation) {
28149
28213
  function daemonMountFields(obs, turnsCompleted) {
28150
28214
  const status = daemonMountStatus(obs, turnsCompleted);
28151
28215
  const tl = obs?.toolsList;
28152
- const failing = tl && !tl.ok ? tl : [obs?.initialize, obs?.discover].find((c) => c && !c.ok);
28216
+ const failing = tl ? tl.ok ? void 0 : tl : [obs?.initialize, obs?.discover].find((c) => c && !c.ok);
28153
28217
  const error = failing?.error ?? (obs?.httpError ? `HTTP ${obs.httpError.status}` : void 0);
28154
28218
  const protocolVersion = tl?.protocolVersion ?? obs?.initialize?.protocolVersion ?? obs?.discover?.protocolVersion;
28155
28219
  return {
@@ -32561,7 +32625,8 @@ function registerSessionTools(rawServer, opts) {
32561
32625
  forceAgentResume: true,
32562
32626
  overrides,
32563
32627
  ...listCatalogModels ? { listCatalogModels } : {},
32564
- ...opts.resolveSandboxProvider ? { resolveSandboxProvider: opts.resolveSandboxProvider } : {}
32628
+ ...opts.resolveSandboxProvider ? { resolveSandboxProvider: opts.resolveSandboxProvider } : {},
32629
+ ...opts.daemonMcpUrl ? { daemonMcpUrl: opts.daemonMcpUrl } : {}
32565
32630
  });
32566
32631
  return {
32567
32632
  content: [
@@ -32771,7 +32836,8 @@ function registerSessionTools(rawServer, opts) {
32771
32836
  }
32772
32837
  }
32773
32838
  const restarted = await restartAgentSession(registry, resolveAgentAdapter, prev, {
32774
- ...opts.resolveSandboxProvider ? { resolveSandboxProvider: opts.resolveSandboxProvider } : {}
32839
+ ...opts.resolveSandboxProvider ? { resolveSandboxProvider: opts.resolveSandboxProvider } : {},
32840
+ ...opts.daemonMcpUrl ? { daemonMcpUrl: opts.daemonMcpUrl } : {}
32775
32841
  });
32776
32842
  return {
32777
32843
  content: [
@@ -39189,10 +39255,273 @@ async function handleSentinelInbound(hookKey, req, deps2) {
39189
39255
 
39190
39256
  // src/sentinel-tools.ts
39191
39257
  init_session_message();
39192
- function resolveUntil(input, prSugar) {
39258
+ var SESSION_SLUG = "session";
39259
+ var SESSION_DEFAULT_TYPES = [
39260
+ "session.turn.ended",
39261
+ "session.awaiting_input",
39262
+ "session.exited"
39263
+ ];
39264
+ var SESSION_SUBJECT_RE = /^session:(.+)$/;
39265
+ function parseSessionSubject(subject) {
39266
+ return SESSION_SUBJECT_RE.exec(subject)?.[1];
39267
+ }
39268
+ var RING_CAP = 2e3;
39269
+ var ringsByBus = /* @__PURE__ */ new WeakMap();
39270
+ function appendEvent(ring, event) {
39271
+ const seq = ring.nextSeq++;
39272
+ ring.entries.push({ seq, event });
39273
+ if (ring.entries.length > RING_CAP) ring.entries.splice(0, ring.entries.length - RING_CAP);
39274
+ return seq;
39275
+ }
39276
+ function parseCursor(cursor) {
39277
+ if (!cursor) return void 0;
39278
+ const i = cursor.lastIndexOf(":");
39279
+ if (i < 0) return void 0;
39280
+ const seq = Number(cursor.slice(i + 1));
39281
+ return Number.isFinite(seq) ? { epoch: cursor.slice(0, i), seq } : void 0;
39282
+ }
39283
+ function formatCursor(ring, seq) {
39284
+ return `${ring.epoch}:${seq}`;
39285
+ }
39286
+ function mintEventId(parts) {
39287
+ return `evt_${createHash("sha256").update(parts.join("|")).digest("hex").slice(0, 24)}`;
39288
+ }
39289
+ function truncate3(text13, max = 200) {
39290
+ const flat = text13.replace(/\s+/g, " ").trim();
39291
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}\u2026`;
39292
+ }
39293
+ function makeEvent2(input) {
39294
+ return {
39295
+ specversion: "1.0",
39296
+ id: mintEventId(input.idParts),
39297
+ source: `//agentproto.local/sentinel/${SESSION_SLUG}`,
39298
+ type: input.type,
39299
+ subject: input.subject,
39300
+ time: input.time,
39301
+ datacontenttype: "application/json",
39302
+ data: input.data,
39303
+ summary: input.summary,
39304
+ subjects: [input.subject],
39305
+ terminal: input.terminal
39306
+ };
39307
+ }
39308
+ function eventsForSessionEvent(ev) {
39309
+ switch (ev.type) {
39310
+ case "session:turn-end": {
39311
+ const subject = `session:${ev.sessionId}`;
39312
+ const who = ev.label ? `${ev.label} (${ev.sessionId})` : ev.sessionId;
39313
+ if (ev.awaitingInput) {
39314
+ const question = ev.question?.text;
39315
+ return [
39316
+ makeEvent2({
39317
+ idParts: [ev.sessionId, "awaiting_input", ev.ts],
39318
+ type: "session.awaiting_input",
39319
+ subject,
39320
+ time: ev.ts,
39321
+ terminal: false,
39322
+ data: { sessionId: ev.sessionId, ...question ? { question } : {} },
39323
+ summary: `${who} is awaiting input${question ? `: ${truncate3(question)}` : ""}`
39324
+ })
39325
+ ];
39326
+ }
39327
+ const detail = ev.error ?? (ev.reason && ev.reason !== "completed" ? ev.reason : void 0);
39328
+ return [
39329
+ makeEvent2({
39330
+ idParts: [ev.sessionId, "turn-end", ev.ts],
39331
+ type: "session.turn.ended",
39332
+ subject,
39333
+ time: ev.ts,
39334
+ terminal: false,
39335
+ data: {
39336
+ sessionId: ev.sessionId,
39337
+ ...ev.reason ? { reason: ev.reason } : {},
39338
+ ...ev.error ? { error: ev.error } : {}
39339
+ },
39340
+ summary: `${who} ended a turn${detail ? `: ${truncate3(detail)}` : ""}`
39341
+ })
39342
+ ];
39343
+ }
39344
+ case "session:awaiting-input": {
39345
+ const subject = `session:${ev.sessionId}`;
39346
+ const who = ev.label ? `${ev.label} (${ev.sessionId})` : ev.sessionId;
39347
+ const question = ev.question?.text;
39348
+ return [
39349
+ makeEvent2({
39350
+ idParts: [ev.sessionId, "awaiting_input", ev.ts],
39351
+ type: "session.awaiting_input",
39352
+ subject,
39353
+ time: ev.ts,
39354
+ terminal: false,
39355
+ data: { sessionId: ev.sessionId, ...question ? { question } : {} },
39356
+ summary: `${who} is awaiting input${question ? `: ${truncate3(question)}` : ""}`
39357
+ })
39358
+ ];
39359
+ }
39360
+ case "session:exited": {
39361
+ const subject = `session:${ev.sessionId}`;
39362
+ const who = ev.label ? `${ev.label} (${ev.sessionId})` : ev.sessionId;
39363
+ return [
39364
+ makeEvent2({
39365
+ idParts: [ev.sessionId, "exited", ev.ts],
39366
+ type: "session.exited",
39367
+ subject,
39368
+ time: ev.ts,
39369
+ terminal: true,
39370
+ data: {
39371
+ sessionId: ev.sessionId,
39372
+ status: ev.status,
39373
+ ...ev.exitCode !== void 0 ? { exitCode: ev.exitCode } : {},
39374
+ ...ev.reason ? { reason: ev.reason } : {}
39375
+ },
39376
+ summary: `${who} exited (${ev.reason ?? ev.status})`
39377
+ })
39378
+ ];
39379
+ }
39380
+ default:
39381
+ return [];
39382
+ }
39383
+ }
39384
+ function getRing(bus) {
39385
+ const existing = ringsByBus.get(bus);
39386
+ if (existing) return existing;
39387
+ const ring = { epoch: randomUUID(), entries: [], nextSeq: 0 };
39388
+ ringsByBus.set(bus, ring);
39389
+ bus.onAny((ev) => {
39390
+ for (const event of eventsForSessionEvent(ev)) appendEvent(ring, event);
39391
+ });
39392
+ return ring;
39393
+ }
39394
+ function identityOf(handle) {
39395
+ const state = handle.state;
39396
+ return typeof state?.sessionId === "string" ? state.sessionId : handle.remoteId;
39397
+ }
39398
+ function synthesizeExitEvent(ring, subject, sessionId, tag, info, goneReason) {
39399
+ const data = info ? {
39400
+ sessionId,
39401
+ status: info.status ?? "exited",
39402
+ ...info.exitCode !== void 0 ? { exitCode: info.exitCode } : {},
39403
+ ...info.endedReason ? { reason: info.endedReason } : {}
39404
+ } : { sessionId, status: "exited", ...goneReason ? { reason: goneReason } : {} };
39405
+ const summary = info ? `${info.label ?? sessionId} had already exited${info.endedReason ? ` (${info.endedReason})` : ""}` : `${sessionId} no longer exists${goneReason ? ` (${goneReason})` : ""}`;
39406
+ appendEvent(
39407
+ ring,
39408
+ makeEvent2({
39409
+ idParts: [sessionId, "exited", tag],
39410
+ type: "session.exited",
39411
+ subject,
39412
+ time: (/* @__PURE__ */ new Date()).toISOString(),
39413
+ terminal: true,
39414
+ data,
39415
+ summary
39416
+ })
39417
+ );
39418
+ }
39419
+ function resyncIfStale(ring, handle, sessionId, subject, getSession, tag, goneTag) {
39420
+ const parsed = parseCursor(handle.cursor);
39421
+ if (parsed && parsed.epoch === ring.epoch) return parsed.seq;
39422
+ const lastSeq = ring.nextSeq - 1;
39423
+ const info = getSession(sessionId);
39424
+ if (!info) {
39425
+ synthesizeExitEvent(ring, subject, sessionId, goneTag, void 0, "session no longer exists");
39426
+ } else if (!info.alive) {
39427
+ synthesizeExitEvent(ring, subject, sessionId, tag, info);
39428
+ }
39429
+ return lastSeq;
39430
+ }
39431
+ function sessionSentinelProvider(deps2) {
39432
+ return {
39433
+ slug: SESSION_SLUG,
39434
+ name: "Session Lifecycle",
39435
+ version: "0.1.0",
39436
+ description: "In-process sentinel over this daemon's own session lifecycle \u2014 turn-end, awaiting-input, exit. No credentials, no external system: the subject IS another session (`session:<id>`).",
39437
+ requiresSetup: false,
39438
+ capabilities: {
39439
+ subjects: [SESSION_SLUG],
39440
+ push: false,
39441
+ poll: true,
39442
+ durable: false,
39443
+ needsPublicUrl: false,
39444
+ requiresAuth: false,
39445
+ typicalLatencyMs: 15e3
39446
+ },
39447
+ async check() {
39448
+ return true;
39449
+ },
39450
+ async create(spec, _delivery) {
39451
+ if (spec.match.length !== 1) {
39452
+ throw new Error(`${SESSION_SLUG}: exactly one match clause is supported (one sentinel per session)`);
39453
+ }
39454
+ const subject = spec.match[0].subject;
39455
+ const sessionId = parseSessionSubject(subject);
39456
+ if (!sessionId) {
39457
+ throw new Error(`${SESSION_SLUG}: subject "${subject}" is not a "session:<id>" subject`);
39458
+ }
39459
+ const info = deps2.getSession(sessionId);
39460
+ if (!info) {
39461
+ throw new Error(`${SESSION_SLUG}: no session "${sessionId}"`);
39462
+ }
39463
+ const ring = getRing(deps2.sessionEvents);
39464
+ const lastSeq = ring.nextSeq - 1;
39465
+ if (!info.alive) {
39466
+ synthesizeExitEvent(ring, subject, sessionId, "already-ended-at-create", info);
39467
+ }
39468
+ return { provider: SESSION_SLUG, remoteId: sessionId, cursor: formatCursor(ring, lastSeq), state: { sessionId } };
39469
+ },
39470
+ async attach(handle, _delivery) {
39471
+ const sessionId = identityOf(handle);
39472
+ if (!sessionId) return { ...handle };
39473
+ const subject = `session:${sessionId}`;
39474
+ const ring = getRing(deps2.sessionEvents);
39475
+ const lastSeq = resyncIfStale(
39476
+ ring,
39477
+ handle,
39478
+ sessionId,
39479
+ subject,
39480
+ deps2.getSession,
39481
+ "already-ended-at-attach",
39482
+ "gone-at-attach"
39483
+ );
39484
+ return { ...handle, cursor: formatCursor(ring, lastSeq) };
39485
+ },
39486
+ async cancel(_handle) {
39487
+ },
39488
+ async status(handle) {
39489
+ const sessionId = identityOf(handle);
39490
+ if (!sessionId) return { ok: false, detail: "no session identity on handle" };
39491
+ const info = deps2.getSession(sessionId);
39492
+ if (!info) return { ok: false, detail: `session "${sessionId}" no longer exists` };
39493
+ return { ok: true };
39494
+ },
39495
+ async poll(handle, limit) {
39496
+ const sessionId = identityOf(handle);
39497
+ if (!sessionId) return { events: [], cursor: handle.cursor ?? "" };
39498
+ const ring = getRing(deps2.sessionEvents);
39499
+ const subject = `session:${sessionId}`;
39500
+ const lastSeq = resyncIfStale(
39501
+ ring,
39502
+ handle,
39503
+ sessionId,
39504
+ subject,
39505
+ deps2.getSession,
39506
+ "already-ended-at-poll",
39507
+ "gone-at-poll"
39508
+ );
39509
+ const matching = ring.entries.filter((e) => e.seq > lastSeq && e.event.subject === subject);
39510
+ const slice = matching.slice(0, limit);
39511
+ const cursor = slice.length > 0 ? formatCursor(ring, slice[slice.length - 1].seq) : formatCursor(ring, lastSeq);
39512
+ return { events: slice.map((e) => e.event), cursor };
39513
+ },
39514
+ defaultTypes(subject) {
39515
+ return parseSessionSubject(subject) ? [...SESSION_DEFAULT_TYPES] : [];
39516
+ }
39517
+ };
39518
+ }
39519
+
39520
+ // src/sentinel-tools.ts
39521
+ function resolveUntil(input, defaultsToTerminal) {
39193
39522
  if (input === "never") return { kind: "never" };
39194
39523
  if (input === "subject_terminal") return { kind: "subject_terminal" };
39195
- return prSugar ? { kind: "subject_terminal" } : { kind: "never" };
39524
+ return defaultsToTerminal ? { kind: "subject_terminal" } : { kind: "never" };
39196
39525
  }
39197
39526
  async function createSentinelWatch(deps2, input) {
39198
39527
  if (!input.subject && !input.prUrl) {
@@ -39214,7 +39543,8 @@ async function createSentinelWatch(deps2, input) {
39214
39543
  } else {
39215
39544
  subject = input.subject;
39216
39545
  }
39217
- const until = resolveUntil(input.until, prSugar);
39546
+ const sessionSubject = !prSugar && parseSessionSubject(subject) !== void 0;
39547
+ const until = resolveUntil(input.until, prSugar || sessionSubject);
39218
39548
  const sessionId = input.sessionId ?? deps2.defaultSessionId;
39219
39549
  if (!sessionId) {
39220
39550
  return {
@@ -39226,10 +39556,10 @@ async function createSentinelWatch(deps2, input) {
39226
39556
  if (!deps2.isSessionAlive(sessionId)) {
39227
39557
  return { ok: false, error: "session_not_alive", message: `session "${sessionId}" is not alive` };
39228
39558
  }
39229
- const providerSlug = input.provider ?? await autoSelectProviderSlug({
39559
+ const providerSlug = input.provider ?? (sessionSubject ? SESSION_SLUG : await autoSelectProviderSlug({
39230
39560
  resolveProvider: deps2.resolveProvider,
39231
39561
  ...deps2.publicUrl ? { publicUrl: deps2.publicUrl } : {}
39232
- });
39562
+ }));
39233
39563
  const provider = await deps2.resolveProvider(providerSlug);
39234
39564
  if (!provider) {
39235
39565
  return { ok: false, error: "unknown_provider", message: `provider "${providerSlug}" is not available` };
@@ -39302,15 +39632,15 @@ function registerSentinelTools(server, opts) {
39302
39632
  const { store, runtime, resolveProvider, isSessionAlive, callerSessionId, activeIntervalMs } = opts;
39303
39633
  server.tool(
39304
39634
  "sentinel_watch",
39305
- "Watch a GitHub subject (a PR, by default) and deliver matching events as a `system`/`notice` message into a session's AIP-46 inbox. Provide either `prUrl` (sugar: `https://github.com/o/r/pull/N` -> subject `github:o/r#N`, the default PR type set, and `until: subject_terminal`) or a raw `subject` (e.g. `github:o/r#N`, `github:o/r`). `sessionId` defaults to the calling session. `provider` picks the backend: `local-gh` (poll every ~15-60s over the host's authenticated `gh` CLI, zero infra), `webhook` (near-real-time push via a GitHub repo hook; needs a public daemon URL \u2014 a named tunnel or AGENTPROTO_PUBLIC_URL \u2014 and a `gh` token with admin:repo_hook) or `agentpush` (hosted, durable subscription: events queue server-side across daemon downtime; needs an agentpush API key; one subject per sentinel); see `list_sentinel_adapters` for readiness. Omitted, it defaults to `agentpush` when set up, else `webhook` when a stable public URL exists and webhook is ready, else `local-gh`.",
39635
+ "Watch a subject \u2014 a GitHub PR/repo (default) or ANOTHER SESSION's own lifecycle \u2014 and deliver matching events as a `system`/`notice` message into a session's AIP-46 inbox. Provide either `prUrl` (sugar: `https://github.com/o/r/pull/N` -> subject `github:o/r#N`, the default PR type set, and `until: subject_terminal`) or a raw `subject`: a GitHub one (`github:o/r#N`, `github:o/r`), or `session:<id>` to watch another session \u2014 woken on its turn-end, awaiting-input, or exit, even if it never calls `message_parent`, with the watch self-expiring once it exits (`until` defaults to `subject_terminal` for this scheme too). Prefer this over `session_follow` for a single, known target that should auto-expire on exit; prefer `session_follow` for a broad selector (`all`/`cwdPrefix`) with coalesced digests across many sessions. `sessionId` (the DELIVERY target) defaults to the calling session \u2014 distinct from the `<id>` inside a `session:<id>` subject (the WATCHED session). `provider` picks the backend: `local-gh` (poll every ~15-60s over the host's authenticated `gh` CLI, zero infra), `webhook` (near-real-time push via a GitHub repo hook; needs a public daemon URL \u2014 a named tunnel or AGENTPROTO_PUBLIC_URL \u2014 and a `gh` token with admin:repo_hook), `agentpush` (hosted, durable subscription: events queue server-side across daemon downtime; needs an agentpush API key; one subject per sentinel) or `session` (in-process, zero infra, no credentials \u2014 the only backend that understands a `session:<id>` subject, and picked automatically for one); see `list_sentinel_adapters` for readiness. Omitted for a GitHub subject, it defaults to `agentpush` when set up, else `webhook` when a stable public URL exists and webhook is ready, else `local-gh`.",
39306
39636
  {
39307
- subject: z.string().optional().describe('Raw subject, e.g. "github:owner/repo#42". Mutually exclusive with prUrl.'),
39637
+ subject: z.string().optional().describe('Raw subject, e.g. "github:owner/repo#42" or "session:sess_abc123". Mutually exclusive with prUrl.'),
39308
39638
  prUrl: z.string().optional().describe("https://github.com/owner/repo/pull/N \u2014 sugar for subject+types+until."),
39309
- sessionId: z.string().optional().describe("Target session. Defaults to the calling session."),
39639
+ sessionId: z.string().optional().describe("Delivery target session. Defaults to the calling session."),
39310
39640
  types: z.array(z.string()).optional().describe("Type globs. Defaults to the provider's defaultTypes(subject)."),
39311
39641
  urgency: urgencyField2.optional().describe('Inbox delivery urgency. Default "next-turn".'),
39312
- until: untilField.optional().describe('Lifetime. Default "subject_terminal" for prUrl, "never" for a raw subject.'),
39313
- provider: z.string().optional().describe('Provider slug: "local-gh", "webhook" or "agentpush". Default: "agentpush" when set up, else "webhook" when a stable public URL is configured and webhook is ready, else "local-gh".')
39642
+ until: untilField.optional().describe('Lifetime. Default "subject_terminal" for prUrl and for a "session:<id>" subject, "never" for any other raw subject.'),
39643
+ provider: z.string().optional().describe('Provider slug: "local-gh", "webhook", "agentpush" or "session". Default for a GitHub subject: "agentpush" when set up, else "webhook" when a stable public URL is configured and webhook is ready, else "local-gh". A "session:<id>" subject always defaults to "session".')
39314
39644
  },
39315
39645
  async (input) => {
39316
39646
  const result = await createSentinelWatch(
@@ -39592,7 +39922,7 @@ function registerSessionFollowTools(server, opts) {
39592
39922
  const { store, hasSession, callerSessionId } = opts;
39593
39923
  server.tool(
39594
39924
  "session_follow",
39595
- "Wake a session (the follower, default: you) when OTHER sessions end a turn, await input, exit/crash, or get a PR opened/merged \u2014 no polling. `selector` picks sessions: `sessionIds`, `all: true` (evaluated at event time, so sessions spawned later are covered; root sessions only unless `rootOnly: false`), or `cwdPrefix`. `exclude` removes ids/labels. Events are coalesced within `batchMs` into one automatic digest message that never interrupts a busy follower. Your own events and your children's are never delivered to you. Upserts by `key` when given. Survives daemon restarts.",
39925
+ "Wake a session (the follower, default: you) when OTHER sessions end a turn, await input, exit/crash, or get a PR opened/merged \u2014 no polling. `selector` picks sessions: `sessionIds`, `all: true` (evaluated at event time, so sessions spawned later are covered; root sessions only unless `rootOnly: false`), or `cwdPrefix`. `exclude` removes ids/labels. Events are coalesced within `batchMs` into one automatic digest message that never interrupts a busy follower. Your own events are never delivered to you; your descendants' only with `excludeFollowerChildren: false`. Upserts by `key` when given. Survives daemon restarts.",
39596
39926
  {
39597
39927
  follower: z.string().optional().describe("Session to wake. Defaults to the calling session."),
39598
39928
  key: z.string().optional().describe("Stable key: re-posting the same key updates that follow in place."),
@@ -39601,7 +39931,9 @@ function registerSessionFollowTools(server, opts) {
39601
39931
  events: z.array(z.enum(FOLLOW_EVENTS)).optional().describe("Event kinds to deliver. Default: all of turn-end, awaiting-input, exited, crashed, pr-opened, pr-merged."),
39602
39932
  batchMs: z.number().int().optional().describe("Coalescing window in ms. Default 15000."),
39603
39933
  skipEmptyTurns: z.boolean().optional().describe("Drop silent no-op turns. Default true."),
39604
- excludeFollowerChildren: z.boolean().optional().describe("Ignore the follower's own descendants. Default true.")
39934
+ excludeFollowerChildren: z.boolean().optional().describe(
39935
+ "Ignore the follower's own descendants. Default true. `false` delivers them (any depth, even under `rootOnly`) \u2014 what a supervisor wants for its own children."
39936
+ )
39605
39937
  },
39606
39938
  async (input) => {
39607
39939
  const result = upsertSessionFollow(
@@ -43677,7 +44009,7 @@ function nextFireDate(cronInstance) {
43677
44009
  return cronInstance.nextRun() ?? null;
43678
44010
  }
43679
44011
  function createCronScheduler(opts) {
43680
- const { sessionEvents, registry, resolveAgentAdapter, dispatchTool, workspace } = opts;
44012
+ const { sessionEvents, registry, resolveAgentAdapter, daemonMcpUrl, dispatchTool, workspace } = opts;
43681
44013
  const observeTurn = opts.observeTurn;
43682
44014
  const observeTimeoutMs = opts.observeTimeoutMs ?? DEFAULT_OBSERVE_TIMEOUT_MS;
43683
44015
  const persistPath = opts.persistPath ?? DEFAULT_PERSIST_PATH3();
@@ -43780,7 +44112,10 @@ function createCronScheduler(opts) {
43780
44112
  // result, which only an agent-cli session has — never let
43781
44113
  // decideRestartStrategy hand back a PTY-native resume (e.g.
43782
44114
  // claude-code's `claude --resume`) here.
43783
- { forceAgentResume: true }
44115
+ {
44116
+ forceAgentResume: true,
44117
+ ...daemonMcpUrl ? { daemonMcpUrl } : {}
44118
+ }
43784
44119
  );
43785
44120
  action.sessionId = restarted.desc.id;
43786
44121
  await registry.sendPrompt(restarted.desc.id, action.prompt);
@@ -50568,16 +50903,7 @@ function projectSessionForHttp(session, fields) {
50568
50903
  var DEFAULT_ALLOWED_ORIGINS = [
50569
50904
  "http://localhost:*",
50570
50905
  "http://127.0.0.1:*",
50571
- "https://localhost:*",
50572
- // The canonical hosted agentproto panel (github.com/agentproto/cli-site,
50573
- // deployed at cli.agentproto.sh). It drives the user's OWN local daemon
50574
- // from the browser: read-only GETs (session list, SSE stream) are ungated
50575
- // and already work, but the /sessions/:id/pty WebSocket upgrade IS gated —
50576
- // so a PTY terminal in the panel 401s unless this first-party origin is
50577
- // trusted like localhost. A malicious page can't forge this Origin (the
50578
- // browser sets it), so the trust is scoped to agentproto's own panel,
50579
- // matching how guilde.work is trusted. Drop it via `strictOrigins`.
50580
- "https://cli.agentproto.sh"
50906
+ "https://localhost:*"
50581
50907
  ];
50582
50908
  var DEFAULT_FRAME_ANCESTORS = ["vscode-webview:", "vscode-file:"];
50583
50909
  var EVENTS_KEEPALIVE_MS = 25e3;
@@ -54588,7 +54914,8 @@ async function handleSessions(req, res, path, registry, resolveAgentAdapter, pty
54588
54914
  const restarted = await restartAgentSession(registry, resolveAgentAdapter, prev, {
54589
54915
  forceAgentResume: true,
54590
54916
  overrides,
54591
- ...listCatalogModels ? { listCatalogModels } : {}
54917
+ ...listCatalogModels ? { listCatalogModels } : {},
54918
+ ...daemonMcpUrl ? { daemonMcpUrl } : {}
54592
54919
  });
54593
54920
  json3(200, {
54594
54921
  ...sessionDescriptorForHttp(restarted.desc),
@@ -57876,7 +58203,10 @@ var BUILTIN_SENTINEL_PROVIDERS = {
57876
58203
  [WEBHOOK_SLUG]: () => webhookSentinelProvider(),
57877
58204
  [AGENTPUSH_SLUG]: (creds) => agentpushSentinelProvider({ creds })
57878
58205
  };
57879
- var BUILTIN_SENTINEL_SLUGS = [LOCAL_GH_SLUG, WEBHOOK_SLUG, AGENTPUSH_SLUG];
58206
+ var BUILTIN_SENTINEL_SLUGS = [LOCAL_GH_SLUG, WEBHOOK_SLUG, AGENTPUSH_SLUG, SESSION_SLUG];
58207
+ function configureSessionSentinelProvider(deps2) {
58208
+ BUILTIN_SENTINEL_PROVIDERS[SESSION_SLUG] = () => sessionSentinelProvider(deps2);
58209
+ }
57880
58210
  var slugToCamel3 = (slug) => slug.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
57881
58211
  function isSentinelProvider(x) {
57882
58212
  if (!x || typeof x !== "object") return false;
@@ -58681,11 +59011,11 @@ function followCoversSession(follow, sessionId, desc, registry, eventLabel) {
58681
59011
  if (follow.exclude?.sessionIds?.includes(sessionId)) return false;
58682
59012
  const label = desc?.label ?? eventLabel;
58683
59013
  if (label && follow.exclude?.labels?.includes(label)) return false;
58684
- if (follow.excludeFollowerChildren && descendsFrom(registry, desc, follow.follower)) {
58685
- return false;
58686
- }
59014
+ const ownDescendant = descendsFrom(registry, desc, follow.follower);
59015
+ if (ownDescendant && follow.excludeFollowerChildren) return false;
58687
59016
  const sel = follow.selector;
58688
59017
  if (sel.sessionIds?.includes(sessionId)) return true;
59018
+ if (ownDescendant && (sel.all === true || sel.cwdPrefix !== void 0 && sel.cwdPrefix !== "")) return true;
58689
59019
  const broad = sel.all === true || sel.cwdPrefix !== void 0 && sel.cwdPrefix !== "";
58690
59020
  if (!broad) return false;
58691
59021
  if (sel.cwdPrefix && !cwdUnder(desc?.cwd, sel.cwdPrefix)) return false;
@@ -64356,7 +64686,9 @@ var RemoteController = class {
64356
64686
  result.bearerToken = bearerToken;
64357
64687
  result.mcpEndpoint = mcpEndpoint;
64358
64688
  result.mcpConfigSnippet = buildMcpConfigSnippet2(mcpEndpoint, bearerToken);
64359
- result.phoneUrl = this.opts.isSessionChatInstalled?.() ? `${started.publicUrl}/apps/${SESSION_CHAT_APP_ID}/ui#token=${bearerToken}` : `https://cli.agentproto.sh/panel#daemon=${encodeURIComponent(started.publicUrl)}&token=${bearerToken}`;
64689
+ if (this.opts.isSessionChatInstalled?.()) {
64690
+ result.phoneUrl = `${started.publicUrl}/apps/${SESSION_CHAT_APP_ID}/ui#token=${bearerToken}`;
64691
+ }
64360
64692
  } else {
64361
64693
  const warning = `\u26A0 ${started.publicUrl} forwards to ${target.host}:${target.port} with NO authentication from the daemon \u2014 anyone with the URL can reach it. Ensure the upstream service enforces its own auth, and run remote_disable when done.`;
64362
64694
  result.warning = warning;
@@ -64450,7 +64782,7 @@ function registerRemoteTools(server, opts) {
64450
64782
  const { controller } = opts;
64451
64783
  server.tool(
64452
64784
  "remote_enable",
64453
- "Publish a local port to the internet via Cloudflare. By default exposes this gateway and gates it with a bearer token (returned ONCE in the response \u2014 store it; only its hash is persisted). Pass `targetPort` to tunnel a different local service (Supabase, dev server, \u2026); in that mode the daemon does not gate the proxied traffic \u2014 the upstream service handles its own auth. Returns the public URL plus a paste-ready `.mcp.json` snippet and a `phoneUrl` \u2014 a single link to open on a phone, with the token in a URL fragment (gateway mode only). Re-running while a tunnel is already up errors; call `remote_disable` first to rotate.",
64785
+ "Publish a local port to the internet via Cloudflare. By default exposes this gateway and gates it with a bearer token (returned ONCE in the response \u2014 store it; only its hash is persisted). Pass `targetPort` to tunnel a different local service (Supabase, dev server, \u2026); in that mode the daemon does not gate the proxied traffic \u2014 the upstream service handles its own auth. Returns the public URL plus a paste-ready `.mcp.json` snippet and a `phoneUrl` \u2014 a single link to open on a phone, with the token in a URL fragment (gateway mode, and only when the @agentik/session-chat app is installed). Re-running while a tunnel is already up errors; call `remote_disable` first to rotate.",
64454
64786
  {
64455
64787
  provider: z.enum(["quick"]).optional().describe(
64456
64788
  "Tunnel backend. `quick` = Cloudflare Quick Tunnel (no API key, ephemeral *.trycloudflare.com URL). Defaults to `quick`. Named-CF / Tailscale providers TBD."
@@ -66457,6 +66789,13 @@ var SENTINEL_CATALOG = [
66457
66789
  description: "Hosted event subscriptions over agentpush \u2014 GitHub App installs, Telegram, generic hooks. Events queue server-side and survive daemon downtime; delivered by poll (default, no public URL) or push. Needs an agentpush workspace API key (`setup_sentinel_provider`, or an imported `agentpush` MCP alias).",
66458
66790
  packageName: "@agentproto/runtime",
66459
66791
  hint: "any source \xB7 durable \xB7 needs API key"
66792
+ },
66793
+ {
66794
+ slug: SESSION_SLUG,
66795
+ name: "Session Lifecycle",
66796
+ description: 'In-process watcher over this daemon\'s own session lifecycle (turn-end, awaiting-input, exit) for a "session:<id>" subject. No credentials, no external system \u2014 wired up by the daemon itself, not configurable via `setup_sentinel_provider`.',
66797
+ packageName: "@agentproto/runtime",
66798
+ hint: "session \xB7 zero-infra \xB7 in-process"
66460
66799
  }
66461
66800
  ];
66462
66801
  function toSentinelInfo(handle) {
@@ -67108,7 +67447,8 @@ function makeRestartForRouting(deps2, config) {
67108
67447
  }
67109
67448
  const restarted = await restartPreferInPlace(deps2.sessions, deps2.resolveAgentAdapter, desc, {
67110
67449
  forceAgentResume: true,
67111
- allowDeliberateEnd: config.allowDeliberateEnd
67450
+ allowDeliberateEnd: config.allowDeliberateEnd,
67451
+ ...deps2.daemonMcpUrl ? { daemonMcpUrl: deps2.daemonMcpUrl } : {}
67112
67452
  });
67113
67453
  return restarted.desc.id;
67114
67454
  };
@@ -67399,6 +67739,20 @@ async function createGateway(opts) {
67399
67739
  }
67400
67740
  } : {}
67401
67741
  });
67742
+ configureSessionSentinelProvider({
67743
+ sessionEvents,
67744
+ getSession: (sessionId) => {
67745
+ const desc = sessions.get(sessionId);
67746
+ if (!desc) return void 0;
67747
+ return {
67748
+ alive: desc.alive === true,
67749
+ status: desc.status,
67750
+ ...desc.label ? { label: desc.label } : {},
67751
+ ...desc.endedReason ? { endedReason: desc.endedReason } : {},
67752
+ ...desc.exitCode !== void 0 ? { exitCode: desc.exitCode } : {}
67753
+ };
67754
+ }
67755
+ });
67402
67756
  const restartScheduler = createRestartScheduler({ registry: sessions, sessionEvents });
67403
67757
  const supervisor = createCompletionPolicySupervisor({
67404
67758
  registry: sessions,
@@ -67440,6 +67794,7 @@ async function createGateway(opts) {
67440
67794
  const cronScheduler = createCronScheduler({
67441
67795
  sessionEvents,
67442
67796
  registry: sessions,
67797
+ daemonMcpUrl,
67443
67798
  ...opts.resolveAgentAdapter ? { resolveAgentAdapter: opts.resolveAgentAdapter } : {},
67444
67799
  ...opts.listAgentAdapters ? { listAgentAdapters: opts.listAgentAdapters } : {},
67445
67800
  getAuthProfile: getAuthProfile,
@@ -67615,11 +67970,11 @@ async function createGateway(opts) {
67615
67970
  return desc.processAlive !== false;
67616
67971
  };
67617
67972
  const restartSentinelSession = makeRestartForRouting(
67618
- { sessions, resolveAgentAdapter: opts.resolveAgentAdapter },
67973
+ { sessions, resolveAgentAdapter: opts.resolveAgentAdapter, daemonMcpUrl },
67619
67974
  { name: "restartSentinelSession", allowDeliberateEnd: false }
67620
67975
  );
67621
67976
  const restartInboundSession = makeRestartForRouting(
67622
- { sessions, resolveAgentAdapter: opts.resolveAgentAdapter },
67977
+ { sessions, resolveAgentAdapter: opts.resolveAgentAdapter, daemonMcpUrl },
67623
67978
  { name: "restartInboundSession", allowDeliberateEnd: true }
67624
67979
  );
67625
67980
  const sentinelRuntime = createSentinelRuntime({