@sema-agent/core 5.1.0 → 5.3.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 +54 -0
- package/dist/agents/roster-store.js +6 -1
- package/dist/bin/sema-tb.js +3 -4
- package/dist/brain/openai.js +3 -5
- package/dist/brain/terminal-cause.d.ts +1 -1
- package/dist/core/a2a-task-state.d.ts +15 -0
- package/dist/core/a2a-task-state.js +68 -0
- package/dist/core/a2a.d.ts +42 -0
- package/dist/core/a2a.js +651 -0
- package/dist/core/checkpoint-store.d.ts +6 -1
- package/dist/core/checkpoint-store.js +3 -1
- package/dist/core/hooks.js +8 -3
- package/dist/core/mcp.d.ts +12 -5
- package/dist/core/mcp.js +11 -31
- package/dist/core/memory-engine/dual-root.js +2 -1
- package/dist/core/memory-engine/engine.d.ts +4 -0
- package/dist/core/memory-engine/engine.js +25 -6
- package/dist/core/memory-engine/file-backend.d.ts +7 -5
- package/dist/core/memory-engine/file-backend.js +2 -2
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +6 -2
- package/dist/core/memory-engine/layout.js +73 -31
- package/dist/core/memory.js +6 -0
- package/dist/core/protocol-naming.d.ts +7 -0
- package/dist/core/protocol-naming.js +32 -0
- package/dist/core/protocol-table.d.ts +13 -8
- package/dist/core/protocol-table.js +34 -15
- package/dist/core/runner/prepare-memory.js +4 -4
- package/dist/core/runner/prepare-task.d.ts +7 -0
- package/dist/core/runner/prepare-task.js +73 -28
- package/dist/core/runner/runtask.js +42 -9
- package/dist/core/runner/tool-disclosure.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.js +7 -2
- package/dist/core/runner/turn-attachments.d.ts +1 -2
- package/dist/core/runner/turn-attachments.js +1 -12
- package/dist/core/store-contracts/background-agent-store-contract.d.ts +5 -0
- package/dist/core/store-contracts/background-agent-store-contract.js +213 -0
- package/dist/core/task-registry-agent.d.ts +14 -1
- package/dist/core/task-registry-agent.js +1 -1
- package/dist/core/tool-policy.d.ts +1 -0
- package/dist/core/tool-policy.js +12 -2
- package/dist/core/types.d.ts +16 -2
- package/dist/index.d.ts +10 -5
- package/dist/index.js +7 -3
- package/dist/orchestration/run-workflow-tool.js +5 -1
- package/dist/prompt-assembly/assemble.js +0 -1
- package/dist/prompt-assembly/event-registry.js +1 -0
- package/dist/stores/cc/mailbox-store.js +58 -14
- package/dist/stores/file/file-snapshot-store.d.ts +9 -1
- package/dist/stores/file/file-snapshot-store.js +28 -5
- package/dist/stores/file/fs-atomic.d.ts +4 -1
- package/dist/stores/file/fs-atomic.js +2 -1
- package/dist/stores/file/index.d.ts +10 -3
- package/dist/stores/file/index.js +4 -3
- package/dist/stores/file/mailbox-store.d.ts +5 -0
- package/dist/stores/file/mailbox-store.js +15 -3
- package/dist/stores/file/session-policy-store.d.ts +11 -8
- package/dist/stores/file/session-policy-store.js +21 -4
- package/dist/stores/file/session-store.d.ts +9 -1
- package/dist/stores/file/session-store.js +19 -4
- package/dist/tools/fs/fs-search-tools.js +9 -0
- package/dist/tools/fs/fs-shared.d.ts +1 -1
- package/dist/tools/fs/fs-shared.js +1 -1
- package/dist/tools/todo.js +13 -6
- package/package.json +1 -1
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
export type ProtocolId = "mcp";
|
|
2
|
-
export interface
|
|
1
|
+
export type ProtocolId = "mcp" | "a2a";
|
|
2
|
+
export interface ProtocolPeerTool {
|
|
3
|
+
peer: string;
|
|
4
|
+
tool: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProtocolNamespace<P extends string = string> {
|
|
3
7
|
readonly id: ProtocolId;
|
|
4
|
-
readonly prefix:
|
|
8
|
+
readonly prefix: P;
|
|
5
9
|
makeName(peer: string, tool: string): string;
|
|
6
|
-
parse(name: string):
|
|
7
|
-
peer: string;
|
|
8
|
-
tool: string;
|
|
9
|
-
} | undefined;
|
|
10
|
+
parse(name: string): ProtocolPeerTool | undefined;
|
|
10
11
|
displayGroupKey(name: string): string;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
declare const MCP_PREFIX_NAME: "mcp__";
|
|
14
|
+
declare const A2A_PREFIX_NAME: "a2a__";
|
|
15
|
+
export declare const MCP_NAMESPACE: ProtocolNamespace<typeof MCP_PREFIX_NAME>;
|
|
16
|
+
export declare const A2A_NAMESPACE: ProtocolNamespace<typeof A2A_PREFIX_NAME>;
|
|
13
17
|
export declare const PROTOCOL_TABLE: readonly ProtocolNamespace[];
|
|
14
18
|
export declare function protocolOf(name: string): ProtocolNamespace | undefined;
|
|
19
|
+
export {};
|
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
const NAME_SEP = "__";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
makeName: (peer, tool) => `mcp${NAME_SEP}${peer}${NAME_SEP}${tool}`,
|
|
6
|
-
parse(name) {
|
|
7
|
-
if (!name.startsWith(this.prefix))
|
|
2
|
+
function makeProtocolNamespace(id, prefix) {
|
|
3
|
+
const parse = (name) => {
|
|
4
|
+
if (!name.startsWith(prefix))
|
|
8
5
|
return undefined;
|
|
9
|
-
const rest = name.slice(
|
|
6
|
+
const rest = name.slice(prefix.length);
|
|
10
7
|
const sep = rest.indexOf(NAME_SEP);
|
|
11
8
|
if (sep <= 0)
|
|
12
9
|
return undefined;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const tool = rest.slice(sep + NAME_SEP.length);
|
|
11
|
+
if (tool.length === 0)
|
|
12
|
+
return undefined;
|
|
13
|
+
return { peer: rest.slice(0, sep), tool };
|
|
14
|
+
};
|
|
15
|
+
const makeName = (peer, tool) => {
|
|
16
|
+
if (peer.length === 0 || tool.length === 0) {
|
|
17
|
+
throw new Error(`protocol table (${id}): peer and tool must both be non-empty (got peer=${JSON.stringify(peer)}, tool=${JSON.stringify(tool)})`);
|
|
18
|
+
}
|
|
19
|
+
if (peer.includes(NAME_SEP)) {
|
|
20
|
+
throw new Error(`protocol table (${id}): peer must not contain the ${NAME_SEP} separator (got ${JSON.stringify(peer)}) — the composed name would parse back as a DIFFERENT peer`);
|
|
21
|
+
}
|
|
22
|
+
return `${prefix}${peer}${NAME_SEP}${tool}`;
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
id,
|
|
26
|
+
prefix,
|
|
27
|
+
makeName,
|
|
28
|
+
parse,
|
|
29
|
+
displayGroupKey: (name) => {
|
|
30
|
+
const p = parse(name);
|
|
31
|
+
return p === undefined ? name : `${prefix}${p.peer}${NAME_SEP}*`;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const MCP_PREFIX_NAME = `mcp${NAME_SEP}`;
|
|
36
|
+
const A2A_PREFIX_NAME = `a2a${NAME_SEP}`;
|
|
37
|
+
export const MCP_NAMESPACE = makeProtocolNamespace("mcp", MCP_PREFIX_NAME);
|
|
38
|
+
export const A2A_NAMESPACE = makeProtocolNamespace("a2a", A2A_PREFIX_NAME);
|
|
39
|
+
export const PROTOCOL_TABLE = [MCP_NAMESPACE, A2A_NAMESPACE];
|
|
21
40
|
export function protocolOf(name) {
|
|
22
41
|
return PROTOCOL_TABLE.find((ns) => name.startsWith(ns.prefix));
|
|
23
42
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { adoptLegacyRepoDirs,
|
|
1
|
+
import { adoptLegacyRepoDirs, deriveRepoControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
2
2
|
import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
|
|
3
3
|
import { normalizeMemorySpec } from "../memory.js";
|
|
4
4
|
import { MemoryEngine } from "../memory-engine/engine.js";
|
|
@@ -38,7 +38,7 @@ export async function prepareMemory(input) {
|
|
|
38
38
|
recordProjectIdHint(engineRoot, repoRoot, marker.projectId);
|
|
39
39
|
if (firstSwitch) {
|
|
40
40
|
try {
|
|
41
|
-
const oldCtl =
|
|
41
|
+
const oldCtl = deriveRepoControlPlaneDir(engineRoot, repoRoot);
|
|
42
42
|
const newCtl = deriveProjectControlDir(engineRoot, marker.projectId);
|
|
43
43
|
const drained = drainMemoryAnnouncements(oldCtl);
|
|
44
44
|
for (const ann of drained.queue)
|
|
@@ -100,7 +100,7 @@ export async function prepareMemory(input) {
|
|
|
100
100
|
const projectEngine = new MemoryEngine({
|
|
101
101
|
backend,
|
|
102
102
|
memoryDir,
|
|
103
|
-
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) :
|
|
103
|
+
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
104
104
|
});
|
|
105
105
|
const personalEngine = createPersonalEngine();
|
|
106
106
|
const p = planes;
|
|
@@ -140,7 +140,7 @@ export async function prepareMemory(input) {
|
|
|
140
140
|
const engine = new MemoryEngine({
|
|
141
141
|
backend,
|
|
142
142
|
memoryDir,
|
|
143
|
-
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) :
|
|
143
|
+
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
144
144
|
});
|
|
145
145
|
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope);
|
|
146
146
|
writeEngine = engine;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AgentHarness, type ThinkingLevel } from "../../internal/harness.js";
|
|
2
2
|
import type { Model } from "../../internal/llm.js";
|
|
3
3
|
import { type MaterializedMcp } from "../mcp.js";
|
|
4
|
+
import { type MaterializedA2a } from "../a2a.js";
|
|
4
5
|
import type { HarvestReport, MemorySessionHandle } from "../memory-engine/types.js";
|
|
5
6
|
import { StoredSession } from "../session.js";
|
|
6
7
|
import type { SessionStore } from "../session.js";
|
|
@@ -15,6 +16,7 @@ import type { MemoryEngine } from "../memory-engine/engine.js";
|
|
|
15
16
|
import { type ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
16
17
|
import type { TaskNotificationPayload } from "../task-notification.js";
|
|
17
18
|
import { type CwdRef } from "../../tools/fs/index.js";
|
|
19
|
+
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
18
20
|
import type { Runner } from "./runtask.js";
|
|
19
21
|
import { type CheckpointGate, type CheckpointState, type CheckpointToken, type ResourceLedger, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
20
22
|
import type { ActiveWorktreeSession, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
@@ -36,6 +38,7 @@ export interface Prepared {
|
|
|
36
38
|
thinking?: ThinkingLevel;
|
|
37
39
|
compModel?: Model;
|
|
38
40
|
mcp: MaterializedMcp;
|
|
41
|
+
a2a?: MaterializedA2a;
|
|
39
42
|
blockedRef: BlockedRef;
|
|
40
43
|
outputRef: OutputRef;
|
|
41
44
|
abortController: AbortController;
|
|
@@ -186,6 +189,10 @@ export interface Prepared {
|
|
|
186
189
|
path: string;
|
|
187
190
|
contentHash: string | null;
|
|
188
191
|
}>;
|
|
192
|
+
workflowSizeGuideline?: {
|
|
193
|
+
legGuideline: WorkflowSizeGuideline;
|
|
194
|
+
current: () => WorkflowSizeGuideline;
|
|
195
|
+
};
|
|
189
196
|
detectExternalChanges?: (maxFiles: number) => Promise<{
|
|
190
197
|
changed: Array<{
|
|
191
198
|
path: string;
|
|
@@ -9,6 +9,7 @@ import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from
|
|
|
9
9
|
import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
|
|
10
10
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
11
11
|
import { materializeMcpTools } from "../mcp.js";
|
|
12
|
+
import { materializeA2aTools } from "../a2a.js";
|
|
12
13
|
import { Type } from "typebox";
|
|
13
14
|
import { brainToRuntime } from "../runtime.js";
|
|
14
15
|
import { StoredSession, isSessionConflict, hasSessionFork } from "../session.js";
|
|
@@ -16,7 +17,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents
|
|
|
16
17
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
17
18
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
18
19
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
19
|
-
import { combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
20
|
+
import { combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
20
21
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
21
22
|
import { computeCallCap, createCallCapRef, softExecDeadlineMs, toolCutDeadlineMs, WALLTIME_STALL_CONNECT_MS, WALLTIME_STALL_FIRST_TOKEN_MS, WALLTIME_STALL_IDLE_MS } from "./call-cap.js";
|
|
22
23
|
import { createCutKillRegistry } from "./cut-kill.js";
|
|
@@ -30,7 +31,7 @@ import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-det
|
|
|
30
31
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
31
32
|
import { defineTool, isDefineToolProduct } from "../tools.js";
|
|
32
33
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
33
|
-
import { protocolOf } from "../protocol-table.js";
|
|
34
|
+
import { protocolOf, PROTOCOL_TABLE } from "../protocol-table.js";
|
|
34
35
|
import { pathToUri } from "../lsp-protocol.js";
|
|
35
36
|
import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
|
|
36
37
|
import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
@@ -64,6 +65,7 @@ import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
|
64
65
|
import { createPresentPlanTool, createEnterPlanModeTool, PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "../present-plan-tool.js";
|
|
65
66
|
import { isSelfOrchestrationActive, selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
66
67
|
import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestration/run-workflow-tool.js";
|
|
68
|
+
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
67
69
|
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
68
70
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
69
71
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, RESOURCE_CHECKPOINT_VERSION, WALLTIME_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
|
|
@@ -82,6 +84,7 @@ function sanitizedTtlMs(ttlMs) {
|
|
|
82
84
|
const ungatedWarnedShapes = new WeakMap();
|
|
83
85
|
const advisedPolicyNames = new WeakMap();
|
|
84
86
|
const ADVISED_KEYS_CAP = 64;
|
|
87
|
+
const NAMESPACED_NAME_SHAPES = PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(", ");
|
|
85
88
|
export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
86
89
|
export function checkpointScopeOf(spec) {
|
|
87
90
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
@@ -261,7 +264,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
261
264
|
let shellGatedMonitor = false;
|
|
262
265
|
for (const t of spec.tools ?? []) {
|
|
263
266
|
if (t.name.includes("__")) {
|
|
264
|
-
const e = new Error(`Tool name "${t.name}" is invalid: "__" is reserved for the
|
|
267
|
+
const e = new Error(`Tool name "${t.name}" is invalid: "__" is reserved for the engine's protocol tool namespaces (${NAMESPACED_NAME_SHAPES}) and must not appear in a caller tool name.`);
|
|
265
268
|
e.code = "config.tool_name_invalid";
|
|
266
269
|
throw e;
|
|
267
270
|
}
|
|
@@ -479,7 +482,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
479
482
|
return maybeOffload(tool, policy);
|
|
480
483
|
return maybeOffload(tool, explicitGlobalThreshold === undefined ? policy : undefined);
|
|
481
484
|
};
|
|
482
|
-
const
|
|
485
|
+
const remoteToolOffload = (tool) => {
|
|
483
486
|
if (explicitGlobalThreshold !== undefined)
|
|
484
487
|
return maybeOffload(tool);
|
|
485
488
|
return maybeOffload(tool, { offloadThresholdChars: tool.mcpMaxResultSizeChars ?? 50_000 });
|
|
@@ -492,6 +495,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
492
495
|
}
|
|
493
496
|
let ownedEnv;
|
|
494
497
|
let mcp;
|
|
498
|
+
let a2a;
|
|
495
499
|
if (internals?.requestedCwd !== undefined && deps.executionEnvFactory === undefined) {
|
|
496
500
|
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-unsupported leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
497
501
|
const e = new Error(`Agent cwd "${internals.requestedCwd}" cannot take effect: this deployment has no executionEnvFactory (a static execution environment cannot be re-rooted per agent). Drop the cwd parameter or deploy a factory.`);
|
|
@@ -969,8 +973,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
969
973
|
`(per-principal entitlement governance, not a misconfiguration — the run_workflow tool is not mounted)`), { phase: "config", sessionId });
|
|
970
974
|
}
|
|
971
975
|
let workflowToolsActive = false;
|
|
976
|
+
let workflowSizeGuideline;
|
|
972
977
|
if (selfOrchestrationActive && runnerSelf && deps.workflowScriptRunner && deps.workflowGovernanceBaseline) {
|
|
973
978
|
workflowToolsActive = true;
|
|
979
|
+
const currentSizeGuideline = () => resolveWorkflowSizeGuideline(deps.workflowLimits?.sizeGuideline).size;
|
|
980
|
+
workflowSizeGuideline = { legGuideline: currentSizeGuideline(), current: currentSizeGuideline };
|
|
974
981
|
toolEffects.set(RUN_WORKFLOW_TOOL_NAME, "write");
|
|
975
982
|
tools.push(await createRunWorkflowTool({
|
|
976
983
|
runner: runnerSelf,
|
|
@@ -1093,7 +1100,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1093
1100
|
throw e;
|
|
1094
1101
|
}
|
|
1095
1102
|
}
|
|
1096
|
-
tools.push(...mcp.tools.map((t) =>
|
|
1103
|
+
tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
|
|
1097
1104
|
const rebuildHarnessToolsRef = {};
|
|
1098
1105
|
const toolCallGateArmedRef = { armed: false };
|
|
1099
1106
|
if (spec.mcp?.length) {
|
|
@@ -1121,7 +1128,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1121
1128
|
const pushable = r.tools.filter((t) => !excludedSet.has(t.name));
|
|
1122
1129
|
const excludedNow = r.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
|
|
1123
1130
|
try {
|
|
1124
|
-
|
|
1131
|
+
foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
|
|
1125
1132
|
}
|
|
1126
1133
|
catch (foldErr) {
|
|
1127
1134
|
lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
|
|
@@ -1135,7 +1142,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1135
1142
|
tools.splice(i, 1);
|
|
1136
1143
|
}
|
|
1137
1144
|
}
|
|
1138
|
-
tools.push(...pushable.map((t) =>
|
|
1145
|
+
tools.push(...pushable.map((t) => remoteToolOffload(t)));
|
|
1139
1146
|
changed = true;
|
|
1140
1147
|
const detail = [];
|
|
1141
1148
|
const shownAdded = r.added.filter((n) => !excludedSet.has(n));
|
|
@@ -1165,7 +1172,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1165
1172
|
});
|
|
1166
1173
|
toolEffects.set("RefreshMcpTools", "read");
|
|
1167
1174
|
}
|
|
1168
|
-
const
|
|
1175
|
+
const foldProtocolAxes = (axes, protocolLabel) => {
|
|
1169
1176
|
for (const axis of axes) {
|
|
1170
1177
|
if (axis.irreversibility === "always") {
|
|
1171
1178
|
irreversibilityTier.set(axis.name, "always");
|
|
@@ -1173,7 +1180,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1173
1180
|
}
|
|
1174
1181
|
if (axis.egress) {
|
|
1175
1182
|
if (axis.effect !== undefined && axis.effect !== "write") {
|
|
1176
|
-
const e = new Error(
|
|
1183
|
+
const e = new Error(`${protocolLabel} tool "${axis.name}" resolves to egress:true with effect:"${axis.effect}" — an egress tool (external write) must have effect:"write". Clear egress (toolAxes egress:false) if it is a pure read, or set effect:"write".`);
|
|
1177
1184
|
e.code = "config.egress_requires_write_effect";
|
|
1178
1185
|
throw e;
|
|
1179
1186
|
}
|
|
@@ -1183,7 +1190,25 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1183
1190
|
toolEffects.set(axis.name, axis.effect ?? "write");
|
|
1184
1191
|
}
|
|
1185
1192
|
};
|
|
1186
|
-
|
|
1193
|
+
foldProtocolAxes(mcp.toolAxes, "MCP");
|
|
1194
|
+
a2a = spec.a2a?.length
|
|
1195
|
+
? await materializeA2aTools(spec.a2a, spec.principal, abortController.signal)
|
|
1196
|
+
: { tools: [], toolAxes: [], warnings: [], statuses: [], refresh: async () => [], dispose: async () => { } };
|
|
1197
|
+
for (const w of a2a.warnings)
|
|
1198
|
+
deps.onError?.(w, { phase: "a2a", sessionId });
|
|
1199
|
+
{
|
|
1200
|
+
const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
1201
|
+
const clash = a2a.tools.find((t) => callerNames.has(t.name));
|
|
1202
|
+
if (clash) {
|
|
1203
|
+
await a2a.dispose();
|
|
1204
|
+
await mcp.dispose();
|
|
1205
|
+
const e = new Error(`Tool name "${clash.name}" is reserved by an injected A2A tool — a caller tool of the same name would silently shadow it.`);
|
|
1206
|
+
e.code = "config.reserved_tool_name";
|
|
1207
|
+
throw e;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
tools.push(...a2a.tools.map((t) => remoteToolOffload(t)));
|
|
1211
|
+
foldProtocolAxes(a2a.toolAxes, "A2A");
|
|
1187
1212
|
const callCapOn = spec.limits?.callCapByDeadline !== false;
|
|
1188
1213
|
const callCapRef = spec.limits?.timeoutSec &&
|
|
1189
1214
|
spec.limits.timeoutSec > 0 &&
|
|
@@ -1861,11 +1886,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1861
1886
|
}
|
|
1862
1887
|
}
|
|
1863
1888
|
const userToolNames = (spec.tools ?? []).map((t) => t.name);
|
|
1864
|
-
const
|
|
1889
|
+
const protocolToolNames = [...mcp.tools.map((t) => t.name), ...a2a.tools.map((t) => t.name)];
|
|
1865
1890
|
const deferred = classifyDeferred({
|
|
1866
1891
|
specs: spec.tools ?? [],
|
|
1867
|
-
|
|
1868
|
-
fullTools: tools.filter((t) => userToolNames.includes(t.name) ||
|
|
1892
|
+
protocolToolNames,
|
|
1893
|
+
fullTools: tools.filter((t) => userToolNames.includes(t.name) || protocolToolNames.includes(t.name)),
|
|
1869
1894
|
deferMode: deps.deferMode,
|
|
1870
1895
|
model,
|
|
1871
1896
|
deferNames: (toolFaceSnapshot.defer ?? []).filter((n) => tools.some((t) => t.name === n)),
|
|
@@ -2211,7 +2236,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2211
2236
|
throw err;
|
|
2212
2237
|
}
|
|
2213
2238
|
if (n.includes("__") && protocolOf(n) === undefined) {
|
|
2214
|
-
const err = new Error(`tool policy ${kind}-list entry "${n}"
|
|
2239
|
+
const err = new Error(`tool policy ${kind}-list entry "${n}" carries the "__" namespace separator but no protocol prefix, and matches nothing in this run's roster — protocol tools are named ${NAMESPACED_NAME_SHAPES}, and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with the owning protocol's marker.`);
|
|
2215
2240
|
err.code = "config.legacy_tool_name";
|
|
2216
2241
|
throw err;
|
|
2217
2242
|
}
|
|
@@ -2364,7 +2389,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2364
2389
|
toolName: creq.toolName,
|
|
2365
2390
|
toolCallId: creq.toolCallId,
|
|
2366
2391
|
args: editArgs,
|
|
2367
|
-
message: re.message ??
|
|
2392
|
+
message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2368
2393
|
...askSourceIdentity(),
|
|
2369
2394
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2370
2395
|
}, onAskOf, csignal ?? abortController.signal);
|
|
@@ -2410,7 +2435,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2410
2435
|
toolName: creq.toolName,
|
|
2411
2436
|
toolCallId: creq.toolCallId,
|
|
2412
2437
|
args: presentedArgs,
|
|
2413
|
-
message: first.message ??
|
|
2438
|
+
message: first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2414
2439
|
...askSourceIdentity(),
|
|
2415
2440
|
...(first.action === "ask" && first.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2416
2441
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
@@ -2466,7 +2491,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2466
2491
|
toolName: creq.toolName,
|
|
2467
2492
|
toolCallId: creq.toolCallId,
|
|
2468
2493
|
args: presentedArgs,
|
|
2469
|
-
message: decision.message ??
|
|
2494
|
+
message: decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2470
2495
|
...askSourceIdentity(),
|
|
2471
2496
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2472
2497
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
@@ -2706,7 +2731,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2706
2731
|
const preview = approvalPreviewOf(req.toolName, req.args);
|
|
2707
2732
|
return preview !== undefined ? { preview } : {};
|
|
2708
2733
|
})(),
|
|
2709
|
-
message: decision.message ??
|
|
2734
|
+
message: decision.message ?? `approval required for "${req.toolName}"`,
|
|
2710
2735
|
...askSourceIdentity(),
|
|
2711
2736
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2712
2737
|
}, onAsk, abortController.signal);
|
|
@@ -2750,17 +2775,33 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2750
2775
|
const checkpointStore = spec.checkpointStore ?? deps.checkpointStore;
|
|
2751
2776
|
const durableApproval = spec.durableApproval ??
|
|
2752
2777
|
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
2753
|
-
const
|
|
2778
|
+
const inFlightSpendMicroUsd = () => {
|
|
2779
|
+
const own = liveSpendRef.get?.().costMicroUsd ?? 0;
|
|
2780
|
+
const seededNested = resume?.seed.nestedStats.costMicroUsd;
|
|
2781
|
+
const nestedDelta = nestedStats.costMicroUsd - (typeof seededNested === "number" && Number.isFinite(seededNested) ? seededNested : 0);
|
|
2782
|
+
return (Number.isFinite(own) ? own : 0) + Math.max(0, Number.isFinite(nestedDelta) ? nestedDelta : 0);
|
|
2783
|
+
};
|
|
2784
|
+
const repairBundleForCheckpoint = (parkedSpendMicroUsd) => {
|
|
2785
|
+
const carried = internals?.repairBundle !== undefined
|
|
2786
|
+
? structuredClone(internals.repairBundle)
|
|
2787
|
+
: resume?.seed.repairBundle !== undefined
|
|
2788
|
+
? structuredClone(resume.seed.repairBundle)
|
|
2789
|
+
: undefined;
|
|
2790
|
+
if (carried === undefined)
|
|
2791
|
+
return undefined;
|
|
2792
|
+
if (parkedSpendMicroUsd === undefined || !Number.isFinite(parkedSpendMicroUsd) || parkedSpendMicroUsd <= 0)
|
|
2793
|
+
return carried;
|
|
2794
|
+
const prior = typeof carried.spentMicroUsd === "number" && Number.isFinite(carried.spentMicroUsd) ? Math.max(0, carried.spentMicroUsd) : 0;
|
|
2795
|
+
carried.spentMicroUsd = prior + parkedSpendMicroUsd;
|
|
2796
|
+
return carried;
|
|
2797
|
+
};
|
|
2798
|
+
const serializeCheckpointState = (workspaceHandle, parkedSpendMicroUsd) => ({
|
|
2754
2799
|
activeTools: [...activeTools],
|
|
2755
2800
|
outputRef: { value: outputRef.value, set: outputRef.set },
|
|
2756
2801
|
nestedStats: { ...nestedStats },
|
|
2757
2802
|
consolidationNotes: undefined,
|
|
2758
2803
|
readFileState: readFileStateForCheckpoint ? [...readFileStateForCheckpoint.entries()] : undefined,
|
|
2759
|
-
repairBundle:
|
|
2760
|
-
? structuredClone(internals.repairBundle)
|
|
2761
|
-
: resume?.seed.repairBundle !== undefined
|
|
2762
|
-
? structuredClone(resume.seed.repairBundle)
|
|
2763
|
-
: undefined,
|
|
2804
|
+
repairBundle: repairBundleForCheckpoint(parkedSpendMicroUsd),
|
|
2764
2805
|
workspaceHandle,
|
|
2765
2806
|
handsCwd: handsCwdRef?.current,
|
|
2766
2807
|
activeWorktree: worktreeSessionRef?.current ? { ...worktreeSessionRef.current } : undefined,
|
|
@@ -2978,7 +3019,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2978
3019
|
leafId,
|
|
2979
3020
|
gate,
|
|
2980
3021
|
pendingAction: { kind: "plan_review" },
|
|
2981
|
-
state: serializeCheckpointState(remoteHandle),
|
|
3022
|
+
state: serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd()),
|
|
2982
3023
|
status: "pending",
|
|
2983
3024
|
createdAt: mintedAt,
|
|
2984
3025
|
suspendedAt: now(),
|
|
@@ -3092,7 +3133,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3092
3133
|
};
|
|
3093
3134
|
const scope = durableApproval?.scope || checkpointScopeOf({ principal: spec.principal });
|
|
3094
3135
|
const ttlMs = sanitizedTtlMs(durableApproval?.ttlMs);
|
|
3095
|
-
const checkpointState = serializeCheckpointState(remoteHandle);
|
|
3136
|
+
const checkpointState = serializeCheckpointState(remoteHandle, inFlightSpendMicroUsd());
|
|
3096
3137
|
const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
|
|
3097
3138
|
const mintedAt = Date.now();
|
|
3098
3139
|
cp = {
|
|
@@ -3396,7 +3437,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3396
3437
|
? undefined
|
|
3397
3438
|
: async (path) => {
|
|
3398
3439
|
try {
|
|
3399
|
-
const d = await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal);
|
|
3440
|
+
const d = refuseOutOfContractDecision(await denyNarrowingPolicy.check({ toolName: "Read", args: { file_path: path }, toolCallId: "changed-files-visibility-probe" }, abortController.signal));
|
|
3400
3441
|
return d.action === "deny";
|
|
3401
3442
|
}
|
|
3402
3443
|
catch {
|
|
@@ -3488,12 +3529,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3488
3529
|
: undefined;
|
|
3489
3530
|
overheadState.promptChars = systemPrompt.length;
|
|
3490
3531
|
const preparedHolder = {};
|
|
3491
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), ownedEnv, suspendRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3532
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), ownedEnv, suspendRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(callCapRef ? { callCapRef } : {}), ...(cutKills ? { cutKills } : {}), ...(callCapRef ? { callIssuedAtRef } : {}), suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3492
3533
|
const prepared = buildPrepared();
|
|
3493
3534
|
preparedHolder.current = prepared;
|
|
3494
3535
|
return prepared;
|
|
3495
3536
|
}
|
|
3496
3537
|
catch (prepareErr) {
|
|
3538
|
+
if (a2a) {
|
|
3539
|
+
const a2aHandle = a2a;
|
|
3540
|
+
await settleTeardownLeg(() => a2aHandle.dispose(), "a2a.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
3541
|
+
}
|
|
3497
3542
|
if (mcp) {
|
|
3498
3543
|
const mcpHandle = mcp;
|
|
3499
3544
|
await settleTeardownLeg(() => mcpHandle.dispose(), "mcp.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
@@ -36,10 +36,11 @@ import { delimitUntrusted, inlineUntrusted, sanitizeUntrustedText } from "../unt
|
|
|
36
36
|
import { reconcileInterruptedSession } from "../session-reconcile.js";
|
|
37
37
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
38
38
|
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
39
|
-
import { toolPolicyNameSets } from "../tool-policy.js";
|
|
39
|
+
import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
|
|
40
40
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
41
41
|
import { discloseDroppedPending, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
42
42
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
43
|
+
import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
|
|
43
44
|
import { DEFAULT_MAX_TURNS } from "../../config/defaults.js";
|
|
44
45
|
import { structuredFrom, toolOutputFrom } from "./tool-output-projection.js";
|
|
45
46
|
export { DEFAULT_MAX_TURNS };
|
|
@@ -51,7 +52,7 @@ function createRunState() {
|
|
|
51
52
|
budget: { remainingMicroUsd: undefined, maxCostMicroUsd: undefined, overBudget: () => false, streamCancel: false, callOutputChars: 0, lastStreamBudgetCheck: 0, projectedOverBudget: () => false },
|
|
52
53
|
turn: { callStartAt: undefined, firstTokenAt: undefined, turnUsage: undefined, turnUsageMissing: false, turnStopReason: undefined, lastTurnHadToolCalls: false, toolBatch: [] },
|
|
53
54
|
counters: { nudgesSent: 0, nudgeIdx: 0, finalizeInjected: false, walltimeSyncBackstopFired: false, compactionFloor: 0, trimForceBackoff: false, callCutoffs: 0, repetitionCuts: 0, repetitionSpared: 0, repetitionEvents: [], REPETITION_EVENTS_CAP: 0, preemptIgnoredReported: false, wroteThisRun: false, finalVerifyInjections: 0, groundingSignalPreR9: false, groundingSignalPostR9: false, cadenceTurns: 0 },
|
|
54
|
-
attach: { attachmentsCfg: undefined, agentListingOn: false, skillsListingOn: false, attachState: undefined, dateState: undefined, instrProbe: undefined, instrState: undefined, attachmentsInjected: 0 },
|
|
55
|
+
attach: { attachmentsCfg: undefined, agentListingOn: false, skillsListingOn: false, attachState: undefined, dateState: undefined, instrProbe: undefined, instrState: undefined, sizeGuidelineState: undefined, attachmentsInjected: 0 },
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
const MAX_CONSECUTIVE_COMPACTION_FAILURES = 3;
|
|
@@ -314,7 +315,10 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
314
315
|
}
|
|
315
316
|
let boundaryAttachmentBytes = 0;
|
|
316
317
|
let attachmentsPayload;
|
|
317
|
-
if ((rs.attach.dateState !== undefined ||
|
|
318
|
+
if ((rs.attach.dateState !== undefined ||
|
|
319
|
+
rs.attach.instrState !== undefined ||
|
|
320
|
+
rs.attach.sizeGuidelineState !== undefined ||
|
|
321
|
+
rs.attach.attachState !== undefined) &&
|
|
318
322
|
!boundarySteered &&
|
|
319
323
|
!rs.counters.finalizeInjected &&
|
|
320
324
|
rs.counters.finalVerifyInjections === 0 &&
|
|
@@ -457,6 +461,16 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
|
|
|
457
461
|
rs.attach.dateState.announcedDate = today;
|
|
458
462
|
}
|
|
459
463
|
}
|
|
464
|
+
if (rs.attach.sizeGuidelineState !== undefined && rs.turn.lastTurnHadToolCalls) {
|
|
465
|
+
const guideline = rs.attach.sizeGuidelineState.current();
|
|
466
|
+
if (guideline !== rs.attach.sizeGuidelineState.announcedGuideline) {
|
|
467
|
+
due.splice(due.length > 0 && due[0].source === "date_change" ? 1 : 0, 0, {
|
|
468
|
+
source: "workflow_size_guideline_change",
|
|
469
|
+
body: workflowSizeGuidelineChangeNotice(guideline),
|
|
470
|
+
});
|
|
471
|
+
rs.attach.sizeGuidelineState.announcedGuideline = guideline;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
460
474
|
if (rs.attach.instrState !== undefined && rs.attach.instrProbe !== undefined && rs.turn.lastTurnHadToolCalls) {
|
|
461
475
|
let probed = null;
|
|
462
476
|
try {
|
|
@@ -1674,13 +1688,19 @@ export class Runner {
|
|
|
1674
1688
|
}
|
|
1675
1689
|
rs.telemetry.taskStart = Date.now();
|
|
1676
1690
|
rs.telemetry.taskStartMonotonic = performance.now();
|
|
1677
|
-
|
|
1691
|
+
const discloseNoteTaskRunFailure = (err) => {
|
|
1678
1692
|
try {
|
|
1679
1693
|
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: prepared.sessionId });
|
|
1680
1694
|
}
|
|
1681
1695
|
catch {
|
|
1682
1696
|
}
|
|
1683
|
-
}
|
|
1697
|
+
};
|
|
1698
|
+
try {
|
|
1699
|
+
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId)).catch(discloseNoteTaskRunFailure);
|
|
1700
|
+
}
|
|
1701
|
+
catch (err) {
|
|
1702
|
+
discloseNoteTaskRunFailure(err);
|
|
1703
|
+
}
|
|
1684
1704
|
rs.degrade.recordDegraded = (info, toModel) => {
|
|
1685
1705
|
if (rs.degrade.degraded !== undefined)
|
|
1686
1706
|
return;
|
|
@@ -1764,6 +1784,10 @@ export class Runner {
|
|
|
1764
1784
|
rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
|
|
1765
1785
|
? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
|
|
1766
1786
|
: undefined;
|
|
1787
|
+
rs.attach.sizeGuidelineState =
|
|
1788
|
+
prepared.workflowSizeGuideline !== undefined
|
|
1789
|
+
? { announcedGuideline: prepared.workflowSizeGuideline.legGuideline, current: prepared.workflowSizeGuideline.current }
|
|
1790
|
+
: undefined;
|
|
1767
1791
|
rs.counters.cadenceTurns = 0;
|
|
1768
1792
|
rs.turn.lastTurnHadToolCalls = false;
|
|
1769
1793
|
if (rs.attach.attachState !== undefined && rs.attach.attachmentsCfg?.backgroundTasks === true) {
|
|
@@ -2614,6 +2638,7 @@ export class Runner {
|
|
|
2614
2638
|
catch (err) {
|
|
2615
2639
|
if (errorCodeOf(err) === "resume.tool_unavailable") {
|
|
2616
2640
|
await settleTeardownLeg(() => prepared.mcp.dispose(), "mcp.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
2641
|
+
await settleTeardownLeg(() => prepared.a2a?.dispose(), "a2a.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
2617
2642
|
await settleTeardownLeg(() => (prepared.ownedEnv && hasDestroy(prepared.ownedEnv) ? prepared.ownedEnv.destroy() : undefined), "ownedEnv.destroy (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
2618
2643
|
throw err;
|
|
2619
2644
|
}
|
|
@@ -3192,12 +3217,12 @@ export class Runner {
|
|
|
3192
3217
|
if (outcome.gate === "policy_ask") {
|
|
3193
3218
|
const boundTo = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
3194
3219
|
if (outcome.boundCallId !== boundTo) {
|
|
3195
|
-
throw new CheckpointError("checkpoint.invalid_outcome", `resume boundCallId "${outcome.boundCallId}" does not match the checkpoint's pending tool call "${boundTo ?? "(none)"}" — the decision-action binding (design/80 D-1) failed; refusing to apply a decision bound to a different action
|
|
3220
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume boundCallId "${outcome.boundCallId}" does not match the checkpoint's pending tool call "${boundTo ?? "(none)"}" — the decision-action binding (design/80 D-1) failed; refusing to apply a decision bound to a different action`, { field: "boundCallId" });
|
|
3196
3221
|
}
|
|
3197
3222
|
const boundHash = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.boundInputHash : undefined;
|
|
3198
3223
|
if (boundHash !== undefined) {
|
|
3199
3224
|
if (outcome.boundInputHash !== boundHash) {
|
|
3200
|
-
throw new CheckpointError("checkpoint.invalid_outcome", "resume boundInputHash does not match the checkpoint's pending tool call input — the decision-action input binding (design/80 D-1 §2) failed; refusing to apply an approval bound to a different input (TOCTOU re-mint guard)");
|
|
3225
|
+
throw new CheckpointError("checkpoint.invalid_outcome", "resume boundInputHash does not match the checkpoint's pending tool call input — the decision-action input binding (design/80 D-1 §2) failed; refusing to apply an approval bound to a different input (TOCTOU re-mint guard)", { field: "boundInputHash" });
|
|
3201
3226
|
}
|
|
3202
3227
|
}
|
|
3203
3228
|
else if (checkpointVersionOf(cp) >= BINDING_CHECKPOINT_VERSION) {
|
|
@@ -3347,7 +3372,7 @@ export class Runner {
|
|
|
3347
3372
|
return;
|
|
3348
3373
|
}
|
|
3349
3374
|
if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
|
|
3350
|
-
const rechecked = await prepared.basePolicyForResumeEdit.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal);
|
|
3375
|
+
const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal));
|
|
3351
3376
|
if (rechecked.action === "deny") {
|
|
3352
3377
|
emitEnd(true);
|
|
3353
3378
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approver EDITED this call's input; the edited call is denied by the deployment's tool policy and was not executed${rechecked.message ? `: ${rechecked.message}` : ""}.`), true));
|
|
@@ -3356,7 +3381,7 @@ export class Runner {
|
|
|
3356
3381
|
}
|
|
3357
3382
|
}
|
|
3358
3383
|
if (prepared.denyNarrowingPolicy) {
|
|
3359
|
-
const narrowed = await prepared.denyNarrowingPolicy.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal);
|
|
3384
|
+
const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal));
|
|
3360
3385
|
if (narrowed.action === "deny") {
|
|
3361
3386
|
emitEnd(true);
|
|
3362
3387
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`), true));
|
|
@@ -3514,6 +3539,14 @@ export class Runner {
|
|
|
3514
3539
|
catch (err) {
|
|
3515
3540
|
this.deps.onError?.(err, { phase: "mcp", sessionId: prepared.sessionId });
|
|
3516
3541
|
}
|
|
3542
|
+
if (prepared.a2a !== undefined) {
|
|
3543
|
+
try {
|
|
3544
|
+
await prepared.a2a.dispose();
|
|
3545
|
+
}
|
|
3546
|
+
catch (err) {
|
|
3547
|
+
this.deps.onError?.(err, { phase: "a2a", sessionId: prepared.sessionId });
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3517
3550
|
return comp;
|
|
3518
3551
|
}
|
|
3519
3552
|
async teardownOwnedEnv(prepared) {
|
|
@@ -14,7 +14,7 @@ export declare function deferHint(description: string, max?: number): string;
|
|
|
14
14
|
export declare function safeName(name: string): string;
|
|
15
15
|
export declare function classifyDeferred(opts: {
|
|
16
16
|
specs: ReadonlyArray<ToolSpec>;
|
|
17
|
-
|
|
17
|
+
protocolToolNames: ReadonlyArray<string>;
|
|
18
18
|
fullTools: ReadonlyArray<ToolFingerprintInput>;
|
|
19
19
|
deferMode?: "auto";
|
|
20
20
|
model: Model;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
|
-
import { defineTool } from "../tools.js";
|
|
3
|
+
import { defineTool, errorResult } from "../tools.js";
|
|
4
4
|
export const TOOL_SEARCH_NAME = "ToolSearch";
|
|
5
5
|
const DEFER_AUTO_FRACTION = 0.1;
|
|
6
6
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -35,7 +35,7 @@ export function classifyDeferred(opts) {
|
|
|
35
35
|
if (s.defer === true && !pinned.has(s.name))
|
|
36
36
|
deferred.add(s.name);
|
|
37
37
|
}
|
|
38
|
-
for (const name of opts.
|
|
38
|
+
for (const name of opts.protocolToolNames)
|
|
39
39
|
if (!pinned.has(name))
|
|
40
40
|
deferred.add(name);
|
|
41
41
|
for (const name of opts.deferNames ?? [])
|
|
@@ -247,6 +247,11 @@ export function createToolSearchTool(opts) {
|
|
|
247
247
|
}),
|
|
248
248
|
effect: "read",
|
|
249
249
|
execute: async (raw) => {
|
|
250
|
+
const staleSelect = (raw ?? {});
|
|
251
|
+
if (staleSelect.select !== undefined) {
|
|
252
|
+
return errorResult(`Error (${TOOL_SEARCH_NAME}): \`select\` is not a parameter (the array form is retired) — put the ` +
|
|
253
|
+
`selection in \`query\` instead: {"query":"select:ToolA,ToolB"}. Nothing was activated.`);
|
|
254
|
+
}
|
|
250
255
|
const args = (raw ?? {});
|
|
251
256
|
const { matched, missing } = resolveToolSearchDetailed(args, registry);
|
|
252
257
|
const mounted = mountedNames?.() ?? new Set();
|