@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/config-schema.mjs.map +1 -1
- package/dist/config.mjs.map +1 -1
- package/dist/daemon-tool-names.d.ts +6 -0
- package/dist/daemon-tool-names.mjs +276 -0
- package/dist/daemon-tool-names.mjs.map +1 -0
- package/dist/index.d.ts +19 -12
- package/dist/index.mjs +411 -56
- package/dist/index.mjs.map +1 -1
- package/dist/session-presence.mjs.map +1 -1
- package/dist/{sessions-Cv4XNQXv.d.ts → sessions-BoZpBAdm.d.ts} +1 -1
- package/dist/user-presets.d.ts +1 -1
- package/package.json +15 -9
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
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
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 (
|
|
26605
|
-
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
39313
|
-
provider: z.string().optional().describe('Provider slug: "local-gh", "webhook" or "
|
|
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
|
|
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(
|
|
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
|
-
{
|
|
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
|
-
|
|
58685
|
-
|
|
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
|
-
|
|
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({
|