@agentproto/runtime 2.3.0 → 2.4.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/catalog-models.mjs +2 -1
- package/dist/catalog-models.mjs.map +1 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +230 -3
- package/dist/index.mjs +430 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -2573,7 +2573,7 @@ function resolveAuthSpec(input) {
|
|
|
2573
2573
|
}
|
|
2574
2574
|
if (!provider) return void 0;
|
|
2575
2575
|
const sub = input.descriptor.authSubscription;
|
|
2576
|
-
const supportsSub = sub !== void 0 && (gatewayRoute === void 0 || isNativeGatewayPreset);
|
|
2576
|
+
const supportsSub = (sub !== void 0 || input.descriptor.modelDerivedApiKey === true) && (gatewayRoute === void 0 || isNativeGatewayPreset);
|
|
2577
2577
|
const enforce = input.descriptor.authEnforce ?? "when-configured";
|
|
2578
2578
|
const external = supportsSub && sub?.external === true;
|
|
2579
2579
|
const subCredAvailable = input.subscriptionCredential !== void 0 || external && input.externalSubscriptionVerified === true;
|
|
@@ -2605,7 +2605,7 @@ function resolveAuthSpec(input) {
|
|
|
2605
2605
|
credentialSource = subCredAvailable ? "cli-local-login" : "none";
|
|
2606
2606
|
externalCredential = true;
|
|
2607
2607
|
} else if (mode === "subscription") {
|
|
2608
|
-
setEnv = sub
|
|
2608
|
+
setEnv = sub?.setEnv ?? apiKeyEnv;
|
|
2609
2609
|
credential = input.subscriptionCredential;
|
|
2610
2610
|
credentialSource = credential !== void 0 ? input.subscriptionCredentialSource ?? "explicit-config" : "none";
|
|
2611
2611
|
} else {
|
|
@@ -3679,7 +3679,8 @@ function resolveModelId(id) {
|
|
|
3679
3679
|
}
|
|
3680
3680
|
function methodsForDirect(descriptor) {
|
|
3681
3681
|
const methods = [];
|
|
3682
|
-
if (descriptor?.authSubscription
|
|
3682
|
+
if (descriptor?.authSubscription || descriptor?.modelDerivedApiKey)
|
|
3683
|
+
methods.push("oauth-bearer");
|
|
3683
3684
|
if (descriptor?.provider || descriptor?.modelDerivedApiKey) methods.push("api-key");
|
|
3684
3685
|
return methods;
|
|
3685
3686
|
}
|
|
@@ -7505,6 +7506,8 @@ function toSessionSummary(desc) {
|
|
|
7505
7506
|
lastOutputAt: desc.lastOutputAt,
|
|
7506
7507
|
lastActivityAt: desc.lastActivityAt,
|
|
7507
7508
|
processAlive: desc.processAlive,
|
|
7509
|
+
watchers: desc.watchers,
|
|
7510
|
+
childrenBusy: desc.childrenBusy,
|
|
7508
7511
|
label: desc.label,
|
|
7509
7512
|
title: desc.title,
|
|
7510
7513
|
renamedByUser: desc.renamedByUser,
|
|
@@ -7532,6 +7535,7 @@ function toSessionSummary(desc) {
|
|
|
7532
7535
|
turnsCompleted: desc.turnsCompleted,
|
|
7533
7536
|
busy: desc.busy,
|
|
7534
7537
|
blockedOn: desc.blockedOn,
|
|
7538
|
+
stalledSinceMs: desc.stalledSinceMs,
|
|
7535
7539
|
origin: desc.origin,
|
|
7536
7540
|
parentSessionId: desc.parentSessionId,
|
|
7537
7541
|
depth: desc.depth,
|
|
@@ -7659,6 +7663,28 @@ function createSessionsRegistry(opts) {
|
|
|
7659
7663
|
const sessionEvents = opts?.sessionEvents;
|
|
7660
7664
|
const resolveAgentAdapter = opts?.resolveAgentAdapter;
|
|
7661
7665
|
const sessions = /* @__PURE__ */ new Map();
|
|
7666
|
+
const watchersById = /* @__PURE__ */ new Map();
|
|
7667
|
+
const stampWatchers = (desc) => {
|
|
7668
|
+
desc.watchers = watchersById.get(desc.id) ?? 0;
|
|
7669
|
+
};
|
|
7670
|
+
const childrenBusyCounts = () => {
|
|
7671
|
+
const all = Array.from(sessions.values());
|
|
7672
|
+
const parentOf = new Map(all.map((s) => [s.desc.id, s.desc.parentSessionId]));
|
|
7673
|
+
const counts = /* @__PURE__ */ new Map();
|
|
7674
|
+
for (const s of all) {
|
|
7675
|
+
const d = s.desc;
|
|
7676
|
+
const midTurn = d.busy === true && (d.status === "running" || d.status === "starting");
|
|
7677
|
+
if (!midTurn) continue;
|
|
7678
|
+
const seen = /* @__PURE__ */ new Set([d.id]);
|
|
7679
|
+
let pid = d.parentSessionId;
|
|
7680
|
+
while (pid && !seen.has(pid)) {
|
|
7681
|
+
seen.add(pid);
|
|
7682
|
+
counts.set(pid, (counts.get(pid) ?? 0) + 1);
|
|
7683
|
+
pid = parentOf.get(pid);
|
|
7684
|
+
}
|
|
7685
|
+
}
|
|
7686
|
+
return counts;
|
|
7687
|
+
};
|
|
7662
7688
|
const pendingPermissions = /* @__PURE__ */ new Map();
|
|
7663
7689
|
let persistTimer = null;
|
|
7664
7690
|
let nextSubId = 1;
|
|
@@ -7722,6 +7748,17 @@ function createSessionsRegistry(opts) {
|
|
|
7722
7748
|
...rt.desc.endedReason ? { reason: rt.desc.endedReason } : {}
|
|
7723
7749
|
});
|
|
7724
7750
|
};
|
|
7751
|
+
const clearStalledFlag = (rt) => {
|
|
7752
|
+
if (rt.desc.stalledSinceMs === void 0) return;
|
|
7753
|
+
rt.desc.stalledSinceMs = void 0;
|
|
7754
|
+
schedulePersist();
|
|
7755
|
+
sessionEvents?.emit({
|
|
7756
|
+
type: "session:stall-cleared",
|
|
7757
|
+
sessionId: rt.desc.id,
|
|
7758
|
+
...rt.desc.label ? { label: rt.desc.label } : {},
|
|
7759
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
7760
|
+
});
|
|
7761
|
+
};
|
|
7725
7762
|
const refreshAwaitingPermission = (rt) => {
|
|
7726
7763
|
let has = false;
|
|
7727
7764
|
for (const p of pendingPermissions.values()) {
|
|
@@ -8503,6 +8540,7 @@ ${message}`;
|
|
|
8503
8540
|
rt.desc.awaitingInput = false;
|
|
8504
8541
|
rt.desc.awaitingQuestion = void 0;
|
|
8505
8542
|
releaseBlockedOn(rt.desc);
|
|
8543
|
+
clearStalledFlag(rt);
|
|
8506
8544
|
let turnCompleted = false;
|
|
8507
8545
|
let sawTurnEnd = false;
|
|
8508
8546
|
let abnormalReason;
|
|
@@ -8560,6 +8598,7 @@ ${message}`;
|
|
|
8560
8598
|
rt.desc.busy = false;
|
|
8561
8599
|
rt.emitter.emit("busy", false);
|
|
8562
8600
|
releaseBlockedOn(rt.desc);
|
|
8601
|
+
clearStalledFlag(rt);
|
|
8563
8602
|
if (pendingToolCallIds.size > 0) {
|
|
8564
8603
|
for (const toolCallId of pendingToolCallIds) {
|
|
8565
8604
|
const synthetic = {
|
|
@@ -9501,13 +9540,17 @@ ${message}`;
|
|
|
9501
9540
|
const rt = sessions.get(id);
|
|
9502
9541
|
if (!rt) return;
|
|
9503
9542
|
rt.desc.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9543
|
+
clearStalledFlag(rt);
|
|
9504
9544
|
schedulePersist();
|
|
9505
9545
|
},
|
|
9506
9546
|
list(opts2) {
|
|
9507
9547
|
const includeArchived = opts2?.includeArchived ?? false;
|
|
9548
|
+
const childrenBusy = childrenBusyCounts();
|
|
9508
9549
|
return Array.from(sessions.values()).map((s) => s.desc).filter((desc) => includeArchived || !desc.archived).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((desc) => {
|
|
9509
9550
|
stampProcessAlive(desc);
|
|
9510
9551
|
stampInterrupted(desc);
|
|
9552
|
+
stampWatchers(desc);
|
|
9553
|
+
desc.childrenBusy = childrenBusy.get(desc.id) ?? 0;
|
|
9511
9554
|
return desc;
|
|
9512
9555
|
});
|
|
9513
9556
|
},
|
|
@@ -9515,11 +9558,14 @@ ${message}`;
|
|
|
9515
9558
|
const includeArchived = opts2?.includeArchived ?? false;
|
|
9516
9559
|
const limit = Math.max(1, Math.min(200, opts2?.limit ?? 50));
|
|
9517
9560
|
const offset = Math.max(0, opts2?.offset ?? 0);
|
|
9561
|
+
const childrenBusy = childrenBusyCounts();
|
|
9518
9562
|
const all = Array.from(sessions.values()).map((s) => s.desc).filter((desc) => includeArchived || !desc.archived).sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
9519
9563
|
const slice = all.slice(offset, offset + limit);
|
|
9520
9564
|
const summaries = slice.map((desc) => {
|
|
9521
9565
|
stampProcessAlive(desc);
|
|
9522
9566
|
stampInterrupted(desc);
|
|
9567
|
+
stampWatchers(desc);
|
|
9568
|
+
desc.childrenBusy = childrenBusy.get(desc.id) ?? 0;
|
|
9523
9569
|
return toSessionSummary(desc);
|
|
9524
9570
|
});
|
|
9525
9571
|
return { summaries, total: all.length };
|
|
@@ -9529,9 +9575,19 @@ ${message}`;
|
|
|
9529
9575
|
if (desc) {
|
|
9530
9576
|
stampProcessAlive(desc);
|
|
9531
9577
|
stampInterrupted(desc);
|
|
9578
|
+
stampWatchers(desc);
|
|
9579
|
+
desc.childrenBusy = childrenBusyCounts().get(desc.id) ?? 0;
|
|
9532
9580
|
}
|
|
9533
9581
|
return desc;
|
|
9534
9582
|
},
|
|
9583
|
+
incWatchers(id) {
|
|
9584
|
+
watchersById.set(id, (watchersById.get(id) ?? 0) + 1);
|
|
9585
|
+
},
|
|
9586
|
+
decWatchers(id) {
|
|
9587
|
+
const next = (watchersById.get(id) ?? 0) - 1;
|
|
9588
|
+
if (next > 0) watchersById.set(id, next);
|
|
9589
|
+
else watchersById.delete(id);
|
|
9590
|
+
},
|
|
9535
9591
|
attach(id, onLine) {
|
|
9536
9592
|
const rt = sessions.get(id);
|
|
9537
9593
|
if (!rt) return null;
|
|
@@ -9698,6 +9754,30 @@ ${message}`;
|
|
|
9698
9754
|
emitExited(rt);
|
|
9699
9755
|
return true;
|
|
9700
9756
|
},
|
|
9757
|
+
markStalled(id, stalledSinceMs) {
|
|
9758
|
+
const rt = sessions.get(id);
|
|
9759
|
+
if (!rt) return false;
|
|
9760
|
+
if (rt.desc.kind !== "agent-cli" || rt.desc.status !== "running") return false;
|
|
9761
|
+
if (rt.desc.busy !== true || rt.desc.blockedOn !== void 0) return false;
|
|
9762
|
+
if (rt.desc.stalledSinceMs !== void 0) return false;
|
|
9763
|
+
rt.desc.stalledSinceMs = stalledSinceMs;
|
|
9764
|
+
schedulePersist();
|
|
9765
|
+
sessionEvents?.emit({
|
|
9766
|
+
type: "session:stalled",
|
|
9767
|
+
sessionId: rt.desc.id,
|
|
9768
|
+
stalledSinceMs,
|
|
9769
|
+
...rt.desc.label ? { label: rt.desc.label } : {},
|
|
9770
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
9771
|
+
});
|
|
9772
|
+
return true;
|
|
9773
|
+
},
|
|
9774
|
+
clearStalled(id) {
|
|
9775
|
+
const rt = sessions.get(id);
|
|
9776
|
+
if (!rt) return false;
|
|
9777
|
+
const wasFlagged = rt.desc.stalledSinceMs !== void 0;
|
|
9778
|
+
clearStalledFlag(rt);
|
|
9779
|
+
return wasFlagged;
|
|
9780
|
+
},
|
|
9701
9781
|
isResuming(id) {
|
|
9702
9782
|
return !!sessions.get(id)?.resumePromise;
|
|
9703
9783
|
},
|
|
@@ -11008,6 +11088,7 @@ function registerAgentTools(server, opts) {
|
|
|
11008
11088
|
buildOrchestratorMcp,
|
|
11009
11089
|
callerScope,
|
|
11010
11090
|
callerSessionId,
|
|
11091
|
+
mcpBridgeOrigin,
|
|
11011
11092
|
webhookNotifier,
|
|
11012
11093
|
daemonMcpUrl,
|
|
11013
11094
|
loadRoleRegistry: loadRoleRegistry2,
|
|
@@ -11285,6 +11366,12 @@ function registerAgentTools(server, opts) {
|
|
|
11285
11366
|
{
|
|
11286
11367
|
...spawnInput,
|
|
11287
11368
|
adapter,
|
|
11369
|
+
// Auto-stamp the source channel (#session-visibility): when the
|
|
11370
|
+
// caller didn't pass an explicit `origin`, fall back to the connecting
|
|
11371
|
+
// client's `?origin=` label so a bridge-client spawn (cowork/vscode/
|
|
11372
|
+
// codex) is attributed instead of landing as a bare root. An explicit
|
|
11373
|
+
// `input.origin` (already in `spawnInput`) always wins.
|
|
11374
|
+
...!spawnInput.origin && mcpBridgeOrigin ? { origin: mcpBridgeOrigin } : {},
|
|
11288
11375
|
// The trusted caller id (from `?callerSessionId=`) becomes the
|
|
11289
11376
|
// implicit auto-parent — attach-by-default without the caller
|
|
11290
11377
|
// passing its own id. An explicit `parentSessionId` still outranks
|
|
@@ -12531,6 +12618,15 @@ async function restartAgentSession(registry, resolveAgentAdapter, prev, opts = {
|
|
|
12531
12618
|
...accessProfileEcho ? { accessProfile: accessProfileEcho } : {},
|
|
12532
12619
|
...effMode ? { mode: effMode } : {},
|
|
12533
12620
|
...resolved.commandPreview ? { commandPreview: resolved.commandPreview } : {},
|
|
12621
|
+
// Lineage carry-forward (#session-visibility). A restart is a NEW
|
|
12622
|
+
// descriptor, but it is the same logical session continued — so its
|
|
12623
|
+
// origin (which channel spawned it: cowork/vscode/codex/cron) and its
|
|
12624
|
+
// parent/depth must survive, exactly as continue-fresh already carries
|
|
12625
|
+
// them (session-continue-fresh.ts). Dropping them here is what left a
|
|
12626
|
+
// restarted session a bare top-level root with no source trace.
|
|
12627
|
+
...prev.origin ? { origin: prev.origin } : {},
|
|
12628
|
+
...prev.parentSessionId ? { parentSessionId: prev.parentSessionId } : {},
|
|
12629
|
+
...prev.depth !== void 0 ? { depth: prev.depth } : {},
|
|
12534
12630
|
// Verifiability echo (never the credential) — see the auth
|
|
12535
12631
|
// resolution block above. Absent when no credential resolved,
|
|
12536
12632
|
// same as session-spawn.ts.
|
|
@@ -14106,6 +14202,13 @@ function registerSessionTools(rawServer, opts) {
|
|
|
14106
14202
|
rows: input.rows ?? 24,
|
|
14107
14203
|
...prev.name ? { name: prev.name } : {},
|
|
14108
14204
|
...prev.label ? { label: prev.label } : {},
|
|
14205
|
+
// Lineage carry-forward (#session-visibility) — same reasoning as
|
|
14206
|
+
// the agent branch in session-restart-core.ts: a restart keeps the
|
|
14207
|
+
// logical session's origin/parent/depth rather than resetting it to
|
|
14208
|
+
// a bare root.
|
|
14209
|
+
...prev.origin ? { origin: prev.origin } : {},
|
|
14210
|
+
...prev.parentSessionId ? { parentSessionId: prev.parentSessionId } : {},
|
|
14211
|
+
...prev.depth !== void 0 ? { depth: prev.depth } : {},
|
|
14109
14212
|
resumedFrom: prev.id,
|
|
14110
14213
|
resumeVia: describeResumePath(augmented)
|
|
14111
14214
|
});
|
|
@@ -19676,11 +19779,13 @@ async function monitorSessionWait(opts) {
|
|
|
19676
19779
|
return new Promise((resolve22) => {
|
|
19677
19780
|
const unsubs = [];
|
|
19678
19781
|
let settled = false;
|
|
19782
|
+
for (const id of resolvedIds) registry.incWatchers(id);
|
|
19679
19783
|
const finish = (result) => {
|
|
19680
19784
|
if (settled) return;
|
|
19681
19785
|
settled = true;
|
|
19682
19786
|
clearTimeout(timer);
|
|
19683
19787
|
for (const u of unsubs) u();
|
|
19788
|
+
for (const id of resolvedIds) registry.decWatchers(id);
|
|
19684
19789
|
resolve22(result);
|
|
19685
19790
|
};
|
|
19686
19791
|
const relevantTypes = targetEvent === "any" ? ["session:turn-end", "session:awaiting-input", "session:exited"] : targetEvent === "turn-end" ? ["session:turn-end", "session:awaiting-input"] : targetEvent === "awaiting-input" ? ["session:awaiting-input"] : ["session:exited"];
|
|
@@ -21039,11 +21144,18 @@ async function startHttpServer(opts) {
|
|
|
21039
21144
|
const raw = new URLSearchParams(url.slice(qIdx + 1)).get("callerSessionId");
|
|
21040
21145
|
return raw && raw.length > 0 ? raw : void 0;
|
|
21041
21146
|
}
|
|
21147
|
+
function parseOriginQuery(url) {
|
|
21148
|
+
const qIdx = url.indexOf("?");
|
|
21149
|
+
if (qIdx === -1) return void 0;
|
|
21150
|
+
const raw = new URLSearchParams(url.slice(qIdx + 1)).get("origin");
|
|
21151
|
+
return raw && raw.length > 0 ? raw : void 0;
|
|
21152
|
+
}
|
|
21042
21153
|
async function handleMcp(req, res) {
|
|
21043
21154
|
if (!authorizeMcp(req, res)) return;
|
|
21044
21155
|
const denyTools = parseDenyToolsQuery(req.url ?? "");
|
|
21045
21156
|
const callerSessionId = parseCallerSessionIdQuery(req.url ?? "");
|
|
21046
|
-
const
|
|
21157
|
+
const origin = parseOriginQuery(req.url ?? "");
|
|
21158
|
+
const server2 = await opts.mcpServerFactory(denyTools, callerSessionId, origin);
|
|
21047
21159
|
await serveMcp(req, res, server2);
|
|
21048
21160
|
}
|
|
21049
21161
|
async function handleOrchestratorMcp(req, res) {
|
|
@@ -21093,7 +21205,8 @@ async function startHttpServer(opts) {
|
|
|
21093
21205
|
resumeSessionsOnBoot: opts.meta.resumeSessionsOnBoot === true,
|
|
21094
21206
|
idleReapAfterMs: opts.meta.idleReapAfterMs ?? 0,
|
|
21095
21207
|
crashDetectIntervalMs: opts.meta.crashDetectIntervalMs ?? 0,
|
|
21096
|
-
restartSweepIntervalMs: opts.meta.restartSweepIntervalMs ?? 0
|
|
21208
|
+
restartSweepIntervalMs: opts.meta.restartSweepIntervalMs ?? 0,
|
|
21209
|
+
turnStallAfterMs: opts.meta.turnStallAfterMs ?? 0
|
|
21097
21210
|
})
|
|
21098
21211
|
);
|
|
21099
21212
|
}
|
|
@@ -24539,6 +24652,49 @@ function runCrashDetectPass(opts) {
|
|
|
24539
24652
|
return summary;
|
|
24540
24653
|
}
|
|
24541
24654
|
|
|
24655
|
+
// src/stall-watchdog.ts
|
|
24656
|
+
function lastActivityMsOf(desc) {
|
|
24657
|
+
const tsStr = desc.lastActivityAt ?? desc.startedAt;
|
|
24658
|
+
const ts = tsStr ? Date.parse(tsStr) : Number.NaN;
|
|
24659
|
+
return Number.isFinite(ts) ? ts : null;
|
|
24660
|
+
}
|
|
24661
|
+
function isStallCandidate(desc, nowMs, thresholdMs) {
|
|
24662
|
+
if (desc.kind !== "agent-cli") return false;
|
|
24663
|
+
if (desc.status !== "running") return false;
|
|
24664
|
+
if (desc.busy !== true) return false;
|
|
24665
|
+
if (desc.blockedOn !== void 0) return false;
|
|
24666
|
+
if (desc.stalledSinceMs !== void 0) return false;
|
|
24667
|
+
const lastMs = lastActivityMsOf(desc);
|
|
24668
|
+
if (lastMs === null) return false;
|
|
24669
|
+
return nowMs - lastMs > thresholdMs;
|
|
24670
|
+
}
|
|
24671
|
+
function runStallWatchdogPass(opts) {
|
|
24672
|
+
const { registry, isServed } = opts;
|
|
24673
|
+
const thresholdMs = opts.turnStallAfterMs;
|
|
24674
|
+
if (!thresholdMs || thresholdMs <= 0) {
|
|
24675
|
+
return { enabled: false, candidates: 0, stalled: 0, ids: [] };
|
|
24676
|
+
}
|
|
24677
|
+
const nowMs = opts.now ? opts.now() : Date.now();
|
|
24678
|
+
const all = registry.list({ includeArchived: true });
|
|
24679
|
+
const candidates = all.filter(
|
|
24680
|
+
(d) => (isServed?.(d) ?? true) && isStallCandidate(d, nowMs, thresholdMs)
|
|
24681
|
+
);
|
|
24682
|
+
const summary = {
|
|
24683
|
+
enabled: true,
|
|
24684
|
+
candidates: candidates.length,
|
|
24685
|
+
stalled: 0,
|
|
24686
|
+
ids: []
|
|
24687
|
+
};
|
|
24688
|
+
for (const d of candidates) {
|
|
24689
|
+
const lastMs = lastActivityMsOf(d) ?? nowMs;
|
|
24690
|
+
if (registry.markStalled(d.id, lastMs)) {
|
|
24691
|
+
summary.stalled++;
|
|
24692
|
+
summary.ids.push(d.id);
|
|
24693
|
+
}
|
|
24694
|
+
}
|
|
24695
|
+
return summary;
|
|
24696
|
+
}
|
|
24697
|
+
|
|
24542
24698
|
// src/restart-scheduler.ts
|
|
24543
24699
|
function isEligibleForRestart(desc) {
|
|
24544
24700
|
const policy = desc.restartPolicy;
|
|
@@ -25638,6 +25794,13 @@ function createAppRegistry(opts) {
|
|
|
25638
25794
|
listApps() {
|
|
25639
25795
|
return [...state.apps];
|
|
25640
25796
|
},
|
|
25797
|
+
removeApp(appId) {
|
|
25798
|
+
const idx = state.apps.findIndex((a) => a.appId === appId);
|
|
25799
|
+
if (idx === -1) return void 0;
|
|
25800
|
+
const [removed] = state.apps.splice(idx, 1);
|
|
25801
|
+
persist();
|
|
25802
|
+
return removed;
|
|
25803
|
+
},
|
|
25641
25804
|
createRun(input) {
|
|
25642
25805
|
const run = {
|
|
25643
25806
|
appRunId: `apprun_${randomUUID()}`,
|
|
@@ -25694,6 +25857,31 @@ function createAppRegistry(opts) {
|
|
|
25694
25857
|
}
|
|
25695
25858
|
};
|
|
25696
25859
|
}
|
|
25860
|
+
var EMPTY_CATALOG = { apps: [] };
|
|
25861
|
+
function defaultAppCatalogPath() {
|
|
25862
|
+
return join(homedir(), ".agentproto", "app-catalog.json");
|
|
25863
|
+
}
|
|
25864
|
+
async function loadAppCatalogFile(path) {
|
|
25865
|
+
const catalogPath = path ?? defaultAppCatalogPath();
|
|
25866
|
+
let raw;
|
|
25867
|
+
try {
|
|
25868
|
+
raw = await readFile(catalogPath, "utf8");
|
|
25869
|
+
} catch {
|
|
25870
|
+
return EMPTY_CATALOG;
|
|
25871
|
+
}
|
|
25872
|
+
try {
|
|
25873
|
+
const parsed = JSON.parse(raw);
|
|
25874
|
+
if (!Array.isArray(parsed.apps)) return EMPTY_CATALOG;
|
|
25875
|
+
const apps = parsed.apps.filter((e) => {
|
|
25876
|
+
if (typeof e !== "object" || e === null) return false;
|
|
25877
|
+
const rec = e;
|
|
25878
|
+
return typeof rec.appId === "string" && typeof rec.dir === "string";
|
|
25879
|
+
});
|
|
25880
|
+
return { apps };
|
|
25881
|
+
} catch {
|
|
25882
|
+
return EMPTY_CATALOG;
|
|
25883
|
+
}
|
|
25884
|
+
}
|
|
25697
25885
|
|
|
25698
25886
|
// src/app-tools.ts
|
|
25699
25887
|
var DEFAULT_AGENT_ADAPTER = "mastra-agent";
|
|
@@ -25726,7 +25914,12 @@ async function readAppRefs(dir) {
|
|
|
25726
25914
|
const toRefs = (v) => Array.isArray(v) ? v.filter(
|
|
25727
25915
|
(e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.path === "string"
|
|
25728
25916
|
).map((e) => ({ id: e.id, path: resolveRef(dir, e.path) })) : [];
|
|
25729
|
-
|
|
25917
|
+
let ui;
|
|
25918
|
+
if (typeof data.ui === "object" && data.ui !== null && typeof data.ui.path === "string") {
|
|
25919
|
+
const uiData = data.ui;
|
|
25920
|
+
ui = { ...uiData, path: resolveRef(dir, uiData.path) };
|
|
25921
|
+
}
|
|
25922
|
+
return { agents: toRefs(data.agents), workflows: toRefs(data.workflows), ...ui ? { ui } : {} };
|
|
25730
25923
|
}
|
|
25731
25924
|
async function performInstall(dir, appRegistry, listRegisteredToolIds, resolveAgentAdapter) {
|
|
25732
25925
|
let handle;
|
|
@@ -25764,20 +25957,31 @@ async function performInstall(dir, appRegistry, listRegisteredToolIds, resolveAg
|
|
|
25764
25957
|
const unvalidatedAgentTools = [
|
|
25765
25958
|
...new Set(handle.agents.flatMap((e) => (e.agent.tools ?? []).map(refIdOf)))
|
|
25766
25959
|
];
|
|
25960
|
+
const ui = refs.ui ? {
|
|
25961
|
+
path: refs.ui.path,
|
|
25962
|
+
...handle.ui?.title !== void 0 ? { title: handle.ui.title } : {},
|
|
25963
|
+
...handle.ui?.description !== void 0 ? { description: handle.ui.description } : {},
|
|
25964
|
+
...handle.ui?.tools !== void 0 ? { tools: handle.ui.tools } : {},
|
|
25965
|
+
...handle.ui?.csp !== void 0 ? { csp: handle.ui.csp } : {}
|
|
25966
|
+
} : void 0;
|
|
25767
25967
|
const record2 = appRegistry.upsertApp({
|
|
25768
25968
|
appId: handle.id,
|
|
25769
25969
|
dir,
|
|
25770
25970
|
...handle.version ? { version: handle.version } : {},
|
|
25771
25971
|
...handle.name ? { name: handle.name } : {},
|
|
25972
|
+
...handle.description ? { description: handle.description } : {},
|
|
25772
25973
|
agents: refs.agents,
|
|
25773
25974
|
workflows: refs.workflows,
|
|
25774
25975
|
unvalidatedAgentTools,
|
|
25775
|
-
...handle.requires ? { requires: handle.requires } : {}
|
|
25976
|
+
...handle.requires ? { requires: handle.requires } : {},
|
|
25977
|
+
...ui ? { ui } : {},
|
|
25978
|
+
...handle.artifacts ? { artifacts: handle.artifacts } : {},
|
|
25979
|
+
...handle.dev ? { dev: handle.dev } : {}
|
|
25776
25980
|
});
|
|
25777
25981
|
return { ok: true, record: record2 };
|
|
25778
25982
|
}
|
|
25779
25983
|
function registerAppTools(server, opts) {
|
|
25780
|
-
const { registry, resolveAgentAdapter, listRegisteredToolIds, workflowRunner } = opts;
|
|
25984
|
+
const { registry, resolveAgentAdapter, listRegisteredToolIds, workflowRunner, dispatchTool, callImportedTool } = opts;
|
|
25781
25985
|
const appRegistry = opts.appRegistry ?? createAppRegistry({
|
|
25782
25986
|
...opts.persistPath !== void 0 ? { persistPath: opts.persistPath } : {},
|
|
25783
25987
|
...opts.persist !== void 0 ? { persist: opts.persist } : {}
|
|
@@ -26026,6 +26230,167 @@ function registerAppTools(server, opts) {
|
|
|
26026
26230
|
return textResult(result);
|
|
26027
26231
|
}
|
|
26028
26232
|
);
|
|
26233
|
+
server.tool(
|
|
26234
|
+
"app_tool_call",
|
|
26235
|
+
"Call one of an installed app's UI-exposed tools \u2014 the allowlist set at `defineApp({ ui: { tools: [...] } })` time (`app_install`'s `record.ui.tools`). A tool id prefixed `imported:<alias>/<toolName>` dispatches through an imported MCP server (same proxy `mcp_imported_call` uses); every other id dispatches through the daemon's own registered tools (same reach-in routine/cron `target.tool` dispatch uses).",
|
|
26236
|
+
{
|
|
26237
|
+
appId: z.string(),
|
|
26238
|
+
tool: z.string().describe("A tool id from the app's `ui.tools` allowlist."),
|
|
26239
|
+
args: z.record(z.string(), z.unknown()).optional().describe("Tool arguments. Default: empty object.")
|
|
26240
|
+
},
|
|
26241
|
+
async (input) => {
|
|
26242
|
+
const installed = appRegistry.getApp(input.appId);
|
|
26243
|
+
if (!installed || !installed.ui) {
|
|
26244
|
+
return errorResult(`app_tool_call: app "${input.appId}" is not installed or has no UI.`);
|
|
26245
|
+
}
|
|
26246
|
+
const allowlist = installed.ui.tools ?? [];
|
|
26247
|
+
if (!allowlist.includes(input.tool)) {
|
|
26248
|
+
return errorResult(
|
|
26249
|
+
`app_tool_call: tool "${input.tool}" is not in app "${input.appId}"'s ui.tools allowlist: ${allowlist.length > 0 ? allowlist.join(", ") : "(empty)"}`
|
|
26250
|
+
);
|
|
26251
|
+
}
|
|
26252
|
+
const args = input.args ?? {};
|
|
26253
|
+
try {
|
|
26254
|
+
if (input.tool.startsWith("imported:")) {
|
|
26255
|
+
if (!callImportedTool) return notEnabled("app_tool_call");
|
|
26256
|
+
const rest = input.tool.slice("imported:".length);
|
|
26257
|
+
const slash = rest.indexOf("/");
|
|
26258
|
+
if (slash === -1) {
|
|
26259
|
+
return errorResult(
|
|
26260
|
+
`app_tool_call: malformed imported tool id "${input.tool}" \u2014 expected "imported:<alias>/<toolName>".`
|
|
26261
|
+
);
|
|
26262
|
+
}
|
|
26263
|
+
const result2 = await callImportedTool(rest.slice(0, slash), rest.slice(slash + 1), args);
|
|
26264
|
+
return textResult(result2);
|
|
26265
|
+
}
|
|
26266
|
+
if (!dispatchTool) return notEnabled("app_tool_call");
|
|
26267
|
+
const result = await dispatchTool(input.tool, args);
|
|
26268
|
+
return textResult(result);
|
|
26269
|
+
} catch (err) {
|
|
26270
|
+
return errorResult(`app_tool_call: ${err instanceof Error ? err.message : String(err)}`);
|
|
26271
|
+
}
|
|
26272
|
+
}
|
|
26273
|
+
);
|
|
26274
|
+
server.tool(
|
|
26275
|
+
"app_uninstall",
|
|
26276
|
+
"Remove an installed app's record. Refuses if the app is applied to any scope (unapply first via app_unapply) or has a running app_run (stop it first via app_stop).",
|
|
26277
|
+
{ appId: z.string() },
|
|
26278
|
+
async (input) => {
|
|
26279
|
+
const applied = appRegistry.listApplied().filter((m) => m.appId === input.appId);
|
|
26280
|
+
if (applied.length > 0) {
|
|
26281
|
+
return errorResult(
|
|
26282
|
+
`app_uninstall: app "${input.appId}" is applied to scope(s) ${applied.map((m) => m.scopeId).join(", ")} \u2014 unapply from scopes first.`
|
|
26283
|
+
);
|
|
26284
|
+
}
|
|
26285
|
+
const runningRuns = appRegistry.listRuns().filter((r) => r.appId === input.appId && r.status === "running");
|
|
26286
|
+
if (runningRuns.length > 0) {
|
|
26287
|
+
return errorResult(
|
|
26288
|
+
`app_uninstall: app "${input.appId}" has running app_run(s) ${runningRuns.map((r) => r.appRunId).join(", ")} \u2014 stop app runs first.`
|
|
26289
|
+
);
|
|
26290
|
+
}
|
|
26291
|
+
const removed = appRegistry.removeApp(input.appId);
|
|
26292
|
+
if (!removed) {
|
|
26293
|
+
return errorResult(`app_uninstall: no installed app "${input.appId}".`);
|
|
26294
|
+
}
|
|
26295
|
+
return textResult({ appId: removed.appId });
|
|
26296
|
+
}
|
|
26297
|
+
);
|
|
26298
|
+
server.tool(
|
|
26299
|
+
"app_catalog",
|
|
26300
|
+
"List browsable apps from the catalog file (default `~/.agentproto/app-catalog.json`, tolerates a missing file), merged with installed-app status \u2014 every entry reports `installed` and `hasUi`. Installed apps absent from the catalog file are included too.",
|
|
26301
|
+
{
|
|
26302
|
+
scopeId: z.string().optional().describe("Reserved for future scope-aware filtering. Currently unused.")
|
|
26303
|
+
},
|
|
26304
|
+
async () => {
|
|
26305
|
+
const catalog = await loadAppCatalogFile(opts.catalogPath);
|
|
26306
|
+
const installedApps = appRegistry.listApps();
|
|
26307
|
+
const installedById = new Map(installedApps.map((a) => [a.appId, a]));
|
|
26308
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26309
|
+
const entries = catalog.apps.map((entry) => {
|
|
26310
|
+
const installed = installedById.get(entry.appId);
|
|
26311
|
+
seen.add(entry.appId);
|
|
26312
|
+
const name = entry.name ?? installed?.name;
|
|
26313
|
+
const description = entry.description ?? installed?.description;
|
|
26314
|
+
return {
|
|
26315
|
+
appId: entry.appId,
|
|
26316
|
+
...name ? { name } : {},
|
|
26317
|
+
...description ? { description } : {},
|
|
26318
|
+
dir: entry.dir,
|
|
26319
|
+
...entry.category ? { category: entry.category } : {},
|
|
26320
|
+
installed: installed !== void 0,
|
|
26321
|
+
hasUi: installed?.ui !== void 0
|
|
26322
|
+
};
|
|
26323
|
+
});
|
|
26324
|
+
for (const app of installedApps) {
|
|
26325
|
+
if (seen.has(app.appId)) continue;
|
|
26326
|
+
entries.push({
|
|
26327
|
+
appId: app.appId,
|
|
26328
|
+
...app.name ? { name: app.name } : {},
|
|
26329
|
+
...app.description ? { description: app.description } : {},
|
|
26330
|
+
dir: app.dir,
|
|
26331
|
+
installed: true,
|
|
26332
|
+
hasUi: app.ui !== void 0
|
|
26333
|
+
});
|
|
26334
|
+
}
|
|
26335
|
+
return textResult(entries);
|
|
26336
|
+
}
|
|
26337
|
+
);
|
|
26338
|
+
}
|
|
26339
|
+
function appUiToolId(appId) {
|
|
26340
|
+
const slug = appId.replace(/^@[^/]+\//, "").replace(/[^a-z0-9]/g, "_");
|
|
26341
|
+
return `app_ui_${slug}`;
|
|
26342
|
+
}
|
|
26343
|
+
function createUiHtmlCache() {
|
|
26344
|
+
const cache = /* @__PURE__ */ new Map();
|
|
26345
|
+
return {
|
|
26346
|
+
async get(path, version) {
|
|
26347
|
+
const cached = cache.get(path);
|
|
26348
|
+
if (cached && cached.version === version) return cached.html;
|
|
26349
|
+
const html = await readFile(path, "utf8");
|
|
26350
|
+
cache.set(path, { version, html });
|
|
26351
|
+
return html;
|
|
26352
|
+
}
|
|
26353
|
+
};
|
|
26354
|
+
}
|
|
26355
|
+
async function makeInstalledAppUiApps(appRegistry, cache, existingToolNames) {
|
|
26356
|
+
const apps = [];
|
|
26357
|
+
const seen = new Set(existingToolNames);
|
|
26358
|
+
for (const app of appRegistry.listApps()) {
|
|
26359
|
+
const ui = app.ui;
|
|
26360
|
+
if (!ui) continue;
|
|
26361
|
+
const toolId = appUiToolId(app.appId);
|
|
26362
|
+
if (seen.has(toolId)) {
|
|
26363
|
+
console.warn(
|
|
26364
|
+
`[app-ui-apps] skipping UI panel for app "${app.appId}": tool id "${toolId}" collides with an existing tool or another installed app's panel.`
|
|
26365
|
+
);
|
|
26366
|
+
continue;
|
|
26367
|
+
}
|
|
26368
|
+
let html;
|
|
26369
|
+
try {
|
|
26370
|
+
html = await cache.get(ui.path, app.updatedAt);
|
|
26371
|
+
} catch (err) {
|
|
26372
|
+
console.warn(
|
|
26373
|
+
`[app-ui-apps] skipping UI panel for app "${app.appId}": could not read "${ui.path}": ${err instanceof Error ? err.message : String(err)}`
|
|
26374
|
+
);
|
|
26375
|
+
continue;
|
|
26376
|
+
}
|
|
26377
|
+
seen.add(toolId);
|
|
26378
|
+
apps.push({
|
|
26379
|
+
id: toolId,
|
|
26380
|
+
title: ui.title ?? app.name ?? app.appId,
|
|
26381
|
+
...ui.description ? { description: ui.description } : {},
|
|
26382
|
+
inputSchema: z.object({}),
|
|
26383
|
+
execute: async () => ({ appId: app.appId, tools: ui.tools ?? [] }),
|
|
26384
|
+
html,
|
|
26385
|
+
...ui.csp ? {
|
|
26386
|
+
csp: {
|
|
26387
|
+
...ui.csp.connectDomains ? { connectDomains: [...ui.csp.connectDomains] } : {},
|
|
26388
|
+
...ui.csp.resourceDomains ? { resourceDomains: [...ui.csp.resourceDomains] } : {}
|
|
26389
|
+
}
|
|
26390
|
+
} : {}
|
|
26391
|
+
});
|
|
26392
|
+
}
|
|
26393
|
+
return apps;
|
|
26029
26394
|
}
|
|
26030
26395
|
function createSessionEventBus() {
|
|
26031
26396
|
const ee = new EventEmitter();
|
|
@@ -29211,7 +29576,8 @@ function registerDaemonHealthTools(server, opts) {
|
|
|
29211
29576
|
resumeSessionsOnBoot: opts.resumeSessionsOnBoot,
|
|
29212
29577
|
idleReapAfterMs: opts.idleReapAfterMs,
|
|
29213
29578
|
crashDetectIntervalMs: opts.crashDetectIntervalMs ?? 0,
|
|
29214
|
-
restartSweepIntervalMs: opts.restartSweepIntervalMs ?? 0
|
|
29579
|
+
restartSweepIntervalMs: opts.restartSweepIntervalMs ?? 0,
|
|
29580
|
+
turnStallAfterMs: opts.turnStallAfterMs ?? 0
|
|
29215
29581
|
});
|
|
29216
29582
|
}
|
|
29217
29583
|
);
|
|
@@ -31356,6 +31722,7 @@ async function isAgentCliAuthConfigured(slug, descriptor, model) {
|
|
|
31356
31722
|
|
|
31357
31723
|
// src/index.ts
|
|
31358
31724
|
var DEFAULT_CRASH_DETECT_INTERVAL_MS = 3e4;
|
|
31725
|
+
var DEFAULT_TURN_STALL_AFTER_MS = 5 * 6e4;
|
|
31359
31726
|
var DEFAULT_ALWAYS_ON_TOOLS = [
|
|
31360
31727
|
"daemon_health",
|
|
31361
31728
|
"agent_start",
|
|
@@ -31366,7 +31733,8 @@ var DEFAULT_ALWAYS_ON_TOOLS = [
|
|
|
31366
31733
|
"session_monitor",
|
|
31367
31734
|
"session_events_poll",
|
|
31368
31735
|
"permissions_list",
|
|
31369
|
-
"permissions_respond"
|
|
31736
|
+
"permissions_respond",
|
|
31737
|
+
"app_tool_call"
|
|
31370
31738
|
];
|
|
31371
31739
|
async function createGateway(opts) {
|
|
31372
31740
|
const startedAt = Date.now();
|
|
@@ -31374,6 +31742,7 @@ async function createGateway(opts) {
|
|
|
31374
31742
|
const idleReapAfterMs = typeof opts.idleReapAfterMs === "number" && opts.idleReapAfterMs > 0 ? opts.idleReapAfterMs : 0;
|
|
31375
31743
|
const crashDetectIntervalMs = typeof opts.crashDetectIntervalMs === "number" ? opts.crashDetectIntervalMs > 0 ? opts.crashDetectIntervalMs : 0 : DEFAULT_CRASH_DETECT_INTERVAL_MS;
|
|
31376
31744
|
const restartSweepIntervalMs = typeof opts.restartSweepIntervalMs === "number" && opts.restartSweepIntervalMs > 0 ? opts.restartSweepIntervalMs : 0;
|
|
31745
|
+
const turnStallAfterMs = typeof opts.turnStallAfterMs === "number" ? opts.turnStallAfterMs > 0 ? opts.turnStallAfterMs : 0 : DEFAULT_TURN_STALL_AFTER_MS;
|
|
31377
31746
|
const workspace = resolve(opts.workspace);
|
|
31378
31747
|
if (!existsSync(workspace)) {
|
|
31379
31748
|
throw new Error(`runtime: workspace dir does not exist: ${workspace}`);
|
|
@@ -31550,7 +31919,8 @@ async function createGateway(opts) {
|
|
|
31550
31919
|
cronScheduler,
|
|
31551
31920
|
dispatchTool
|
|
31552
31921
|
});
|
|
31553
|
-
const appRegistry = createAppRegistry();
|
|
31922
|
+
const appRegistry = createAppRegistry({ persist });
|
|
31923
|
+
const appUiHtmlCache = createUiHtmlCache();
|
|
31554
31924
|
const workflowRunner = opts.resolveAgentAdapter ? createWorkflowRunner({
|
|
31555
31925
|
registry: sessions,
|
|
31556
31926
|
sessionEvents,
|
|
@@ -31671,7 +32041,7 @@ async function createGateway(opts) {
|
|
|
31671
32041
|
...opts.listAgentAdapters ? { listAgentAdapters: opts.listAgentAdapters } : {},
|
|
31672
32042
|
...opts.listHarnessCapabilities ? { listHarnessCapabilities: opts.listHarnessCapabilities } : {}
|
|
31673
32043
|
});
|
|
31674
|
-
const mcpServerFactory = async (denyTools, callerSessionId) => {
|
|
32044
|
+
const mcpServerFactory = async (denyTools, callerSessionId, origin) => {
|
|
31675
32045
|
const { server: rawServer } = await createMcpServer({
|
|
31676
32046
|
specs: opts.specs,
|
|
31677
32047
|
workspace,
|
|
@@ -31690,7 +32060,8 @@ async function createGateway(opts) {
|
|
|
31690
32060
|
resumeSessionsOnBoot: opts.resumeSessionsOnBoot === true,
|
|
31691
32061
|
idleReapAfterMs,
|
|
31692
32062
|
crashDetectIntervalMs,
|
|
31693
|
-
restartSweepIntervalMs
|
|
32063
|
+
restartSweepIntervalMs,
|
|
32064
|
+
turnStallAfterMs
|
|
31694
32065
|
});
|
|
31695
32066
|
registerCommandTools(server, {
|
|
31696
32067
|
workspace,
|
|
@@ -31714,6 +32085,9 @@ async function createGateway(opts) {
|
|
|
31714
32085
|
// a spawn made by an agent session attaches under it by default (see
|
|
31715
32086
|
// spawn-attach.ts). Absent on a human/root `/mcp` call → no auto-parent.
|
|
31716
32087
|
...callerSessionId ? { callerSessionId } : {},
|
|
32088
|
+
// `?origin=` query (#session-visibility) — the connecting client's source
|
|
32089
|
+
// label, used as the default origin for a spawn that doesn't set its own.
|
|
32090
|
+
...origin ? { mcpBridgeOrigin: origin } : {},
|
|
31717
32091
|
webhookNotifier,
|
|
31718
32092
|
daemonMcpUrl,
|
|
31719
32093
|
resolveSandboxProvider: resolveSandboxProviderResolved,
|
|
@@ -31753,6 +32127,16 @@ async function createGateway(opts) {
|
|
|
31753
32127
|
registry: sessions,
|
|
31754
32128
|
listRegisteredToolIds,
|
|
31755
32129
|
appRegistry,
|
|
32130
|
+
dispatchTool,
|
|
32131
|
+
// Same proxy `mcp_imported_call` (session-tools.ts) dispatches
|
|
32132
|
+
// through — unwraps `{ok,result}|{ok:false,error}` into a plain
|
|
32133
|
+
// return-or-throw for `app_tool_call`'s `imported:<alias>/<toolName>`
|
|
32134
|
+
// ids.
|
|
32135
|
+
callImportedTool: async (alias, tool, args) => {
|
|
32136
|
+
const out = await mcpProxy.callTool(alias, tool, args);
|
|
32137
|
+
if (!out.ok) throw new Error(`app_tool_call: imported "${alias}".${tool}: ${out.error}`);
|
|
32138
|
+
return out.result;
|
|
32139
|
+
},
|
|
31756
32140
|
...opts.resolveAgentAdapter ? { resolveAgentAdapter: opts.resolveAgentAdapter } : {},
|
|
31757
32141
|
...workflowRunner ? { workflowRunner } : {}
|
|
31758
32142
|
});
|
|
@@ -31764,7 +32148,7 @@ async function createGateway(opts) {
|
|
|
31764
32148
|
}
|
|
31765
32149
|
return rows;
|
|
31766
32150
|
};
|
|
31767
|
-
|
|
32151
|
+
const builtinPanelApps = [
|
|
31768
32152
|
makeSessionsPanelApp({ listSessions: listSessionsFiltered }),
|
|
31769
32153
|
makeAgentsOverviewApp({ listSessions: listSessionsFiltered }),
|
|
31770
32154
|
makeBureauSessionsApp({ listSessions: listSessionsFiltered }),
|
|
@@ -31814,7 +32198,13 @@ async function createGateway(opts) {
|
|
|
31814
32198
|
}
|
|
31815
32199
|
})
|
|
31816
32200
|
] : []
|
|
31817
|
-
]
|
|
32201
|
+
];
|
|
32202
|
+
const installedAppUiApps = await makeInstalledAppUiApps(
|
|
32203
|
+
appRegistry,
|
|
32204
|
+
appUiHtmlCache,
|
|
32205
|
+
new Set(builtinPanelApps.map((app) => app.id))
|
|
32206
|
+
);
|
|
32207
|
+
registerMcpApps(server, [...builtinPanelApps, ...installedAppUiApps]);
|
|
31818
32208
|
registerSummarizeSessionTool(server, {
|
|
31819
32209
|
getSession: (id) => sessions.get(id),
|
|
31820
32210
|
tailLines: (id, lastN) => {
|
|
@@ -31967,7 +32357,8 @@ async function createGateway(opts) {
|
|
|
31967
32357
|
resumeSessionsOnBoot: opts.resumeSessionsOnBoot === true,
|
|
31968
32358
|
idleReapAfterMs,
|
|
31969
32359
|
crashDetectIntervalMs,
|
|
31970
|
-
restartSweepIntervalMs
|
|
32360
|
+
restartSweepIntervalMs,
|
|
32361
|
+
turnStallAfterMs
|
|
31971
32362
|
},
|
|
31972
32363
|
cronScheduler,
|
|
31973
32364
|
routineRegistrar
|
|
@@ -32012,6 +32403,27 @@ async function createGateway(opts) {
|
|
|
32012
32403
|
}, crashDetectIntervalMs);
|
|
32013
32404
|
crashDetectTimer.unref?.();
|
|
32014
32405
|
}
|
|
32406
|
+
let turnStallTimer = null;
|
|
32407
|
+
if (turnStallAfterMs > 0) {
|
|
32408
|
+
const rawInterval = process.env.AGENTPROTO_TURN_STALL_INTERVAL_MS;
|
|
32409
|
+
const parsedInterval = rawInterval ? Number.parseInt(rawInterval, 10) : NaN;
|
|
32410
|
+
const intervalMs = Number.isFinite(parsedInterval) && parsedInterval > 0 ? parsedInterval : Math.min(turnStallAfterMs, 6e4);
|
|
32411
|
+
turnStallTimer = setInterval(() => {
|
|
32412
|
+
try {
|
|
32413
|
+
const summary = runStallWatchdogPass({ registry: sessions, turnStallAfterMs });
|
|
32414
|
+
if (summary.stalled > 0) {
|
|
32415
|
+
console.log(
|
|
32416
|
+
`[stall-watchdog] flagged ${summary.stalled}/${summary.candidates} stalled agent session(s): ${summary.ids.join(", ")}`
|
|
32417
|
+
);
|
|
32418
|
+
}
|
|
32419
|
+
} catch (err) {
|
|
32420
|
+
console.warn(
|
|
32421
|
+
`[stall-watchdog] sweep failed: ${err instanceof Error ? err.message : String(err)}`
|
|
32422
|
+
);
|
|
32423
|
+
}
|
|
32424
|
+
}, intervalMs);
|
|
32425
|
+
turnStallTimer.unref?.();
|
|
32426
|
+
}
|
|
32015
32427
|
let restartSweepTimer = null;
|
|
32016
32428
|
if (restartSweepIntervalMs > 0) {
|
|
32017
32429
|
restartSweepTimer = setInterval(() => {
|
|
@@ -32072,6 +32484,7 @@ async function createGateway(opts) {
|
|
|
32072
32484
|
heartbeat.stop();
|
|
32073
32485
|
if (idleReapTimer) clearInterval(idleReapTimer);
|
|
32074
32486
|
if (crashDetectTimer) clearInterval(crashDetectTimer);
|
|
32487
|
+
if (turnStallTimer) clearInterval(turnStallTimer);
|
|
32075
32488
|
if (restartSweepTimer) clearInterval(restartSweepTimer);
|
|
32076
32489
|
restartScheduler.dispose();
|
|
32077
32490
|
inboundWatcher?.shutdown();
|
|
@@ -32131,6 +32544,6 @@ var export_providersPath = providers_store_exports.providersPath;
|
|
|
32131
32544
|
var export_removeProviderKey = providers_store_exports.removeProviderKey;
|
|
32132
32545
|
var export_setProviderKey = providers_store_exports.setProviderKey;
|
|
32133
32546
|
|
|
32134
|
-
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAUDE_CODE_OAUTH_SOURCE, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, INBOUND_PROVIDERS, LEGACY_SESSIONS_FILE, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, ResumeDisabledError, SESSION_ID_ENV, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, activityCounts, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, composeMode, composeSessionObservers, conversationIndexPath, createActivityProjector, createFileStepCache, createGateway, createInboundEndpointStore, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createScopeTokenRegistry, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceFs, credentialFingerprint, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decomposeMode, deleteUserPreset, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, evaluateCostBudget, fileConversationStore, filterActivities, findConversationRecord, findNativeMode, formatToolCall, formatToolResult, getMcpCredentialDeps, export_getProviderKey as getProviderKey, getUserPreset, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isSafeBucketSlug, isTerminalActivityState, listBuckets, listPresets, listUserPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, migrateLegacySessionsFile, migrationMarkerPath, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, policyToActivities, policyWatchesSession, prToActivities, projectSessionUsage, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readConversationIndex, readDaemonRegistry, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readUsageSnapshots, reapOrphanedDescendants, recordProfileQuota, registerBuiltinRoutes, registerPairingTools, registerSandboxAttachTool, export_removeProviderKey as removeProviderKey, resolveAuthSpec, resolveBucketSlug, resolveNativeLink, resolvePosture, resolveSpawnDefaults, resolveSubscriptionCredential, rollupUsage, runCrashDetectPass, runEagerResumePass, runIdleReapPass, saveUserPreset, setMcpCredentialDeps, export_setProviderKey as setProviderKey, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, userPresetsPath, verifyInboundSignature, workflowToActivities, writeDaemonRegistryEntry };
|
|
32547
|
+
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAUDE_CODE_OAUTH_SOURCE, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, INBOUND_PROVIDERS, LEGACY_SESSIONS_FILE, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, ResumeDisabledError, SESSION_ID_ENV, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, activityCounts, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, composeMode, composeSessionObservers, conversationIndexPath, createActivityProjector, createFileStepCache, createGateway, createInboundEndpointStore, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createScopeTokenRegistry, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceFs, credentialFingerprint, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decomposeMode, deleteUserPreset, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, evaluateCostBudget, fileConversationStore, filterActivities, findConversationRecord, findNativeMode, formatToolCall, formatToolResult, getMcpCredentialDeps, export_getProviderKey as getProviderKey, getUserPreset, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isSafeBucketSlug, isTerminalActivityState, listBuckets, listPresets, listUserPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, migrateLegacySessionsFile, migrationMarkerPath, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, policyToActivities, policyWatchesSession, prToActivities, projectSessionUsage, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readConversationIndex, readDaemonRegistry, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readUsageSnapshots, reapOrphanedDescendants, recordProfileQuota, registerBuiltinRoutes, registerPairingTools, registerSandboxAttachTool, export_removeProviderKey as removeProviderKey, resolveAuthSpec, resolveBucketSlug, resolveNativeLink, resolvePosture, resolveSpawnDefaults, resolveSubscriptionCredential, rollupUsage, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runStallWatchdogPass, saveUserPreset, setMcpCredentialDeps, export_setProviderKey as setProviderKey, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, userPresetsPath, verifyInboundSignature, workflowToActivities, writeDaemonRegistryEntry };
|
|
32135
32548
|
//# sourceMappingURL=index.mjs.map
|
|
32136
32549
|
//# sourceMappingURL=index.mjs.map
|