@sema-agent/core 5.2.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 +21 -0
- 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/mcp.d.ts +11 -4
- package/dist/core/mcp.js +11 -31
- package/dist/core/protocol-naming.d.ts +7 -0
- package/dist/core/protocol-naming.js +32 -0
- package/dist/core/protocol-table.d.ts +3 -1
- package/dist/core/protocol-table.js +37 -32
- package/dist/core/runner/prepare-task.d.ts +7 -0
- package/dist/core/runner/prepare-task.js +43 -14
- package/dist/core/runner/runtask.js +31 -4
- package/dist/core/runner/tool-disclosure.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.js +1 -1
- 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/types.d.ts +16 -2
- package/dist/index.d.ts +8 -3
- package/dist/index.js +6 -2
- package/dist/prompt-assembly/event-registry.js +1 -0
- package/package.json +1 -1
package/dist/core/mcp.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProtocolId } from "./protocol-table.js";
|
|
1
2
|
import type { AgentTool } from "../internal/harness-types.js";
|
|
2
3
|
import type { ImageContent, TextContent } from "../internal/llm.js";
|
|
3
4
|
import { type McpImageResizer } from "./image-downsample.js";
|
|
@@ -69,14 +70,20 @@ export declare function mcpIdleTimeoutMs(kind: "stdio" | "http"): number;
|
|
|
69
70
|
export declare function describeMcpSpecErrorCode(code: unknown): string | undefined;
|
|
70
71
|
export declare function collapseMcpErrorPrefix(message: string): string;
|
|
71
72
|
export declare function networkErrorCode(err: unknown, depth?: number): string | undefined;
|
|
72
|
-
export
|
|
73
|
-
|
|
73
|
+
export interface HttpTransportFailure {
|
|
74
|
+
condition: string;
|
|
75
|
+
delivered: "no" | "unknown";
|
|
76
|
+
httpStatus?: number;
|
|
77
|
+
}
|
|
78
|
+
export declare function describeHttpTransportFailure(err: unknown): HttpTransportFailure | undefined;
|
|
79
|
+
export { normalizeNameSegment as normalizeMcpName, clampNameSegment } from "./protocol-naming.js";
|
|
74
80
|
export * from "./image-downsample.js";
|
|
75
81
|
export declare function inferCompactSchema(value: unknown, depth?: number): string;
|
|
76
|
-
export
|
|
82
|
+
export interface ProtocolHttpAuth {
|
|
77
83
|
headers?: Record<string, string>;
|
|
78
84
|
principalHeader?: string;
|
|
79
|
-
}
|
|
85
|
+
}
|
|
86
|
+
export declare function resolveProtocolHttpHeaders(protocol: ProtocolId, t: ProtocolHttpAuth, principal?: string): Record<string, string> | undefined;
|
|
80
87
|
interface McpContentItem {
|
|
81
88
|
type: string;
|
|
82
89
|
text?: string;
|
package/dist/core/mcp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { MCP_NAMESPACE } from "./protocol-table.js";
|
|
2
|
+
import { mintNamespacePrefix, mintNamespacedToolName, normalizeNameSegment } from "./protocol-naming.js";
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
5
|
import { StreamableHTTPClientTransport, StreamableHTTPError } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -12,7 +12,6 @@ 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
14
|
import { validateJsonSchemaShape } from "./runner/strict-output-schema.js";
|
|
15
|
-
const NAME_SEP = "__";
|
|
16
15
|
export const MCP_PREFIX = MCP_NAMESPACE.prefix;
|
|
17
16
|
const MCP_OUTPUT_TOKENS_DEFAULT = 25_000;
|
|
18
17
|
const MCP_CHARS_PER_TOKEN = 4;
|
|
@@ -198,7 +197,7 @@ export function networkErrorCode(err, depth = 0) {
|
|
|
198
197
|
}
|
|
199
198
|
return networkErrorCode(err.cause, depth + 1);
|
|
200
199
|
}
|
|
201
|
-
function describeHttpTransportFailure(err) {
|
|
200
|
+
export function describeHttpTransportFailure(err) {
|
|
202
201
|
if (err instanceof McpError)
|
|
203
202
|
return undefined;
|
|
204
203
|
if (err instanceof StreamableHTTPError) {
|
|
@@ -297,19 +296,7 @@ function throwDeadServer(server, what) {
|
|
|
297
296
|
e.details = { server };
|
|
298
297
|
throw e;
|
|
299
298
|
}
|
|
300
|
-
export
|
|
301
|
-
const charset = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
302
|
-
return clampNameSegment(charset);
|
|
303
|
-
}
|
|
304
|
-
export function clampNameSegment(seg, max = 64) {
|
|
305
|
-
if (seg === "")
|
|
306
|
-
return "_";
|
|
307
|
-
if (seg.length <= max)
|
|
308
|
-
return seg;
|
|
309
|
-
const digest = createHash("sha256").update(seg).digest("hex").slice(0, 8);
|
|
310
|
-
const keep = max - digest.length - 1;
|
|
311
|
-
return keep > 0 ? `${seg.slice(0, keep)}_${digest}` : digest.slice(0, max);
|
|
312
|
-
}
|
|
299
|
+
export { normalizeNameSegment as normalizeMcpName, clampNameSegment } from "./protocol-naming.js";
|
|
313
300
|
export * from "./image-downsample.js";
|
|
314
301
|
const MCP_BLOB_DIR = join(tmpdir(), "sema-mcp-blobs");
|
|
315
302
|
function extensionForMime(mimeType) {
|
|
@@ -345,7 +332,7 @@ function extensionForMime(mimeType) {
|
|
|
345
332
|
async function persistMcpBlob(base64Data, mimeType, server) {
|
|
346
333
|
const bytes = Buffer.from(base64Data, "base64");
|
|
347
334
|
const ext = extensionForMime(mimeType);
|
|
348
|
-
const filePath = join(MCP_BLOB_DIR, `mcp-${
|
|
335
|
+
const filePath = join(MCP_BLOB_DIR, `mcp-${normalizeNameSegment(server ?? "server")}-blob-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`);
|
|
349
336
|
try {
|
|
350
337
|
await mkdir(MCP_BLOB_DIR, { recursive: true, mode: 0o700 });
|
|
351
338
|
const st = await lstat(MCP_BLOB_DIR);
|
|
@@ -409,10 +396,10 @@ export function inferCompactSchema(value, depth = 2) {
|
|
|
409
396
|
}
|
|
410
397
|
return typeof value;
|
|
411
398
|
}
|
|
412
|
-
export function
|
|
399
|
+
export function resolveProtocolHttpHeaders(protocol, t, principal) {
|
|
413
400
|
if (t.principalHeader && principal !== undefined) {
|
|
414
401
|
if (/[\r\n]/.test(principal))
|
|
415
|
-
throw new Error(
|
|
402
|
+
throw new Error(`${protocol}: principal must not contain CR/LF (header injection guard)`);
|
|
416
403
|
return { ...t.headers, [t.principalHeader]: principal };
|
|
417
404
|
}
|
|
418
405
|
return t.headers;
|
|
@@ -426,7 +413,7 @@ function buildTransport(spec, principal) {
|
|
|
426
413
|
env: t.env,
|
|
427
414
|
});
|
|
428
415
|
}
|
|
429
|
-
const headers =
|
|
416
|
+
const headers = resolveProtocolHttpHeaders(MCP_NAMESPACE.id, t, principal);
|
|
430
417
|
return new StreamableHTTPClientTransport(new URL(t.url), {
|
|
431
418
|
requestInit: headers ? { headers } : undefined,
|
|
432
419
|
});
|
|
@@ -667,7 +654,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
|
|
|
667
654
|
droppedTools,
|
|
668
655
|
statuses,
|
|
669
656
|
refresh: async (server) => {
|
|
670
|
-
const prefixOf = (name) =>
|
|
657
|
+
const prefixOf = (name) => mintNamespacePrefix(MCP_NAMESPACE, name);
|
|
671
658
|
const targets = server !== undefined ? serverHandles.filter((h) => h.name === server) : serverHandles;
|
|
672
659
|
const results = [];
|
|
673
660
|
if (server !== undefined && targets.length === 0) {
|
|
@@ -1149,14 +1136,14 @@ function intakeListedTools(listed, spec, client, health, imageResizer) {
|
|
|
1149
1136
|
}
|
|
1150
1137
|
const normalized = normalizeMcpToolSchema(t.inputSchema);
|
|
1151
1138
|
let effectiveInputSchema = t.inputSchema;
|
|
1152
|
-
let effectiveDescription = t.description;
|
|
1139
|
+
let effectiveDescription = t.description !== undefined ? sanitizeUntrustedText(t.description) : undefined;
|
|
1153
1140
|
if (normalized.outcome === "drop") {
|
|
1154
1141
|
dropped.push({ tool: inlineUntrusted(t.name), reason: inlineUntrusted(normalized.reason, 240) });
|
|
1155
1142
|
continue;
|
|
1156
1143
|
}
|
|
1157
1144
|
if (normalized.outcome === "normalized") {
|
|
1158
1145
|
effectiveInputSchema = normalized.schema;
|
|
1159
|
-
effectiveDescription = normalized.note + (
|
|
1146
|
+
effectiveDescription = normalized.note + (effectiveDescription ? `\n\n${effectiveDescription}` : "");
|
|
1160
1147
|
}
|
|
1161
1148
|
const schemaProblem = mcpToolSchemaProblem(effectiveInputSchema);
|
|
1162
1149
|
if (schemaProblem !== undefined) {
|
|
@@ -1164,14 +1151,7 @@ function intakeListedTools(listed, spec, client, health, imageResizer) {
|
|
|
1164
1151
|
continue;
|
|
1165
1152
|
}
|
|
1166
1153
|
const remoteName = t.name;
|
|
1167
|
-
const namespacedName = (
|
|
1168
|
-
const TOOL_MIN = 16;
|
|
1169
|
-
const fixed = MCP_PREFIX.length + NAME_SEP.length;
|
|
1170
|
-
const serverBudget = Math.max(1, 64 - fixed - TOOL_MIN);
|
|
1171
|
-
const server = clampNameSegment(normalizeMcpName(spec.name), serverBudget);
|
|
1172
|
-
const prefix = `${MCP_PREFIX}${server}${NAME_SEP}`;
|
|
1173
|
-
return `${prefix}${clampNameSegment(normalizeMcpName(remoteName), Math.max(1, 64 - prefix.length))}`;
|
|
1174
|
-
})();
|
|
1154
|
+
const namespacedName = mintNamespacedToolName(MCP_NAMESPACE, spec.name, remoteName);
|
|
1175
1155
|
const hintAxis = mcpAxisFor(namespacedName, t.annotations);
|
|
1176
1156
|
const axis = applyCallerAxisOverride(namespacedName, hintAxis, spec.toolAxes?.[remoteName]);
|
|
1177
1157
|
if (axis)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProtocolNamespace } from "./protocol-table.js";
|
|
2
|
+
export declare const TOOL_NAME_MAX_CHARS = 64;
|
|
3
|
+
export declare const MINTED_TOOL_SEGMENT_MIN_CHARS = 16;
|
|
4
|
+
export declare function normalizeNameSegment(name: string): string;
|
|
5
|
+
export declare function clampNameSegment(seg: string, max?: number): string;
|
|
6
|
+
export declare function mintNamespacePrefix(ns: ProtocolNamespace, peer: string): string;
|
|
7
|
+
export declare function mintNamespacedToolName(ns: ProtocolNamespace, peer: string, tool: string): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export const TOOL_NAME_MAX_CHARS = 64;
|
|
3
|
+
export const MINTED_TOOL_SEGMENT_MIN_CHARS = 16;
|
|
4
|
+
const SEGMENT_DIGEST_CHARS = 8;
|
|
5
|
+
const NAME_SEP = "__";
|
|
6
|
+
export function normalizeNameSegment(name) {
|
|
7
|
+
return clampNameSegment(name.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
8
|
+
}
|
|
9
|
+
const SEGMENT_DIGEST_JOIN = "-";
|
|
10
|
+
export function clampNameSegment(seg, max = TOOL_NAME_MAX_CHARS) {
|
|
11
|
+
if (seg === "")
|
|
12
|
+
return "_";
|
|
13
|
+
if (seg.length <= max)
|
|
14
|
+
return seg;
|
|
15
|
+
const digest = createHash("sha256").update(seg).digest("hex").slice(0, SEGMENT_DIGEST_CHARS);
|
|
16
|
+
const keep = max - digest.length - SEGMENT_DIGEST_JOIN.length;
|
|
17
|
+
return keep > 0 ? `${seg.slice(0, keep)}${SEGMENT_DIGEST_JOIN}${digest}` : digest.slice(0, max);
|
|
18
|
+
}
|
|
19
|
+
function settlePeerSegment(clamped) {
|
|
20
|
+
const folded = clamped.replace(/_{2,}/g, "_").replace(/_+$/, "");
|
|
21
|
+
if (folded !== "")
|
|
22
|
+
return folded;
|
|
23
|
+
return createHash("sha256").update(clamped).digest("hex").slice(0, SEGMENT_DIGEST_CHARS);
|
|
24
|
+
}
|
|
25
|
+
export function mintNamespacePrefix(ns, peer) {
|
|
26
|
+
const peerBudget = Math.max(1, TOOL_NAME_MAX_CHARS - ns.prefix.length - NAME_SEP.length - MINTED_TOOL_SEGMENT_MIN_CHARS);
|
|
27
|
+
return `${ns.prefix}${settlePeerSegment(clampNameSegment(normalizeNameSegment(peer), peerBudget))}${NAME_SEP}`;
|
|
28
|
+
}
|
|
29
|
+
export function mintNamespacedToolName(ns, peer, tool) {
|
|
30
|
+
const prefix = mintNamespacePrefix(ns, peer);
|
|
31
|
+
return `${prefix}${clampNameSegment(normalizeNameSegment(tool), Math.max(1, TOOL_NAME_MAX_CHARS - prefix.length))}`;
|
|
32
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ProtocolId = "mcp";
|
|
1
|
+
export type ProtocolId = "mcp" | "a2a";
|
|
2
2
|
export interface ProtocolPeerTool {
|
|
3
3
|
peer: string;
|
|
4
4
|
tool: string;
|
|
@@ -11,7 +11,9 @@ export interface ProtocolNamespace<P extends string = string> {
|
|
|
11
11
|
displayGroupKey(name: string): string;
|
|
12
12
|
}
|
|
13
13
|
declare const MCP_PREFIX_NAME: "mcp__";
|
|
14
|
+
declare const A2A_PREFIX_NAME: "a2a__";
|
|
14
15
|
export declare const MCP_NAMESPACE: ProtocolNamespace<typeof MCP_PREFIX_NAME>;
|
|
16
|
+
export declare const A2A_NAMESPACE: ProtocolNamespace<typeof A2A_PREFIX_NAME>;
|
|
15
17
|
export declare const PROTOCOL_TABLE: readonly ProtocolNamespace[];
|
|
16
18
|
export declare function protocolOf(name: string): ProtocolNamespace | undefined;
|
|
17
19
|
export {};
|
|
@@ -1,37 +1,42 @@
|
|
|
1
1
|
const NAME_SEP = "__";
|
|
2
|
+
function makeProtocolNamespace(id, prefix) {
|
|
3
|
+
const parse = (name) => {
|
|
4
|
+
if (!name.startsWith(prefix))
|
|
5
|
+
return undefined;
|
|
6
|
+
const rest = name.slice(prefix.length);
|
|
7
|
+
const sep = rest.indexOf(NAME_SEP);
|
|
8
|
+
if (sep <= 0)
|
|
9
|
+
return undefined;
|
|
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
|
+
}
|
|
2
35
|
const MCP_PREFIX_NAME = `mcp${NAME_SEP}`;
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const sep = rest.indexOf(NAME_SEP);
|
|
8
|
-
if (sep <= 0)
|
|
9
|
-
return undefined;
|
|
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 makeMcpName = (peer, tool) => {
|
|
16
|
-
if (peer.length === 0 || tool.length === 0) {
|
|
17
|
-
throw new Error(`protocol table (mcp): 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 (mcp): 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 `${MCP_PREFIX_NAME}${peer}${NAME_SEP}${tool}`;
|
|
23
|
-
};
|
|
24
|
-
export const MCP_NAMESPACE = {
|
|
25
|
-
id: "mcp",
|
|
26
|
-
prefix: MCP_PREFIX_NAME,
|
|
27
|
-
makeName: makeMcpName,
|
|
28
|
-
parse: parseMcpName,
|
|
29
|
-
displayGroupKey: (name) => {
|
|
30
|
-
const p = parseMcpName(name);
|
|
31
|
-
return p === undefined ? name : `${MCP_PREFIX_NAME}${p.peer}${NAME_SEP}*`;
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
export const PROTOCOL_TABLE = [MCP_NAMESPACE];
|
|
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];
|
|
35
40
|
export function protocolOf(name) {
|
|
36
41
|
return PROTOCOL_TABLE.find((ns) => name.startsWith(ns.prefix));
|
|
37
42
|
}
|
|
@@ -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";
|
|
@@ -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
|
}
|
|
@@ -3504,12 +3529,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3504
3529
|
: undefined;
|
|
3505
3530
|
overheadState.promptChars = systemPrompt.length;
|
|
3506
3531
|
const preparedHolder = {};
|
|
3507
|
-
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 } : {}) });
|
|
3508
3533
|
const prepared = buildPrepared();
|
|
3509
3534
|
preparedHolder.current = prepared;
|
|
3510
3535
|
return prepared;
|
|
3511
3536
|
}
|
|
3512
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
|
+
}
|
|
3513
3542
|
if (mcp) {
|
|
3514
3543
|
const mcpHandle = mcp;
|
|
3515
3544
|
await settleTeardownLeg(() => mcpHandle.dispose(), "mcp.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
@@ -40,6 +40,7 @@ import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.
|
|
|
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 {
|
|
@@ -1770,6 +1784,10 @@ export class Runner {
|
|
|
1770
1784
|
rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
|
|
1771
1785
|
? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
|
|
1772
1786
|
: undefined;
|
|
1787
|
+
rs.attach.sizeGuidelineState =
|
|
1788
|
+
prepared.workflowSizeGuideline !== undefined
|
|
1789
|
+
? { announcedGuideline: prepared.workflowSizeGuideline.legGuideline, current: prepared.workflowSizeGuideline.current }
|
|
1790
|
+
: undefined;
|
|
1773
1791
|
rs.counters.cadenceTurns = 0;
|
|
1774
1792
|
rs.turn.lastTurnHadToolCalls = false;
|
|
1775
1793
|
if (rs.attach.attachState !== undefined && rs.attach.attachmentsCfg?.backgroundTasks === true) {
|
|
@@ -2620,6 +2638,7 @@ export class Runner {
|
|
|
2620
2638
|
catch (err) {
|
|
2621
2639
|
if (errorCodeOf(err) === "resume.tool_unavailable") {
|
|
2622
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 }));
|
|
2623
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 }));
|
|
2624
2643
|
throw err;
|
|
2625
2644
|
}
|
|
@@ -3198,12 +3217,12 @@ export class Runner {
|
|
|
3198
3217
|
if (outcome.gate === "policy_ask") {
|
|
3199
3218
|
const boundTo = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
3200
3219
|
if (outcome.boundCallId !== boundTo) {
|
|
3201
|
-
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" });
|
|
3202
3221
|
}
|
|
3203
3222
|
const boundHash = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.boundInputHash : undefined;
|
|
3204
3223
|
if (boundHash !== undefined) {
|
|
3205
3224
|
if (outcome.boundInputHash !== boundHash) {
|
|
3206
|
-
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" });
|
|
3207
3226
|
}
|
|
3208
3227
|
}
|
|
3209
3228
|
else if (checkpointVersionOf(cp) >= BINDING_CHECKPOINT_VERSION) {
|
|
@@ -3520,6 +3539,14 @@ export class Runner {
|
|
|
3520
3539
|
catch (err) {
|
|
3521
3540
|
this.deps.onError?.(err, { phase: "mcp", sessionId: prepared.sessionId });
|
|
3522
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
|
+
}
|
|
3523
3550
|
return comp;
|
|
3524
3551
|
}
|
|
3525
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;
|
|
@@ -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 ?? [])
|
|
@@ -15,7 +15,7 @@ export declare const TOOL_SEARCH_REMINDER_CONFIG: {
|
|
|
15
15
|
export declare const CHANGED_FILES_MAX = 20;
|
|
16
16
|
export declare const CHANGED_FILES_MTIME_EPS_MS = 2000;
|
|
17
17
|
export declare const ATTACHMENT_BYTE_CAP: number;
|
|
18
|
-
export type AttachmentSource = "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
18
|
+
export type AttachmentSource = "todo_reminder" | "task_reminder" | "tool_search_usage_reminder" | "changed_files" | "plan_mode" | "date_change" | "instructions_change" | "workflow_size_guideline_change" | "budget_usd" | "background_tasks" | "tools_delta" | "agent_listing" | "skills_listing" | "mcp_instructions" | "mcp_dropped_tools";
|
|
19
19
|
export interface AgentListingEntry {
|
|
20
20
|
name: string;
|
|
21
21
|
description: string;
|
|
@@ -31,7 +31,6 @@ export interface TurnAttachment {
|
|
|
31
31
|
body: string;
|
|
32
32
|
}
|
|
33
33
|
export interface ListProjection {
|
|
34
|
-
statusCounts: Record<string, number>;
|
|
35
34
|
items: Array<{
|
|
36
35
|
content: string;
|
|
37
36
|
status: string;
|