@sema-agent/core 5.46.0 → 5.48.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/CHANGELOG.md +93 -0
- package/dist/agents/agent-transcript-tool.d.ts +4 -0
- package/dist/agents/agent-transcript-tool.js +10 -3
- package/dist/agents/send-message-tool.d.ts +43 -1
- package/dist/agents/send-message-tool.js +50 -11
- package/dist/agents/subagent.d.ts +18 -0
- package/dist/agents/subagent.js +231 -4
- package/dist/config/defaults.d.ts +20 -0
- package/dist/config/defaults.js +5 -0
- package/dist/core/background-agent-store.d.ts +1 -0
- package/dist/core/background-agent-store.js +13 -0
- package/dist/core/governance-codes.d.ts +13 -0
- package/dist/core/governance-codes.js +33 -0
- package/dist/core/mcp.d.ts +6 -1
- package/dist/core/mcp.js +34 -7
- package/dist/core/memory-engine/delegation-settlement.d.ts +318 -0
- package/dist/core/memory-engine/delegation-settlement.js +661 -0
- package/dist/core/memory-engine/engine.d.ts +159 -1
- package/dist/core/memory-engine/engine.js +699 -15
- package/dist/core/memory-engine/file-backend.d.ts +1 -0
- package/dist/core/memory-engine/file-backend.js +3 -1
- package/dist/core/memory-engine/frontmatter.d.ts +46 -19
- package/dist/core/memory-engine/frontmatter.js +91 -77
- package/dist/core/memory-engine/index.d.ts +4 -3
- package/dist/core/memory-engine/index.js +3 -2
- package/dist/core/memory-engine/layout.d.ts +14 -0
- package/dist/core/memory-engine/layout.js +2 -2
- package/dist/core/memory-engine/memory-backend-contract.js +43 -0
- package/dist/core/memory-engine/origin-clearance.d.ts +66 -0
- package/dist/core/memory-engine/origin-clearance.js +84 -0
- package/dist/core/memory-engine/provenance-wording.d.ts +50 -0
- package/dist/core/memory-engine/provenance-wording.js +15 -0
- package/dist/core/memory-engine/tools.d.ts +61 -7
- package/dist/core/memory-engine/tools.js +34 -9
- package/dist/core/memory-engine/types.d.ts +70 -2
- package/dist/core/reminder-disclosure.d.ts +90 -0
- package/dist/core/reminder-disclosure.js +64 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +6 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-memory.js +50 -15
- package/dist/core/runner/prepare-task.d.ts +39 -0
- package/dist/core/runner/prepare-task.js +128 -40
- package/dist/core/runner/runtask.js +3 -1
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/session-store.d.ts +59 -1
- package/dist/core/session-store.js +82 -14
- package/dist/core/session.d.ts +83 -1
- package/dist/core/task-registry-agent.d.ts +28 -0
- package/dist/core/task-registry-agent.js +63 -2
- package/dist/core/task-registry.d.ts +21 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/types.d.ts +98 -3
- package/dist/core/types.js +3 -0
- package/dist/core/untrusted-text.d.ts +63 -0
- package/dist/core/untrusted-text.js +48 -0
- package/dist/core/wiring-manifest.d.ts +35 -0
- package/dist/core/wiring-manifest.js +21 -1
- package/dist/engine/harness/types.d.ts +36 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +6 -4
- package/dist/internal/harness-types.d.ts +1 -0
- package/dist/stores/file/index.d.ts +19 -3
- package/dist/stores/file/index.js +24 -1
- package/dist/stores/file/session-store.d.ts +18 -4
- package/dist/stores/file/session-store.js +73 -12
- package/dist/tools/fs/fs-pdf.d.ts +12 -1
- package/dist/tools/fs/fs-pdf.js +17 -3
- package/dist/tools/fs/fs-read.d.ts +2 -1
- package/dist/tools/fs/fs-read.js +33 -5
- package/dist/tools/fs/fs-shared.d.ts +6 -2
- package/dist/tools/fs/index.d.ts +7 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/task-list.d.ts +5 -1
- package/dist/tools/web.js +21 -2
- package/package.json +3 -2
- package/test/export-surface.snapshot.json +24 -2
package/dist/agents/subagent.js
CHANGED
|
@@ -3,6 +3,8 @@ import { isAbsolute } from "node:path";
|
|
|
3
3
|
import { withDelegationProvenance } from "../core/tool-policy.js";
|
|
4
4
|
import { isHighSurrogate, isLowSurrogate } from "../core/surrogate-safe-slice.js";
|
|
5
5
|
import { newDelegationProvenanceAggregate, reduceDelegationAttestation } from "../core/memory-engine/delegation-provenance.js";
|
|
6
|
+
import { registerDelegationLaunch, replayExternalSettlementEffects, settleDelegation } from "../core/memory-engine/delegation-settlement.js";
|
|
7
|
+
import { enqueueMemoryAnnouncement } from "../core/memory-engine/layout.js";
|
|
6
8
|
import { resolveModel, resolveModelDisplayLabel } from "../core/roles.js";
|
|
7
9
|
import { OUTPUT_TOOL_NAME, REPORT_BLOCKED_TOOL_NAME } from "../core/runner/synthetic-tools.js";
|
|
8
10
|
import { TOOL_SEARCH_NAME } from "../core/runner/tool-disclosure.js";
|
|
@@ -22,7 +24,7 @@ import { BG_AGENT_REAP_STOP_ERROR } from "../core/task-registry.js";
|
|
|
22
24
|
import { readDurableOrgAdmission } from "../core/memory-admission.js";
|
|
23
25
|
import { extractErrorCode } from "../brain/errors.js";
|
|
24
26
|
import { governanceRetryClass } from "../core/governance-codes.js";
|
|
25
|
-
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS } from "../config/defaults.js";
|
|
27
|
+
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS, DELEGATION_MAX_CONCURRENT_DEFAULT, DELEGATION_MAX_PER_SESSION_DEFAULT } from "../config/defaults.js";
|
|
26
28
|
function rollupDelegatedCost(stats, nested) {
|
|
27
29
|
if (stats.costMicroUsd === undefined)
|
|
28
30
|
return undefined;
|
|
@@ -873,6 +875,43 @@ function parkCompletionNotify(deps) {
|
|
|
873
875
|
};
|
|
874
876
|
entry.deferredNotify = { ...(payload.seq !== undefined ? { seq: payload.seq } : {}), cancel, flush: () => deliver(false) };
|
|
875
877
|
}
|
|
878
|
+
export function resolveDelegationEntryCaps(caps) {
|
|
879
|
+
const bad = (member, value) => {
|
|
880
|
+
const e = new Error(`RunnerDeps.delegationEntryCaps.${member} holds ${typeof value === "number" ? String(value) : value === null ? "null" : typeof value} — must be a positive integer (or absent for the default). A bad cap refuses loudly; it is never silently folded to the default.`);
|
|
881
|
+
e.code = "config.delegation_entry_caps";
|
|
882
|
+
throw e;
|
|
883
|
+
};
|
|
884
|
+
const member = (name, fallback) => {
|
|
885
|
+
const v = caps?.[name];
|
|
886
|
+
if (v === undefined)
|
|
887
|
+
return fallback;
|
|
888
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v <= 0)
|
|
889
|
+
bad(name, v);
|
|
890
|
+
return v;
|
|
891
|
+
};
|
|
892
|
+
const maxConcurrent = member("maxConcurrent", DELEGATION_MAX_CONCURRENT_DEFAULT);
|
|
893
|
+
const maxCumulativePerSession = member("maxCumulativePerSession", DELEGATION_MAX_PER_SESSION_DEFAULT);
|
|
894
|
+
if (maxConcurrent > maxCumulativePerSession) {
|
|
895
|
+
const e = new Error(`RunnerDeps.delegationEntryCaps resolves to maxConcurrent ${maxConcurrent} > maxCumulativePerSession ${maxCumulativePerSession} — a tree cannot run more agents at once than it may ever create. Fix the pair; a contradictory configuration refuses loudly.`);
|
|
896
|
+
e.code = "config.delegation_entry_caps";
|
|
897
|
+
throw e;
|
|
898
|
+
}
|
|
899
|
+
return { maxConcurrent, maxCumulativePerSession };
|
|
900
|
+
}
|
|
901
|
+
const delegationEntryLedgers = new WeakMap();
|
|
902
|
+
function delegationEntryLedger(registry, key) {
|
|
903
|
+
let byKey = delegationEntryLedgers.get(registry);
|
|
904
|
+
if (byKey === undefined) {
|
|
905
|
+
byKey = new Map();
|
|
906
|
+
delegationEntryLedgers.set(registry, byKey);
|
|
907
|
+
}
|
|
908
|
+
let set = byKey.get(key);
|
|
909
|
+
if (set === undefined) {
|
|
910
|
+
set = new Set();
|
|
911
|
+
byKey.set(key, set);
|
|
912
|
+
}
|
|
913
|
+
return set;
|
|
914
|
+
}
|
|
876
915
|
export function normalizeSubagentType(value) {
|
|
877
916
|
return value.normalize("NFKC").toLowerCase().replace(/[\p{White_Space}\p{Pd}_]+/gu, "");
|
|
878
917
|
}
|
|
@@ -1935,6 +1974,59 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1935
1974
|
const shortDesc = `fork: ${(typeof a.description === "string" && a.description.trim() ? a.description.trim() : prompt).slice(0, 180)}`;
|
|
1936
1975
|
const bgOwner = sessionScopedBg ? ctx.sessionId : ctx.taskId ?? bg.owner;
|
|
1937
1976
|
const bgScope = treeScope;
|
|
1977
|
+
const forkSettlementSeat = ctx.delegationSettlement?.();
|
|
1978
|
+
const forkSettleId = forkSettlementSeat !== undefined ? `bg-${uuidv7()}` : undefined;
|
|
1979
|
+
if (forkSettlementSeat !== undefined && forkSettleId !== undefined) {
|
|
1980
|
+
try {
|
|
1981
|
+
registerDelegationLaunch(forkSettlementSeat.controlDir, { settleId: forkSettleId, sessionId: forkSettlementSeat.sessionId, now: Date.now, ...(ctx.toolCallId !== undefined ? { toolUseId: ctx.toolCallId } : {}) });
|
|
1982
|
+
}
|
|
1983
|
+
catch (e) {
|
|
1984
|
+
dropHostAbortListener();
|
|
1985
|
+
const wt = await finishWorktree();
|
|
1986
|
+
return {
|
|
1987
|
+
isError: true,
|
|
1988
|
+
content: `Sub-agent not started in background: the delegation settlement account could not record the launch (${(e instanceof Error ? e.message : String(e)).slice(0, 300)}) — fail-closed; repair the memory control plane or retry.${wt ? `\n${wt}` : ""}`,
|
|
1989
|
+
details: { error: "settlement_write_ahead_failed" },
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
const settleForkRow = (status) => {
|
|
1994
|
+
if (forkSettlementSeat === undefined || forkSettleId === undefined)
|
|
1995
|
+
return;
|
|
1996
|
+
const att = status !== undefined ? childAttestation(status) : undefined;
|
|
1997
|
+
const verdict = att === "external" ? "external" : att === "clean" ? "clean" : "unattestable";
|
|
1998
|
+
let settled = false;
|
|
1999
|
+
let lastErr;
|
|
2000
|
+
for (let attempt = 0; attempt < 3 && !settled; attempt++) {
|
|
2001
|
+
try {
|
|
2002
|
+
settleDelegation(forkSettlementSeat.controlDir, { settleId: forkSettleId, status: verdict, now: Date.now });
|
|
2003
|
+
settled = true;
|
|
2004
|
+
}
|
|
2005
|
+
catch (e) {
|
|
2006
|
+
lastErr = e;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
if (settled && verdict === "external") {
|
|
2010
|
+
try {
|
|
2011
|
+
replayExternalSettlementEffects(forkSettlementSeat.controlDir, { carry: true, now: Date.now });
|
|
2012
|
+
}
|
|
2013
|
+
catch {
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (!settled) {
|
|
2017
|
+
const detail = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
2018
|
+
try {
|
|
2019
|
+
enqueueMemoryAnnouncement(forkSettlementSeat.controlDir, {
|
|
2020
|
+
kind: "gate",
|
|
2021
|
+
at: Date.now(),
|
|
2022
|
+
items: [`delegation settlement: the terminal observation for a background fork of session ${JSON.stringify(forkSettlementSeat.sessionId)} could NOT be recorded (verdict ${verdict}) — the row stays pending and expires as UNPROVEN at the settlement window (fail-closed floor): ${detail.slice(0, 200)}`],
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
catch {
|
|
2026
|
+
console.warn(`[sema] delegation settlement terminal write failed (fork, verdict ${verdict}, session ${forkSettlementSeat.sessionId}): ${detail}`);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
1938
2030
|
let taskId;
|
|
1939
2031
|
try {
|
|
1940
2032
|
taskId = bg.registry.registerBackgroundAgent({
|
|
@@ -1962,6 +2054,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1962
2054
|
catch {
|
|
1963
2055
|
}
|
|
1964
2056
|
const wt = await finishWorktree();
|
|
2057
|
+
if (forkSettlementSeat !== undefined && forkSettleId !== undefined) {
|
|
2058
|
+
try {
|
|
2059
|
+
settleDelegation(forkSettlementSeat.controlDir, { settleId: forkSettleId, status: "void", now: Date.now, note: "registration failed before invoke (not dispatched)" });
|
|
2060
|
+
}
|
|
2061
|
+
catch {
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1965
2064
|
return { isError: true, content: `Sub-agent not started in background: ${e instanceof Error ? e.message : String(e)}${wt ? `\n${wt}` : ""}`, details: { error: "register_failed" } };
|
|
1966
2065
|
}
|
|
1967
2066
|
childInternals.peerSelfRef?.addAxis("h", taskId);
|
|
@@ -2106,6 +2205,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2106
2205
|
void opts.runner
|
|
2107
2206
|
.runTask(bgForkSpec, bgForkInternals)
|
|
2108
2207
|
.then(async (child) => {
|
|
2208
|
+
settleForkRow(child.status);
|
|
2109
2209
|
dropHostAbortListener();
|
|
2110
2210
|
await finishWorktree();
|
|
2111
2211
|
try {
|
|
@@ -2202,6 +2302,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2202
2302
|
}
|
|
2203
2303
|
})
|
|
2204
2304
|
.catch((e) => {
|
|
2305
|
+
settleForkRow("failed");
|
|
2205
2306
|
dropHostAbortListener();
|
|
2206
2307
|
void finishWorktree();
|
|
2207
2308
|
const killed = abort.signal.aborted;
|
|
@@ -2268,7 +2369,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2268
2369
|
: `Note: it is stopped automatically (killed) if still running when this task ends — do not promise the user results beyond this task.`,
|
|
2269
2370
|
],
|
|
2270
2371
|
}),
|
|
2271
|
-
details: { type: "agent", subagent_type: FORK_SUBAGENT_TYPE, status: "async_launched", isAsync: true, task_id: taskId, description: shortDesc, prompt },
|
|
2372
|
+
details: { type: "agent", subagent_type: FORK_SUBAGENT_TYPE, status: "async_launched", isAsync: true, task_id: taskId, description: shortDesc, prompt, ...(forkSettleId !== undefined ? { settle_id: forkSettleId } : {}) },
|
|
2272
2373
|
};
|
|
2273
2374
|
}
|
|
2274
2375
|
const bgIgnoredNote = a.run_in_background === true
|
|
@@ -2368,6 +2469,107 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2368
2469
|
const shortDesc = reviveRow?.description ?? String(a.description ?? "sub-agent").slice(0, 200);
|
|
2369
2470
|
const bgOwner = reviveRow !== undefined ? reviveRow.owner : sessionScopedBg ? ctx.sessionId : ctx.taskId ?? bg.owner;
|
|
2370
2471
|
const bgScope = treeScope;
|
|
2472
|
+
const capScope = bgScope;
|
|
2473
|
+
const capRoot = reviveRow !== undefined
|
|
2474
|
+
? (reviveRow.rootSessionId ?? reviveRow.parentSessionId ?? (reviveRow.sessionScoped ? reviveRow.owner : undefined))
|
|
2475
|
+
: (ctx.rootSessionId ?? ctx.sessionId);
|
|
2476
|
+
const entryCaps = ctx.delegationEntryCaps ?? { maxConcurrent: DELEGATION_MAX_CONCURRENT_DEFAULT, maxCumulativePerSession: DELEGATION_MAX_PER_SESSION_DEFAULT };
|
|
2477
|
+
const capLedgerKey = capScope !== undefined && capRoot !== undefined ? JSON.stringify([capScope, capRoot]) : undefined;
|
|
2478
|
+
if (capScope !== undefined && capRoot !== undefined) {
|
|
2479
|
+
let storedHandles = [];
|
|
2480
|
+
let storedEnumerationOk = false;
|
|
2481
|
+
if (bg.agentStore !== undefined && reviveRow === undefined) {
|
|
2482
|
+
try {
|
|
2483
|
+
storedHandles = (await bg.agentStore.listBySession(capScope, capRoot)).map((r) => r.handle);
|
|
2484
|
+
storedEnumerationOk = true;
|
|
2485
|
+
}
|
|
2486
|
+
catch {
|
|
2487
|
+
storedHandles = [];
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
const capRefusal = async (code, text) => {
|
|
2491
|
+
dropHostAbortListener();
|
|
2492
|
+
await cancelObserver();
|
|
2493
|
+
const wt = await finishWorktree();
|
|
2494
|
+
return { isError: true, content: `Sub-agent not started in background: ${text}${wt ? `\n${wt}` : ""}`, details: { error: code } };
|
|
2495
|
+
};
|
|
2496
|
+
const activeFace = bg.registry.activeDelegationHandles;
|
|
2497
|
+
const active = typeof activeFace === "function" ? activeFace.call(bg.registry, capScope, capRoot) : [];
|
|
2498
|
+
if (typeof activeFace === "function" && active.length >= entryCaps.maxConcurrent) {
|
|
2499
|
+
return await capRefusal("delegation.concurrency_cap", `this session tree already has ${active.length} background agents running — the concurrency cap (${entryCaps.maxConcurrent}; RunnerDeps.delegationEntryCaps.maxConcurrent) refuses another. Wait for one to complete (you will be notified) or stop one, then relaunch.`);
|
|
2500
|
+
}
|
|
2501
|
+
if (reviveRow === undefined) {
|
|
2502
|
+
const ledger = delegationEntryLedger(bg.registry, capLedgerKey);
|
|
2503
|
+
let cumulative;
|
|
2504
|
+
if (bg.agentStore !== undefined && storedEnumerationOk) {
|
|
2505
|
+
const keep = new Set([...storedHandles, ...active]);
|
|
2506
|
+
for (const h of [...ledger])
|
|
2507
|
+
if (!keep.has(h))
|
|
2508
|
+
ledger.delete(h);
|
|
2509
|
+
cumulative = new Set([...storedHandles, ...ledger]).size;
|
|
2510
|
+
}
|
|
2511
|
+
else {
|
|
2512
|
+
cumulative = ledger.size;
|
|
2513
|
+
}
|
|
2514
|
+
if (cumulative >= entryCaps.maxCumulativePerSession) {
|
|
2515
|
+
return await capRefusal("delegation.session_cap", `this session tree has already launched ${cumulative} background agents in its retained window — the cumulative cap (${entryCaps.maxCumulativePerSession}; RunnerDeps.delegationEntryCaps.maxCumulativePerSession) refuses more. Continue an existing agent (SendMessage) instead of launching new ones.`);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
const settlementSeat = ctx.delegationSettlement?.();
|
|
2520
|
+
const bgSettleId = settlementSeat !== undefined ? `bg-${uuidv7()}` : undefined;
|
|
2521
|
+
if (settlementSeat !== undefined && bgSettleId !== undefined) {
|
|
2522
|
+
try {
|
|
2523
|
+
registerDelegationLaunch(settlementSeat.controlDir, { settleId: bgSettleId, sessionId: settlementSeat.sessionId, now: Date.now, ...(ctx.toolCallId !== undefined ? { toolUseId: ctx.toolCallId } : {}) });
|
|
2524
|
+
}
|
|
2525
|
+
catch (e) {
|
|
2526
|
+
dropHostAbortListener();
|
|
2527
|
+
await cancelObserver();
|
|
2528
|
+
const wt = await finishWorktree();
|
|
2529
|
+
return {
|
|
2530
|
+
isError: true,
|
|
2531
|
+
content: `Sub-agent not started in background: the delegation settlement account could not record the launch (${(e instanceof Error ? e.message : String(e)).slice(0, 300)}) — fail-closed; repair the memory control plane or retry.${wt ? `\n${wt}` : ""}`,
|
|
2532
|
+
details: { error: "settlement_write_ahead_failed" },
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
const settleDelegationRow = (status) => {
|
|
2537
|
+
if (settlementSeat === undefined || bgSettleId === undefined)
|
|
2538
|
+
return;
|
|
2539
|
+
const att = status !== undefined ? childAttestation(status) : undefined;
|
|
2540
|
+
const verdict = att === "external" ? "external" : att === "clean" ? "clean" : "unattestable";
|
|
2541
|
+
let settled = false;
|
|
2542
|
+
let lastErr;
|
|
2543
|
+
for (let attempt = 0; attempt < 3 && !settled; attempt++) {
|
|
2544
|
+
try {
|
|
2545
|
+
settleDelegation(settlementSeat.controlDir, { settleId: bgSettleId, status: verdict, now: Date.now });
|
|
2546
|
+
settled = true;
|
|
2547
|
+
}
|
|
2548
|
+
catch (e) {
|
|
2549
|
+
lastErr = e;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
if (settled && verdict === "external") {
|
|
2553
|
+
try {
|
|
2554
|
+
replayExternalSettlementEffects(settlementSeat.controlDir, { carry: true, now: Date.now });
|
|
2555
|
+
}
|
|
2556
|
+
catch {
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
if (!settled) {
|
|
2560
|
+
const detail = lastErr instanceof Error ? lastErr.message : String(lastErr);
|
|
2561
|
+
try {
|
|
2562
|
+
enqueueMemoryAnnouncement(settlementSeat.controlDir, {
|
|
2563
|
+
kind: "gate",
|
|
2564
|
+
at: Date.now(),
|
|
2565
|
+
items: [`delegation settlement: the terminal observation for a background delegation of session ${JSON.stringify(settlementSeat.sessionId)} could NOT be recorded (verdict ${verdict}) — the row stays pending and expires as UNPROVEN at the settlement window (fail-closed floor): ${detail.slice(0, 200)}`],
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
catch {
|
|
2569
|
+
console.warn(`[sema] delegation settlement terminal write failed (verdict ${verdict}, session ${settlementSeat.sessionId}): ${detail} — the pending row expires as unproven at the settlement window`);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
};
|
|
2371
2573
|
let taskId;
|
|
2372
2574
|
try {
|
|
2373
2575
|
taskId = bg.registry.registerBackgroundAgent(reviveRow !== undefined
|
|
@@ -2427,8 +2629,17 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2427
2629
|
dropHostAbortListener();
|
|
2428
2630
|
await cancelObserver();
|
|
2429
2631
|
const wt = await finishWorktree();
|
|
2632
|
+
if (settlementSeat !== undefined && bgSettleId !== undefined) {
|
|
2633
|
+
try {
|
|
2634
|
+
settleDelegation(settlementSeat.controlDir, { settleId: bgSettleId, status: "void", now: Date.now, note: "registration failed before invoke (not dispatched)" });
|
|
2635
|
+
}
|
|
2636
|
+
catch {
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2430
2639
|
return { isError: true, content: `Sub-agent not started in background: ${e instanceof Error ? e.message : String(e)}${wt ? `\n${wt}` : ""}`, details: { error: "register_failed" } };
|
|
2431
2640
|
}
|
|
2641
|
+
if (capLedgerKey !== undefined && reviveRow === undefined)
|
|
2642
|
+
delegationEntryLedger(bg.registry, capLedgerKey).add(taskId);
|
|
2432
2643
|
childInternals.peerSelfRef?.addAxis("h", taskId);
|
|
2433
2644
|
if (agentName !== undefined) {
|
|
2434
2645
|
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...((typeof childModel === "string" ? resolveModelDisplayLabel(childModel) : childModel?.id) !== undefined ? { model: typeof childModel === "string" ? childModel : childModel?.id } : {}), ...(reviveRow !== undefined ? ((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined ? { rootSessionId: reviveRow.rootSessionId ?? reviveRow.parentSessionId } : {}) : (ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: reviveRow?.spawnedAt ?? Date.now() }, (err) => opts.onObserverError?.(err, { site: "roster.recordSpawn" }));
|
|
@@ -2566,10 +2777,22 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2566
2777
|
usage: { toolUses: toolStarts },
|
|
2567
2778
|
});
|
|
2568
2779
|
}, () => bg.registry.noteBackgroundAgentActivity(taskId));
|
|
2780
|
+
const bgPlacementRoot = reviveRow !== undefined ? (reviveRow.rootSessionId ?? reviveRow.parentSessionId) : (ctx.rootSessionId ?? ctx.sessionId);
|
|
2781
|
+
const bgPlacementParent = reviveRow !== undefined ? reviveRow.parentSessionId : ctx.sessionId;
|
|
2782
|
+
const bgPlacement = bg.agentStore !== undefined && opts.runner.sessions.placements?.subagent !== undefined
|
|
2783
|
+
? {
|
|
2784
|
+
kind: "subagent",
|
|
2785
|
+
...(bgScope !== undefined ? { scope: bgScope } : {}),
|
|
2786
|
+
...(bgPlacementParent !== undefined ? { parentSessionId: bgPlacementParent } : {}),
|
|
2787
|
+
...(bgPlacementRoot !== undefined ? { rootSessionId: bgPlacementRoot } : {}),
|
|
2788
|
+
handle: taskId,
|
|
2789
|
+
}
|
|
2790
|
+
: undefined;
|
|
2569
2791
|
const bgInternals = bgSink
|
|
2570
2792
|
? {
|
|
2571
2793
|
...childInternals,
|
|
2572
2794
|
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2795
|
+
...(bgPlacement !== undefined ? { sessionPlacement: bgPlacement } : {}),
|
|
2573
2796
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2574
2797
|
delegationTaskType: "background_agent",
|
|
2575
2798
|
...(bgCycleSeq !== undefined ? { cycleSeq: bgCycleSeq } : {}),
|
|
@@ -2605,6 +2828,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2605
2828
|
: {
|
|
2606
2829
|
...childInternals,
|
|
2607
2830
|
...(bgQuestionStrip !== undefined ? bgQuestionStrip.internalsFlag : {}),
|
|
2831
|
+
...(bgPlacement !== undefined ? { sessionPlacement: bgPlacement } : {}),
|
|
2608
2832
|
onNotifyInjectorReady: s2NotifyReady,
|
|
2609
2833
|
delegationTaskType: "background_agent",
|
|
2610
2834
|
...(bgCycleSeq !== undefined ? { cycleSeq: bgCycleSeq } : {}),
|
|
@@ -2635,7 +2859,8 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2635
2859
|
};
|
|
2636
2860
|
try {
|
|
2637
2861
|
await opts.runner.sessions.acquire(bgChildSessionId, { requireExisting: true });
|
|
2638
|
-
|
|
2862
|
+
if (opts.runner.sessions.forget)
|
|
2863
|
+
await opts.runner.sessions.forget(bgChildSessionId);
|
|
2639
2864
|
}
|
|
2640
2865
|
catch (e) {
|
|
2641
2866
|
const code = e.code;
|
|
@@ -2744,6 +2969,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2744
2969
|
: opts.runner.runTask(bgSpec, bgInternals);
|
|
2745
2970
|
void bgChildPromise
|
|
2746
2971
|
.then(async (child) => {
|
|
2972
|
+
settleDelegationRow(child.status);
|
|
2747
2973
|
closeObserverWindow(child.status);
|
|
2748
2974
|
dropHostAbortListener();
|
|
2749
2975
|
await finishWorktree();
|
|
@@ -2938,6 +3164,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2938
3164
|
}
|
|
2939
3165
|
})
|
|
2940
3166
|
.catch(async (e) => {
|
|
3167
|
+
settleDelegationRow("failed");
|
|
2941
3168
|
closeObserverWindow("failed");
|
|
2942
3169
|
dropHostAbortListener();
|
|
2943
3170
|
void finishWorktree();
|
|
@@ -3043,7 +3270,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3043
3270
|
: `Note: it is stopped automatically (killed) if still running when this task ends — do not promise the user results beyond this task.`,
|
|
3044
3271
|
],
|
|
3045
3272
|
}),
|
|
3046
|
-
details: { type: "agent", status: "async_launched", isAsync: true, task_id: taskId, description: shortDesc, prompt },
|
|
3273
|
+
details: { type: "agent", status: "async_launched", isAsync: true, task_id: taskId, description: shortDesc, prompt, ...(bgSettleId !== undefined ? { settle_id: bgSettleId } : {}) },
|
|
3047
3274
|
};
|
|
3048
3275
|
}
|
|
3049
3276
|
let child;
|
|
@@ -21,6 +21,26 @@
|
|
|
21
21
|
*/
|
|
22
22
|
export declare const RETAIN_DEFAULT_TTL_MS: number;
|
|
23
23
|
export declare const RETAIN_DEFAULT_MAX = 16;
|
|
24
|
+
/** Subagent transcript persistence — the delegation ENTRY caps (CC values: 20 concurrent /
|
|
25
|
+
* 200 per session tree). The INLET bound family (`RunnerDeps.delegationEntryCaps`), as opposed to
|
|
26
|
+
* the retain pair above, which is a pure in-memory fast-lane handle bound (outlet — armed
|
|
27
|
+
* deployments lose nothing when it evicts). Concurrent = running/pending a* handles under one
|
|
28
|
+
* (scope, rootSessionId) key in THIS process's registry; cumulative = retained-window count under
|
|
29
|
+
* the same key (see the enforcement site for the exact retained-window semantics). */
|
|
30
|
+
export declare const DELEGATION_MAX_CONCURRENT_DEFAULT = 20;
|
|
31
|
+
export declare const DELEGATION_MAX_PER_SESSION_DEFAULT = 200;
|
|
32
|
+
/** Subagent transcript persistence — orphan adoption (CC parity: after a restart a deployment
|
|
33
|
+
* auto-adopts at most this many stale rows whose transcript mtime is inside the window; older ones
|
|
34
|
+
* stay MANUALLY continuable for the whole retention period — never deleted by the window). The
|
|
35
|
+
* TRIGGER is deployment-owned (core has no daemon and never revives runs nobody asked for); these
|
|
36
|
+
* constants are the shared vocabulary so every deployment adopts by the same numbers. */
|
|
37
|
+
export declare const ORPHAN_ADOPT_WINDOW_MS_DEFAULT: number;
|
|
38
|
+
export declare const ORPHAN_ADOPT_MAX_DEFAULT = 20;
|
|
39
|
+
/** Subagent transcript persistence — the default transcript retention period (CC
|
|
40
|
+
* cleanupPeriodDays parity). Core ships NO sweeper (design/151 ruling 2): the blessed path is the
|
|
41
|
+
* deployment calling `TaskRegistry.reapDurableAgents` with `maxAgeMs` derived from this (boot +
|
|
42
|
+
* every 24h is the reference cadence). */
|
|
43
|
+
export declare const SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT = 30;
|
|
24
44
|
/** Default idle TTL (days) before a cached session is evicted (config-catalog `session.idleTtlDays`). */
|
|
25
45
|
export declare const SESSION_DEFAULT_TTL_DAYS = 7;
|
|
26
46
|
/**
|
package/dist/config/defaults.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export const RETAIN_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
2
2
|
export const RETAIN_DEFAULT_MAX = 16;
|
|
3
|
+
export const DELEGATION_MAX_CONCURRENT_DEFAULT = 20;
|
|
4
|
+
export const DELEGATION_MAX_PER_SESSION_DEFAULT = 200;
|
|
5
|
+
export const ORPHAN_ADOPT_WINDOW_MS_DEFAULT = 48 * 60 * 60 * 1000;
|
|
6
|
+
export const ORPHAN_ADOPT_MAX_DEFAULT = 20;
|
|
7
|
+
export const SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT = 30;
|
|
3
8
|
export const SESSION_DEFAULT_TTL_DAYS = 7;
|
|
4
9
|
export const RUNNING_AGENT_OBSERVE_EVERY_BEATS = 4;
|
|
@@ -170,6 +170,7 @@ export interface BackgroundAgentRecord {
|
|
|
170
170
|
export declare const REVIVED_ROW_CLEARED_FIELDS: readonly ["settledAt", "stoppedBy", "completionId", "finalOutput", "finalOutputFull", "error", "errorCode", "errorRetryable", "errorKind", "errorRetryAfterMs", "resultIsPartial", "summary", "recentSteps", "editedFiles", "usage"];
|
|
171
171
|
/** Erase {@link REVIVED_ROW_CLEARED_FIELDS} from a record a revival is about to write back. */
|
|
172
172
|
export declare function clearRevivedRowTerminalPayload(record: BackgroundAgentRecord): void;
|
|
173
|
+
export declare function announceTranscriptIntegrityGapOnce(handle: string, sink: ((handle: string) => void) | undefined): void;
|
|
173
174
|
/** Content-free projection for list reads (design/151 HIGH-1: `summary`/`finalOutput`/`recentSteps`
|
|
174
175
|
* and friends NEVER ride a list — content is get-by-handle only, behind the full predicate). */
|
|
175
176
|
export interface BackgroundAgentRowSummary {
|
|
@@ -21,6 +21,19 @@ export function clearRevivedRowTerminalPayload(record) {
|
|
|
21
21
|
for (const field of REVIVED_ROW_CLEARED_FIELDS)
|
|
22
22
|
delete record[field];
|
|
23
23
|
}
|
|
24
|
+
const transcriptIntegrityAnnounced = new Set();
|
|
25
|
+
export function announceTranscriptIntegrityGapOnce(handle, sink) {
|
|
26
|
+
if (sink === undefined)
|
|
27
|
+
return;
|
|
28
|
+
if (transcriptIntegrityAnnounced.has(handle))
|
|
29
|
+
return;
|
|
30
|
+
transcriptIntegrityAnnounced.add(handle);
|
|
31
|
+
try {
|
|
32
|
+
sink(handle);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
24
37
|
export class BackgroundAgentStoreError extends Error {
|
|
25
38
|
code;
|
|
26
39
|
constructor(code, message) {
|
|
@@ -67,3 +67,16 @@ export type RuleSyncDropReason = keyof typeof RULE_SYNC_DROP_CODES;
|
|
|
67
67
|
/** The subset that may appear on a LOCAL quarantined row (design/182 §8.1 `quarantine` instruction /
|
|
68
68
|
* fence arm / local screening). `own_actor_forged` is inbound-only by construction. */
|
|
69
69
|
export type RuleQuarantineReason = Exclude<RuleSyncDropReason, "own_actor_forged">;
|
|
70
|
+
/**
|
|
71
|
+
* The presentation-tier registry (the server's wire whitelist retires into this
|
|
72
|
+
* table once shipped): WHO a notice code is for. `"user"` = a session-scoped disclosure the end
|
|
73
|
+
* user of that session should see (safe to project onto the session's event stream); `"operator"`
|
|
74
|
+
* = a deployment/config/ops fact for whoever runs the process. Presentation tier is a property of
|
|
75
|
+
* the CODE (closed set, one code one tier) — never of an individual emission, which is why this is
|
|
76
|
+
* a registry and not an EngineNotice field. Codes absent from the table read as `"operator"`
|
|
77
|
+
* (the conservative default: never push an unclassified code at an end user).
|
|
78
|
+
* Orthogonal to {@link NON_GOVERNANCE_MEMORY_CODES} (a retry-semantics table, not presentation).
|
|
79
|
+
*/
|
|
80
|
+
export declare const NOTICE_AUDIENCE: Readonly<Record<string, "user" | "operator">>;
|
|
81
|
+
/** The audience for `code` — table lookup with the conservative `"operator"` default. */
|
|
82
|
+
export declare function noticeAudienceOf(code: string): "user" | "operator";
|
|
@@ -29,6 +29,28 @@ export const NON_GOVERNANCE_MEMORY_CODES = new Set([
|
|
|
29
29
|
"memory.erasure_index_residue",
|
|
30
30
|
"memory.export_incomplete",
|
|
31
31
|
"memory.import_rejected",
|
|
32
|
+
"memory.hold_opened",
|
|
33
|
+
"memory.hold_released",
|
|
34
|
+
"memory.hold_disposed",
|
|
35
|
+
"memory.settlement_resolve_unattributed",
|
|
36
|
+
"memory.settlement_resolve_invalid",
|
|
37
|
+
"memory.settlement_resolve_unknown",
|
|
38
|
+
"memory.session_account_resolve_unattributed",
|
|
39
|
+
"memory.session_account_resolve_unknown",
|
|
40
|
+
"memory.hold_resolve_unattributed",
|
|
41
|
+
"memory.hold_resolve_invalid",
|
|
42
|
+
"memory.hold_resolve_unknown",
|
|
43
|
+
"memory.hold_resolve_invalid_state",
|
|
44
|
+
"memory.settlement_record_failed",
|
|
45
|
+
"memory.session_account_failed",
|
|
46
|
+
"memory.origin_clear_unattributed",
|
|
47
|
+
"memory.origin_clear_invalid",
|
|
48
|
+
"memory.origin_clear_unknown",
|
|
49
|
+
"memory.origin_clear_not_marked",
|
|
50
|
+
"memory.origin_clear_challenged",
|
|
51
|
+
"memory.origin_clear_pending",
|
|
52
|
+
"memory.origin_clear_conflict",
|
|
53
|
+
"memory.origin_clear_failed",
|
|
32
54
|
]);
|
|
33
55
|
export function governanceRetryClass(code) {
|
|
34
56
|
if (Object.prototype.hasOwnProperty.call(GOVERNANCE_CODES, code)) {
|
|
@@ -46,3 +68,14 @@ export const RULE_SYNC_DROP_CODES = {
|
|
|
46
68
|
below_gc_frontier: "local-quarantined",
|
|
47
69
|
server_rejected: "local-quarantined",
|
|
48
70
|
};
|
|
71
|
+
export const NOTICE_AUDIENCE = {
|
|
72
|
+
"memory.session_polluted": "user",
|
|
73
|
+
"memory.harvest_quarantined": "user",
|
|
74
|
+
"memory.delegation_static_mark_waived": "user",
|
|
75
|
+
"memory.hold_opened": "user",
|
|
76
|
+
"memory.hold_released": "user",
|
|
77
|
+
"memory.hold_disposed": "user",
|
|
78
|
+
};
|
|
79
|
+
export function noticeAudienceOf(code) {
|
|
80
|
+
return NOTICE_AUDIENCE[code] ?? "operator";
|
|
81
|
+
}
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ import type { AgentTool } from "../internal/harness-types.js";
|
|
|
36
36
|
import type { ImageContent, TextContent } from "../internal/llm.js";
|
|
37
37
|
import { type McpImageResizer } from "./image-downsample.js";
|
|
38
38
|
import type { McpServerSpec, OnElicit, ToolEffect } from "./types.js";
|
|
39
|
+
import { type ReminderDisclosureCounts } from "./reminder-disclosure.js";
|
|
39
40
|
/**
|
|
40
41
|
* The safety axes (design/77 §4 irreversibility, design/70 egress) derived from one materialized MCP
|
|
41
42
|
* tool's server-advertised `annotations`. These ride alongside the {@link AgentTool} (which is vendored
|
|
@@ -462,7 +463,11 @@ export declare function normalizeMcpToolSchema(schema: unknown): McpSchemaNormal
|
|
|
462
463
|
* combinator's own structure is legal JSON Schema ⇒ passes validateJsonSchemaShape too).
|
|
463
464
|
*/
|
|
464
465
|
export declare function mcpToolSchemaProblem(schema: unknown): string | undefined;
|
|
465
|
-
export declare function materializeMcpTools(specs: McpServerSpec[], principal?: string, onElicit?: OnElicit, imageResizer?: McpImageResizer
|
|
466
|
+
export declare function materializeMcpTools(specs: McpServerSpec[], principal?: string, onElicit?: OnElicit, imageResizer?: McpImageResizer, // design/116 CONFIRM-1 seam: deployment-injected; default = auto-detected sharp
|
|
467
|
+
reminderDisclosure?: {
|
|
468
|
+
reminderMark?: string;
|
|
469
|
+
counts?: ReminderDisclosureCounts;
|
|
470
|
+
}): Promise<MaterializedMcp>;
|
|
466
471
|
/**
|
|
467
472
|
* Fold the caller's AUTHORITATIVE per-tool override (design F: caller = trust root) over the server-hint axis.
|
|
468
473
|
* Unlike server hints, the caller may RAISE or LOWER any axis: `effect` sets the repeat-safety class (lower to
|
package/dist/core/mcp.js
CHANGED
|
@@ -11,6 +11,7 @@ import { MCP_IMAGE_MAX_BASE64, sharpImageResizer } from "./image-downsample.js";
|
|
|
11
11
|
import { sliceHeadSafe, sliceTailSafe } from "./surrogate-safe-slice.js";
|
|
12
12
|
import { truncateError } from "./tool-errors.js";
|
|
13
13
|
import { delimitUntrusted, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
14
|
+
import { discloseReminderShaped } from "./reminder-disclosure.js";
|
|
14
15
|
import { withContentOrigin } from "./memory-engine/content-origin.js";
|
|
15
16
|
import { validateJsonSchemaShape } from "./runner/strict-output-schema.js";
|
|
16
17
|
export const MCP_PREFIX = MCP_NAMESPACE.prefix;
|
|
@@ -649,7 +650,10 @@ export function mcpToolSchemaProblem(schema) {
|
|
|
649
650
|
}
|
|
650
651
|
return undefined;
|
|
651
652
|
}
|
|
652
|
-
export async function materializeMcpTools(specs, principal, onElicit, imageResizer) {
|
|
653
|
+
export async function materializeMcpTools(specs, principal, onElicit, imageResizer, reminderDisclosure) {
|
|
654
|
+
const mcpDisclosure = reminderDisclosure?.reminderMark !== undefined
|
|
655
|
+
? { mark: reminderDisclosure.reminderMark, windows: new Map(), ...(reminderDisclosure.counts !== undefined ? { counts: reminderDisclosure.counts } : {}) }
|
|
656
|
+
: undefined;
|
|
653
657
|
const clients = [];
|
|
654
658
|
const tools = [];
|
|
655
659
|
const toolAxes = [];
|
|
@@ -661,7 +665,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
661
665
|
const droppedTools = [];
|
|
662
666
|
let disposing = false;
|
|
663
667
|
const serverHandles = [];
|
|
664
|
-
const settled = await Promise.allSettled(specs.map((spec) => connectServer(spec, principal, onElicit, imageResizer)));
|
|
668
|
+
const settled = await Promise.allSettled(specs.map((spec) => connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure)));
|
|
665
669
|
try {
|
|
666
670
|
for (let i = 0; i < specs.length; i++) {
|
|
667
671
|
const r = settled[i];
|
|
@@ -728,7 +732,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
728
732
|
try {
|
|
729
733
|
const listed = await listToolsLenient(h.client);
|
|
730
734
|
cacheMcpToolMetadata(h.client, listed.tools);
|
|
731
|
-
const { serverTools, serverAxes, dropped } = intakeListedTools(listed, h.spec, h.client, h.health, imageResizer);
|
|
735
|
+
const { serverTools, serverAxes, dropped } = intakeListedTools(listed, h.spec, h.client, h.health, imageResizer, mcpDisclosure);
|
|
732
736
|
const newNames = serverTools.map((t) => t.name);
|
|
733
737
|
const added = newNames.filter((n) => !h.toolNames.includes(n));
|
|
734
738
|
const removed = h.toolNames.filter((n) => !newNames.includes(n));
|
|
@@ -1119,7 +1123,7 @@ const LENIENT_CALL_TOOL_RESULT_SCHEMA = { safeParse: parseCallToolResultLenient
|
|
|
1119
1123
|
async function listToolsLenient(client, options) {
|
|
1120
1124
|
return client.request({ method: "tools/list", params: {} }, LenientListToolsResultSchema, options);
|
|
1121
1125
|
}
|
|
1122
|
-
async function connectServer(spec, principal, onElicit, imageResizer) {
|
|
1126
|
+
async function connectServer(spec, principal, onElicit, imageResizer, reminderDisclosure) {
|
|
1123
1127
|
const elicitOn = spec.elicitation === true && onElicit !== undefined;
|
|
1124
1128
|
const health = { dead: false, pendingElicitations: 0, lastElicitationClosedAt: 0 };
|
|
1125
1129
|
const client = new Client({ name: `sema-core/${spec.name}`, version: "0.1.0" }, { capabilities: elicitOn ? { elicitation: { form: {} } } : {} });
|
|
@@ -1155,7 +1159,7 @@ async function connectServer(spec, principal, onElicit, imageResizer) {
|
|
|
1155
1159
|
};
|
|
1156
1160
|
const listed = await listToolsLenient(client, startupOpts);
|
|
1157
1161
|
cacheMcpToolMetadata(client, listed.tools);
|
|
1158
|
-
const { serverTools, serverAxes, dropped } = intakeListedTools(listed, spec, client, health, imageResizer);
|
|
1162
|
+
const { serverTools, serverAxes, dropped } = intakeListedTools(listed, spec, client, health, imageResizer, reminderDisclosure);
|
|
1159
1163
|
const caps = client.getServerCapabilities();
|
|
1160
1164
|
const resourceInfo = caps?.resources
|
|
1161
1165
|
? {
|
|
@@ -1188,7 +1192,7 @@ async function connectServer(spec, principal, onElicit, imageResizer) {
|
|
|
1188
1192
|
throw err;
|
|
1189
1193
|
}
|
|
1190
1194
|
}
|
|
1191
|
-
function intakeListedTools(listed, spec, client, health, imageResizer) {
|
|
1195
|
+
function intakeListedTools(listed, spec, client, health, imageResizer, reminderDisclosure) {
|
|
1192
1196
|
const serverTools = [];
|
|
1193
1197
|
const serverAxes = [];
|
|
1194
1198
|
const dropped = [];
|
|
@@ -1276,8 +1280,31 @@ function intakeListedTools(listed, spec, client, health, imageResizer) {
|
|
|
1276
1280
|
type: "text",
|
|
1277
1281
|
text: `[structuredContent] JSON with schema: ${inferCompactSchema(sc)}\n${truncateError(JSON.stringify(sc))}`,
|
|
1278
1282
|
});
|
|
1279
|
-
return { content, details: { type: "mcp", structuredContent: sc }, terminate: false };
|
|
1280
1283
|
}
|
|
1284
|
+
if (reminderDisclosure !== undefined) {
|
|
1285
|
+
const textIdx = [];
|
|
1286
|
+
const segments = [];
|
|
1287
|
+
content.forEach((b, i) => {
|
|
1288
|
+
if (b.type === "text" && typeof b.text === "string") {
|
|
1289
|
+
textIdx.push(i);
|
|
1290
|
+
segments.push(b.text);
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
const d = discloseReminderShaped({
|
|
1294
|
+
segments,
|
|
1295
|
+
mark: reminderDisclosure.mark,
|
|
1296
|
+
outlet: "mcp",
|
|
1297
|
+
defuseExactMark: true,
|
|
1298
|
+
throttle: { key: `${spec.name}:${remoteName}`, windows: reminderDisclosure.windows },
|
|
1299
|
+
...(reminderDisclosure.counts !== undefined ? { counts: reminderDisclosure.counts } : {}),
|
|
1300
|
+
});
|
|
1301
|
+
if (d.defused)
|
|
1302
|
+
textIdx.forEach((ci, si) => (content[ci] = { type: "text", text: d.segments[si] }));
|
|
1303
|
+
if (d.trailer !== undefined)
|
|
1304
|
+
content.push({ type: "text", text: d.trailer });
|
|
1305
|
+
}
|
|
1306
|
+
if (sc !== undefined)
|
|
1307
|
+
return { content, details: { type: "mcp", structuredContent: sc }, terminate: false };
|
|
1281
1308
|
return { content, details: res, terminate: false };
|
|
1282
1309
|
},
|
|
1283
1310
|
});
|