@sema-agent/core 5.13.0 → 5.15.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 +418 -0
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +4 -0
- package/dist/agents/subagent.js +133 -41
- package/dist/brain/anthropic.js +33 -10
- package/dist/brain/context-overflow.d.ts +20 -0
- package/dist/brain/context-overflow.js +58 -0
- package/dist/brain/errors.js +21 -1
- package/dist/brain/open-responses.js +24 -10
- package/dist/brain/openai.js +29 -11
- package/dist/brain/request-params.d.ts +2 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/brain/retry.d.ts +5 -0
- package/dist/brain/retry.js +16 -4
- package/dist/brain/stream-engine.d.ts +9 -1
- package/dist/brain/stream-engine.js +261 -28
- package/dist/brain/timeout.d.ts +1 -0
- package/dist/brain/timeout.js +1 -0
- package/dist/core/a2a.d.ts +2 -2
- package/dist/core/a2a.js +3 -3
- package/dist/core/ask-question.d.ts +47 -2
- package/dist/core/ask-question.js +209 -28
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/checkpoint-store.d.ts +41 -17
- package/dist/core/checkpoint-store.js +137 -6
- package/dist/core/hooks.d.ts +24 -2
- package/dist/core/hooks.js +97 -10
- package/dist/core/human-input-projection.d.ts +12 -0
- package/dist/core/human-input-projection.js +27 -0
- package/dist/core/mcp.d.ts +7 -2
- package/dist/core/mcp.js +7 -7
- package/dist/core/memory-admission.d.ts +4 -0
- package/dist/core/memory-admission.js +3 -0
- package/dist/core/memory-engine/engine.d.ts +3 -1
- package/dist/core/memory-engine/engine.js +4 -3
- package/dist/core/memory-recall.d.ts +1 -1
- package/dist/core/memory-recall.js +3 -2
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +1 -0
- package/dist/core/runner/prepare-memory.js +3 -3
- package/dist/core/runner/prepare-task.d.ts +19 -6
- package/dist/core/runner/prepare-task.js +343 -35
- package/dist/core/runner/runtask.d.ts +3 -6
- package/dist/core/runner/runtask.js +290 -64
- package/dist/core/runner/tool-disclosure.d.ts +5 -0
- package/dist/core/runner/tool-disclosure.js +65 -16
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/runner/turn-attachments.d.ts +4 -0
- package/dist/core/runner/turn-attachments.js +15 -2
- package/dist/core/session-store.d.ts +3 -0
- package/dist/core/session-store.js +4 -0
- package/dist/core/session.d.ts +1 -0
- package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
- package/dist/core/task-notification.d.ts +2 -0
- package/dist/core/task-notification.js +5 -3
- package/dist/core/task-registry-agent.d.ts +1 -0
- package/dist/core/task-registry-agent.js +6 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/task-tool-shape.js +4 -3
- package/dist/core/tool-policy.d.ts +5 -0
- package/dist/core/tool-policy.js +2 -1
- package/dist/core/trace.d.ts +6 -0
- package/dist/core/types.d.ts +32 -1
- package/dist/core/wiring-manifest.d.ts +97 -0
- package/dist/core/wiring-manifest.js +186 -0
- package/dist/engine/compaction/compaction.js +2 -2
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +8 -1
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +7 -0
- package/dist/engine/llm/types.js +8 -1
- package/dist/engine/session/import-validate.d.ts +6 -1
- package/dist/engine/session/import-validate.js +29 -6
- package/dist/engine/session/memory-repo.d.ts +3 -1
- package/dist/engine/session/memory-repo.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +8 -5
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +4 -0
- package/dist/orchestration/run-workflow-tool.js +3 -0
- package/dist/orchestration/workflow-types.d.ts +8 -0
- package/dist/orchestration/workflow-types.js +14 -0
- package/dist/orchestration/workflow.d.ts +4 -0
- package/dist/orchestration/workflow.js +134 -5
- package/dist/prompts/default.js +1 -1
- package/dist/prompts/supervisor.d.ts +2 -2
- package/dist/prompts/supervisor.js +5 -4
- package/dist/stores/file/checkpoint-store.d.ts +3 -5
- package/dist/stores/file/checkpoint-store.js +31 -2
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/session-store.d.ts +3 -1
- package/dist/stores/file/session-store.js +2 -2
- package/dist/stores/file/shared-ledger.js +8 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
- package/dist/tools/fs/bash-readonly-classifier.js +94 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -0
- package/dist/tools/fs/fs-bash.js +32 -13
- package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
- package/dist/tools/fs/gh-rate-limit.js +4 -3
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -0
- package/dist/tools/fs/safety.js +34 -10
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ import type { ToolSpec } from "../types.js";
|
|
|
5
5
|
import type { ToolFingerprintInput } from "../cache-break-detector.js";
|
|
6
6
|
export declare const TOOL_SEARCH_NAME = "ToolSearch";
|
|
7
7
|
export declare const TOOL_SEARCH_DEFAULT_MAX_RESULTS = 5;
|
|
8
|
+
export declare const DEFERRED_NO_PROGRESS_LIMIT = 3;
|
|
9
|
+
export declare const DEFERRED_SCHEMA_INCOMPATIBLE_CODE = "tool.deferred_schema_incompatible";
|
|
8
10
|
export interface DeferredToolInfo {
|
|
9
11
|
name: string;
|
|
10
12
|
hint: string;
|
|
@@ -28,12 +30,14 @@ export declare function buildDeferredRegistry(deferred: ReadonlySet<string>, too
|
|
|
28
30
|
export interface PlaceholderDirectCall {
|
|
29
31
|
resolveReal: () => PlaceholderDirectTarget | undefined;
|
|
30
32
|
executionMode?: ToolExecutionMode;
|
|
33
|
+
staticFace?: () => boolean;
|
|
31
34
|
activate: () => Promise<string | undefined>;
|
|
32
35
|
}
|
|
33
36
|
export interface PlaceholderDirectTarget {
|
|
34
37
|
parameters: TSchema;
|
|
35
38
|
invoke: (toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<unknown>) => Promise<AgentToolResult<unknown>>;
|
|
36
39
|
}
|
|
40
|
+
export declare function staticSchemaRenderable(schema: TSchema | undefined): boolean;
|
|
37
41
|
export declare function createPlaceholderTool(info: DeferredToolInfo, direct?: PlaceholderDirectCall): AgentTool;
|
|
38
42
|
export declare function scoreToolMatch(query: string, info: DeferredToolInfo): number;
|
|
39
43
|
export interface ToolSearchArgs {
|
|
@@ -53,6 +57,7 @@ export declare function createToolSearchTool(opts: {
|
|
|
53
57
|
listingRide?: (newlyActivated: readonly string[]) => string | undefined;
|
|
54
58
|
mountedNames?: () => ReadonlySet<string>;
|
|
55
59
|
directCallEnabled?: boolean;
|
|
60
|
+
isMounted?: (name: string) => boolean;
|
|
56
61
|
serializeActivation?: <T>(section: () => Promise<T>) => Promise<T>;
|
|
57
62
|
staticSchemaFor?: (name: string) => TSchema | undefined;
|
|
58
63
|
}): AgentTool;
|
|
@@ -7,6 +7,8 @@ const DEFER_AUTO_FRACTION = 0.1;
|
|
|
7
7
|
const CHARS_PER_TOKEN = 4;
|
|
8
8
|
export const TOOL_SEARCH_DEFAULT_MAX_RESULTS = 5;
|
|
9
9
|
const MAX_QUERY_RESULTS = 25;
|
|
10
|
+
export const DEFERRED_NO_PROGRESS_LIMIT = 3;
|
|
11
|
+
export const DEFERRED_SCHEMA_INCOMPATIBLE_CODE = "tool.deferred_schema_incompatible";
|
|
10
12
|
const EMPTY_PARAMS = Type.Object({});
|
|
11
13
|
export function deferHint(description, max = 120) {
|
|
12
14
|
const first = (description.split("\n").find((l) => l.trim() !== "") ?? "").trim();
|
|
@@ -70,7 +72,13 @@ function renderSchemaForModel(schema) {
|
|
|
70
72
|
catch {
|
|
71
73
|
return undefined;
|
|
72
74
|
}
|
|
73
|
-
|
|
75
|
+
if (json === undefined)
|
|
76
|
+
return undefined;
|
|
77
|
+
const text = truncateError(json);
|
|
78
|
+
return { text, complete: text === json };
|
|
79
|
+
}
|
|
80
|
+
export function staticSchemaRenderable(schema) {
|
|
81
|
+
return schema !== undefined && renderSchemaForModel(schema)?.complete === true;
|
|
74
82
|
}
|
|
75
83
|
export function createPlaceholderTool(info, direct) {
|
|
76
84
|
const sn = safeName(info.name);
|
|
@@ -78,10 +86,36 @@ export function createPlaceholderTool(info, direct) {
|
|
|
78
86
|
throw new Error(`Tool "${sn}" is not active yet. Call ${TOOL_SEARCH_NAME} with {"query":"select:${sn}"} ` +
|
|
79
87
|
`(or a keyword \`query\`) to load its full schema, then call ${sn} with the proper arguments.`);
|
|
80
88
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
let lastFailureShape;
|
|
90
|
+
let repeats = 0;
|
|
91
|
+
const invalidArgumentsRejection = (target, params, schema, ride) => {
|
|
92
|
+
const completeness = schema.complete ? "its full parameter schema" : "an ABRIDGED copy of its parameter schema (too large to inline in full)";
|
|
93
|
+
const durability = !schema.complete
|
|
94
|
+
? `${completeness} is below; the tools list carries the complete declaration. `
|
|
95
|
+
: direct?.staticFace?.() === true
|
|
96
|
+
? `${completeness} is below and is carried by THIS result only — the tools list keeps the compact ` +
|
|
97
|
+
`placeholder entry, so re-run ${TOOL_SEARCH_NAME} with {"query":"select:${sn}"} if you need the schema again later. `
|
|
98
|
+
: `${completeness} is below, and the next request's tools list will advertise it too. `;
|
|
99
|
+
const shape = formatZodValidationError(target.parameters, params);
|
|
100
|
+
repeats = shape === lastFailureShape ? repeats + 1 : 1;
|
|
101
|
+
lastFailureShape = shape;
|
|
102
|
+
if (repeats >= DEFERRED_NO_PROGRESS_LIMIT) {
|
|
103
|
+
const stuck = `\`${sn}\` rejected the same arguments ${repeats} times in a row and its parameter schema has already ` +
|
|
104
|
+
`been delivered, so re-sending them will not start working. This usually means the caller cannot ` +
|
|
105
|
+
`produce arguments outside the compact placeholder entry advertised for \`${sn}\`. \`${sn}\` is ` +
|
|
106
|
+
`finished for this run — no further call to it will be answered differently. Other tools are ` +
|
|
107
|
+
`unaffected; if this was the only work left, the run stops here. ` +
|
|
108
|
+
`(${DEFERRED_SCHEMA_INCOMPATIBLE_CODE})\nLast rejection: ${shape}`;
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: "text", text: stuck }],
|
|
111
|
+
details: { invalidArguments: true, noProgress: DEFERRED_SCHEMA_INCOMPATIBLE_CODE, repeats },
|
|
112
|
+
isError: true,
|
|
113
|
+
terminate: true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const text = `Invalid arguments for \`${sn}\`: ${shape} ` +
|
|
117
|
+
`\`${sn}\` is now active — ${durability}` +
|
|
118
|
+
`Call \`${sn}\` again with arguments matching it.\nParameter schema: ${schema.text}`;
|
|
85
119
|
return {
|
|
86
120
|
content: ride === undefined || ride === "" ? [{ type: "text", text }] : [{ type: "text", text }, { type: "text", text: ride }],
|
|
87
121
|
details: { invalidArguments: true },
|
|
@@ -102,17 +136,19 @@ export function createPlaceholderTool(info, direct) {
|
|
|
102
136
|
if (real === undefined)
|
|
103
137
|
return teachingRejection();
|
|
104
138
|
if (Value.Check(real.parameters, params)) {
|
|
139
|
+
lastFailureShape = undefined;
|
|
140
|
+
repeats = 0;
|
|
105
141
|
const ride = await direct.activate();
|
|
106
142
|
const result = await real.invoke(toolCallId, params, signal, onUpdate);
|
|
107
143
|
if (ride === undefined || ride === "")
|
|
108
144
|
return result;
|
|
109
145
|
return { ...result, content: [...result.content, { type: "text", text: ride }] };
|
|
110
146
|
}
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
147
|
+
const schemaRender = renderSchemaForModel(real.parameters);
|
|
148
|
+
if (schemaRender === undefined)
|
|
113
149
|
return teachingRejection();
|
|
114
150
|
const ride = await direct.activate();
|
|
115
|
-
return invalidArgumentsRejection(real, params,
|
|
151
|
+
return invalidArgumentsRejection(real, params, schemaRender, ride);
|
|
116
152
|
},
|
|
117
153
|
};
|
|
118
154
|
}
|
|
@@ -236,12 +272,13 @@ export function extractDiscoveredToolNames(messages, registry) {
|
|
|
236
272
|
return [...names];
|
|
237
273
|
}
|
|
238
274
|
export function createToolSearchTool(opts) {
|
|
239
|
-
const { registry, active, rematerialize, listingRide, mountedNames, staticSchemaFor } = opts;
|
|
275
|
+
const { registry, active, rematerialize, listingRide, mountedNames, staticSchemaFor, isMounted } = opts;
|
|
240
276
|
const directCallEnabled = opts.directCallEnabled !== false;
|
|
241
277
|
const activationPosture = directCallEnabled
|
|
242
278
|
? (staticSchemaFor !== undefined
|
|
243
279
|
? "Most tools start as name-only placeholders to keep requests small; activating one here returns its full " +
|
|
244
|
-
"parameter schema in the result (the tools list keeps the compact placeholder entry
|
|
280
|
+
"parameter schema in the result (the tools list keeps the compact placeholder entry — except for a " +
|
|
281
|
+
"declaration too large to inline, which goes to the tools list instead). "
|
|
245
282
|
: "Most tools start as name-only placeholders to keep requests small; activating one here loads its full " +
|
|
246
283
|
"parameter schema. ") +
|
|
247
284
|
"Until you have that schema you cannot reliably form a call, so activate a tool rather " +
|
|
@@ -286,14 +323,21 @@ export function createToolSearchTool(opts) {
|
|
|
286
323
|
`selection in \`query\` instead: {"query":"select:ToolA,ToolB"}. Nothing was activated.`);
|
|
287
324
|
}
|
|
288
325
|
const args = (raw ?? {});
|
|
289
|
-
const
|
|
326
|
+
const resolved = resolveToolSearchDetailed(args, registry);
|
|
327
|
+
const missing = resolved.missing;
|
|
328
|
+
const withdrawn = isMounted === undefined ? [] : resolved.matched.filter((n) => !isMounted(n));
|
|
329
|
+
const matched = isMounted === undefined ? resolved.matched : resolved.matched.filter((n) => isMounted(n));
|
|
290
330
|
const mounted = mountedNames?.() ?? new Set();
|
|
291
331
|
const missCallable = missing.filter((n) => mounted.has(n));
|
|
292
332
|
const missUnknown = missing.filter((n) => !mounted.has(n));
|
|
293
333
|
const callableNote = missCallable.length > 0
|
|
294
334
|
? `\nAlready available: ${missCallable.map((n) => safeName(n)).join(", ")} — ${missCallable.length > 1 ? "these tools are" : "this tool is"} not deferred; call ${missCallable.length > 1 ? "them" : "it"} directly right now (no activation needed).`
|
|
295
335
|
: "";
|
|
336
|
+
const withdrawnNote = withdrawn.length > 0
|
|
337
|
+
? `\nNo longer available: ${withdrawn.map((n) => safeName(n)).join(", ")} — ${withdrawn.length > 1 ? "these tools were" : "this tool was"} withdrawn by ${withdrawn.length > 1 ? "their providers" : "its provider"} and cannot be activated or called.`
|
|
338
|
+
: "";
|
|
296
339
|
const missingNote = callableNote +
|
|
340
|
+
withdrawnNote +
|
|
297
341
|
(missUnknown.length > 0
|
|
298
342
|
? `\nNot found: ${missUnknown.map((n) => safeName(n)).join(", ")} — not in the deferred registry under this exact name (lookup is case-sensitive). ` +
|
|
299
343
|
"(Already-active and non-deferred tools are callable directly and don't appear here.)"
|
|
@@ -311,7 +355,7 @@ export function createToolSearchTool(opts) {
|
|
|
311
355
|
matches: [],
|
|
312
356
|
query: typeof args.query === "string" ? args.query : "",
|
|
313
357
|
total_deferred_tools: registry.size,
|
|
314
|
-
...(missing.length > 0 ? { missing: missing.map(safeName) } : {}),
|
|
358
|
+
...(missing.length > 0 || withdrawn.length > 0 ? { missing: [...missing, ...withdrawn].map(safeName) } : {}),
|
|
315
359
|
},
|
|
316
360
|
};
|
|
317
361
|
}
|
|
@@ -332,6 +376,7 @@ export function createToolSearchTool(opts) {
|
|
|
332
376
|
return { newly, ride: newly.length > 0 ? listingRide?.(newly) : undefined };
|
|
333
377
|
});
|
|
334
378
|
const { newly, ride } = await section;
|
|
379
|
+
let missingSchemaLines = false;
|
|
335
380
|
const lines = matched.map((n) => {
|
|
336
381
|
const info = registry.get(n);
|
|
337
382
|
const tag = newly.includes(n) ? "activated" : "already active";
|
|
@@ -340,12 +385,16 @@ export function createToolSearchTool(opts) {
|
|
|
340
385
|
return base;
|
|
341
386
|
const schema = staticSchemaFor(n);
|
|
342
387
|
const json = schema === undefined ? undefined : renderSchemaForModel(schema);
|
|
343
|
-
|
|
388
|
+
if (json === undefined || !json.complete) {
|
|
389
|
+
missingSchemaLines = true;
|
|
390
|
+
return `${base}\n parameters: not inlined — this tool's full declaration is in the tools list.`;
|
|
391
|
+
}
|
|
392
|
+
return `${base}\n parameters: ${json.text}`;
|
|
344
393
|
});
|
|
345
394
|
const head = staticSchemaFor !== undefined
|
|
346
395
|
? newly.length > 0
|
|
347
|
-
? `Activated ${newly.length} tool(s) — call them directly with arguments matching the parameter schemas below (the tools list keeps compact placeholder entries):`
|
|
348
|
-
:
|
|
396
|
+
? `Activated ${newly.length} tool(s) — call them directly with arguments matching the parameter schemas below (the tools list keeps compact placeholder entries${missingSchemaLines ? ", except where a line says otherwise" : ""}):`
|
|
397
|
+
: `These tools are already active — call them directly; their parameter schemas are repeated below${missingSchemaLines ? " where they can be inlined" : ""}:`
|
|
349
398
|
: newly.length > 0
|
|
350
399
|
? `Activated ${newly.length} tool(s); they are now available with full parameters — call them directly:`
|
|
351
400
|
: "These tools are already active — call them directly:";
|
|
@@ -358,7 +407,7 @@ export function createToolSearchTool(opts) {
|
|
|
358
407
|
matches: matched.map(safeName),
|
|
359
408
|
query: typeof args.query === "string" ? args.query : "",
|
|
360
409
|
total_deferred_tools: registry.size,
|
|
361
|
-
...(missing.length > 0 ? { missing: missing.map(safeName) } : {}),
|
|
410
|
+
...(missing.length > 0 || withdrawn.length > 0 ? { missing: [...missing, ...withdrawn].map(safeName) } : {}),
|
|
362
411
|
},
|
|
363
412
|
};
|
|
364
413
|
},
|
|
@@ -57,6 +57,7 @@ export const toolOutputFrom = (result) => {
|
|
|
57
57
|
return { output: raw, truncated: true, totalChars };
|
|
58
58
|
};
|
|
59
59
|
const CC_DETAIL_TYPES = new Set([
|
|
60
|
+
"ask-question",
|
|
60
61
|
"edit", "create", "update", "bash", "notebook-edit", "notebook", "file_unchanged", "worktree", "text", "grep", "glob", "mcp",
|
|
61
62
|
"agent", "task", "task-list", "task-output", "workflow-run",
|
|
62
63
|
"web-fetch", "web-search", "todo", "cron-create", "cron-delete", "cron-list", "image",
|
|
@@ -101,6 +101,8 @@ export interface AttachmentInputs {
|
|
|
101
101
|
backgroundTasks?: ReadonlyArray<BackgroundTaskSnapshot>;
|
|
102
102
|
newTools?: readonly string[];
|
|
103
103
|
newToolsStaticFace?: boolean;
|
|
104
|
+
newToolsSwappedUnderStatic?: readonly string[];
|
|
105
|
+
readdedToolsSwappedUnderStatic?: readonly string[];
|
|
104
106
|
mcpToolsDelta?: McpToolsDeltaFacts;
|
|
105
107
|
agentListing?: ReadonlyArray<AgentListingEntry>;
|
|
106
108
|
agentToolName?: string;
|
|
@@ -150,6 +152,8 @@ export interface McpToolsDeltaFacts {
|
|
|
150
152
|
export declare function renderToolsDelta(input: {
|
|
151
153
|
added?: readonly string[];
|
|
152
154
|
staticFace?: boolean;
|
|
155
|
+
swappedUnderStatic?: readonly string[];
|
|
156
|
+
readdedSwappedUnderStatic?: readonly string[];
|
|
153
157
|
} & McpToolsDeltaFacts): string | undefined;
|
|
154
158
|
export declare const AGENT_TOOLS_NOTE_DEFAULT = "All tools";
|
|
155
159
|
export declare const AGENT_CONCURRENCY_NOTE = "When you launch multiple agents for independent work, send them in a single message with multiple tool uses so they run concurrently.";
|
|
@@ -171,6 +171,12 @@ export function collectDueAttachments(state, inp) {
|
|
|
171
171
|
const body = renderToolsDelta({
|
|
172
172
|
...(inp.newTools !== undefined ? { added: inp.newTools } : {}),
|
|
173
173
|
...(inp.newToolsStaticFace === true ? { staticFace: true } : {}),
|
|
174
|
+
...(inp.newToolsSwappedUnderStatic !== undefined && inp.newToolsSwappedUnderStatic.length > 0
|
|
175
|
+
? { swappedUnderStatic: inp.newToolsSwappedUnderStatic }
|
|
176
|
+
: {}),
|
|
177
|
+
...(inp.readdedToolsSwappedUnderStatic !== undefined && inp.readdedToolsSwappedUnderStatic.length > 0
|
|
178
|
+
? { readdedSwappedUnderStatic: inp.readdedToolsSwappedUnderStatic }
|
|
179
|
+
: {}),
|
|
174
180
|
...(inp.mcpToolsDelta ?? {}),
|
|
175
181
|
});
|
|
176
182
|
if (body !== undefined)
|
|
@@ -392,19 +398,26 @@ export function renderToolsDelta(input) {
|
|
|
392
398
|
const blocks = [];
|
|
393
399
|
const added = input.added ?? [];
|
|
394
400
|
if (added.length > 0) {
|
|
401
|
+
const swapped = input.staticFace === true ? (input.swappedUnderStatic ?? []).filter((n) => added.includes(n)) : [];
|
|
395
402
|
blocks.push((input.staticFace === true
|
|
396
403
|
? "The following deferred tools are now active — call them directly. Their parameter schemas were " +
|
|
397
404
|
"provided in the ToolSearch result (the tools list itself keeps compact placeholder entries):\n"
|
|
398
405
|
: "The following deferred tools are now available. Their full schemas are loaded — call them " +
|
|
399
406
|
"directly like any other tool:\n") +
|
|
400
|
-
added.map((n) => `- ${n}`).join("\n")
|
|
407
|
+
added.map((n) => `- ${n}`).join("\n") +
|
|
408
|
+
(swapped.length > 0
|
|
409
|
+
? `\nException: ${swapped.join(", ")} — too large to inline, so the full declaration is in the tools list instead.`
|
|
410
|
+
: ""));
|
|
401
411
|
}
|
|
402
412
|
const readded = input.readded ?? [];
|
|
403
413
|
if (readded.length > 0) {
|
|
404
414
|
blocks.push(`${readded.length} deferred tool${readded.length === 1 ? " is" : "s are"} available again (MCP server reconnected — ` +
|
|
405
415
|
`names announced earlier in this conversation): ${groupByMcpServer(readded)}. ` +
|
|
406
416
|
(input.staticFace === true
|
|
407
|
-
? `The tools list keeps compact placeholder entries — re-run ToolSearch ("select:<name>") if you need their current parameter schemas.`
|
|
417
|
+
? `The tools list keeps compact placeholder entries — re-run ToolSearch ("select:<name>") if you need their current parameter schemas.` +
|
|
418
|
+
((input.readdedSwappedUnderStatic ?? []).filter((n) => readded.includes(n)).length > 0
|
|
419
|
+
? ` Exception: ${(input.readdedSwappedUnderStatic ?? []).filter((n) => readded.includes(n)).join(", ")} — too large to inline, so the full declaration is in the tools list instead.`
|
|
420
|
+
: "")
|
|
408
421
|
: `Their schemas are loaded again — call them directly.`));
|
|
409
422
|
}
|
|
410
423
|
const removed = input.removed ?? [];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type SessionRepo } from "../internal/harness.js";
|
|
2
2
|
import { type AcquiredSession, type SessionStore, type SessionStoreSummary } from "./session.js";
|
|
3
|
+
import type { StoreDurability } from "./checkpoint-store.js";
|
|
3
4
|
import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
|
|
4
5
|
export { SESSION_DEFAULT_TTL_DAYS };
|
|
5
6
|
export type EvictPolicy = "delete" | "forget";
|
|
@@ -8,9 +9,11 @@ export interface TtlSessionStoreOptions {
|
|
|
8
9
|
sweepIntervalMs?: number;
|
|
9
10
|
repo?: SessionRepo;
|
|
10
11
|
evict?: EvictPolicy;
|
|
12
|
+
durability?: StoreDurability;
|
|
11
13
|
}
|
|
12
14
|
export declare class TtlSessionStore implements SessionStore {
|
|
13
15
|
readonly retention: "none";
|
|
16
|
+
readonly durability?: StoreDurability;
|
|
14
17
|
private repo;
|
|
15
18
|
private entries;
|
|
16
19
|
private owners;
|
|
@@ -5,6 +5,7 @@ import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
|
|
|
5
5
|
export { SESSION_DEFAULT_TTL_DAYS };
|
|
6
6
|
export class TtlSessionStore {
|
|
7
7
|
retention = "none";
|
|
8
|
+
durability;
|
|
8
9
|
repo;
|
|
9
10
|
entries = new Map();
|
|
10
11
|
owners = new Map();
|
|
@@ -15,6 +16,9 @@ export class TtlSessionStore {
|
|
|
15
16
|
sweepTimer;
|
|
16
17
|
constructor(opts = {}) {
|
|
17
18
|
this.repo = opts.repo ?? new InMemorySessionRepo();
|
|
19
|
+
const durability = opts.durability ?? (opts.repo === undefined ? "process-local" : undefined);
|
|
20
|
+
if (durability !== undefined)
|
|
21
|
+
this.durability = durability;
|
|
18
22
|
this.defaultTtlMs = (opts.defaultTtlDays ?? SESSION_DEFAULT_TTL_DAYS) * DAY_MS;
|
|
19
23
|
this.evictPolicy = opts.evict ?? (opts.repo ? "forget" : "delete");
|
|
20
24
|
if (opts.sweepIntervalMs !== undefined && opts.sweepIntervalMs > 0) {
|
package/dist/core/session.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface SessionStoreSummary {
|
|
|
20
20
|
forkedFrom?: string;
|
|
21
21
|
}
|
|
22
22
|
export interface SessionStore {
|
|
23
|
+
readonly durability?: import("./checkpoint-store.js").StoreDurability;
|
|
23
24
|
acquire(sessionId?: string, opts?: {
|
|
24
25
|
requireExisting?: boolean;
|
|
25
26
|
}): Promise<AcquiredSession>;
|
|
@@ -52,6 +52,25 @@ export async function backgroundAgentStoreContract(mk, runAssertion) {
|
|
|
52
52
|
assert.equal(await store.get("a1", "tenant-C"), null, "an unrelated scope must not see the row");
|
|
53
53
|
assert.equal(await store.get("a-missing", S), null);
|
|
54
54
|
});
|
|
55
|
+
run("the org-admission fields round-trip, and ABSENT stays absent (a backend must not invent a verdict)", async (make) => {
|
|
56
|
+
const store = make();
|
|
57
|
+
await store.put(record({ handle: "a1", admittedOrgScopes: ["org:acme", "org:beta"], admittedOrgWriteScope: "org:acme" }));
|
|
58
|
+
const withVerdict = await held(store, "a1");
|
|
59
|
+
assert.deepEqual(withVerdict.admittedOrgScopes, ["org:acme", "org:beta"]);
|
|
60
|
+
assert.equal(withVerdict.admittedOrgWriteScope, "org:acme");
|
|
61
|
+
await store.put(record({ handle: "a2", admittedOrgScopes: [], admittedOrgWriteScope: null }));
|
|
62
|
+
const readOnly = await held(store, "a2");
|
|
63
|
+
assert.deepEqual(readOnly.admittedOrgScopes, []);
|
|
64
|
+
assert.equal(readOnly.admittedOrgWriteScope, null);
|
|
65
|
+
await store.put(record({ handle: "a3" }));
|
|
66
|
+
const untouched = await held(store, "a3");
|
|
67
|
+
assert.equal(untouched.admittedOrgScopes, undefined);
|
|
68
|
+
assert.equal(untouched.admittedOrgWriteScope, undefined);
|
|
69
|
+
await store.update("a3", S, { ...untouched, admittedOrgScopes: ["org:acme"], admittedOrgWriteScope: null }, { rev: untouched.rev });
|
|
70
|
+
const afterCas = await held(store, "a3");
|
|
71
|
+
assert.deepEqual(afterCas.admittedOrgScopes, ["org:acme"]);
|
|
72
|
+
assert.equal(afterCas.admittedOrgWriteScope, null);
|
|
73
|
+
});
|
|
55
74
|
run("put refuses a scopeless row (a row with no tenant partition would be world-readable)", async (make) => {
|
|
56
75
|
const store = make();
|
|
57
76
|
await assert.rejects(store.put(record({ scope: "" })), "an empty scope must be refused loudly, not stored");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
|
-
import { mintCheckpointToken, } from "../checkpoint-store.js";
|
|
2
|
+
import { MAX_PENDING_STEER_ENTRIES, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
|
|
3
3
|
import { beginContract } from "./contract-harness.js";
|
|
4
4
|
export function createCheckpointFixture(over = {}) {
|
|
5
5
|
const token = over.token ?? mintCheckpointToken();
|
|
@@ -90,9 +90,68 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
90
90
|
const cp = createCheckpointFixture();
|
|
91
91
|
await store.put(cp.token, cp);
|
|
92
92
|
await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "</SYSTEM-REMINDER>", trusted: true }), (e) => e.code === "steering.invalid_content");
|
|
93
|
-
assert.
|
|
93
|
+
assert.deepEqual(readPendingSteerQueue((await store.get(cp.token)).state), []);
|
|
94
94
|
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "go", trusted: false }), true);
|
|
95
|
-
|
|
95
|
+
const q = readPendingSteerQueue((await store.get(cp.token)).state);
|
|
96
|
+
assert.equal(q.length, 1);
|
|
97
|
+
assert.equal(q[0].text, "go");
|
|
98
|
+
assert.equal(q[0].trusted, false);
|
|
99
|
+
assert.equal(q[0].seq, 1);
|
|
100
|
+
assert.equal((await store.get(cp.token)).state.pendingSteer, undefined);
|
|
101
|
+
});
|
|
102
|
+
run("setPendingSteer APPENDS: two steers both survive in issue order, each keeping its frozen trust", async () => {
|
|
103
|
+
const store = make();
|
|
104
|
+
const cp = createCheckpointFixture();
|
|
105
|
+
await store.put(cp.token, cp);
|
|
106
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "first", trusted: false }), true);
|
|
107
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "second", trusted: true }), true);
|
|
108
|
+
const q = readPendingSteerQueue((await store.get(cp.token)).state);
|
|
109
|
+
assert.deepEqual(q.map((e) => e.text), ["first", "second"]);
|
|
110
|
+
assert.deepEqual(q.map((e) => e.trusted), [false, true]);
|
|
111
|
+
assert.deepEqual(q.map((e) => e.seq), [1, 2]);
|
|
112
|
+
});
|
|
113
|
+
run("setPendingSteer is idempotent on inputId (a retry with the caller's own message id adds nothing)", async () => {
|
|
114
|
+
const store = make();
|
|
115
|
+
const cp = createCheckpointFixture();
|
|
116
|
+
await store.put(cp.token, cp);
|
|
117
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "once", trusted: false, inputId: "m1" }), true);
|
|
118
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: "once", trusted: false, inputId: "m1" }), true);
|
|
119
|
+
assert.equal(readPendingSteerQueue((await store.get(cp.token)).state).length, 1);
|
|
120
|
+
});
|
|
121
|
+
run("the queue bound is fail-loud steering.queue_full and evicts nothing", async () => {
|
|
122
|
+
const store = make();
|
|
123
|
+
const cp = createCheckpointFixture();
|
|
124
|
+
await store.put(cp.token, cp);
|
|
125
|
+
for (let i = 0; i < MAX_PENDING_STEER_ENTRIES; i++) {
|
|
126
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, { text: `s${i}`, trusted: false }), true);
|
|
127
|
+
}
|
|
128
|
+
await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "overflow", trusted: false }), (e) => e.code === "steering.queue_full");
|
|
129
|
+
const q = readPendingSteerQueue((await store.get(cp.token)).state);
|
|
130
|
+
assert.equal(q.length, MAX_PENDING_STEER_ENTRIES);
|
|
131
|
+
assert.deepEqual(q.map((e) => e.text), Array.from({ length: MAX_PENDING_STEER_ENTRIES }, (_, i) => `s${i}`));
|
|
132
|
+
});
|
|
133
|
+
run("a queue entry round-trips every FROZEN field (actor/inputId/priority included)", async () => {
|
|
134
|
+
const store = make();
|
|
135
|
+
const cp = createCheckpointFixture();
|
|
136
|
+
await store.put(cp.token, cp);
|
|
137
|
+
assert.equal(await store.setPendingSteer(cp.token, cp.scope, {
|
|
138
|
+
text: "attributed",
|
|
139
|
+
trusted: true,
|
|
140
|
+
actor: { id: "slack:U1", hostAsserted: true, issuer: "slack-app:A1" },
|
|
141
|
+
inputId: "m-42",
|
|
142
|
+
priority: "now",
|
|
143
|
+
}), true);
|
|
144
|
+
const q = readPendingSteerQueue((await store.get(cp.token)).state);
|
|
145
|
+
assert.deepEqual(q, [
|
|
146
|
+
{ text: "attributed", trusted: true, actor: { id: "slack:U1", hostAsserted: true, issuer: "slack-app:A1" }, inputId: "m-42", priority: "now", seq: 1 },
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
run("an unknown steering field is REFUSED (a producer must not have its new field silently dropped)", async () => {
|
|
150
|
+
const store = make();
|
|
151
|
+
const cp = createCheckpointFixture();
|
|
152
|
+
await store.put(cp.token, cp);
|
|
153
|
+
await assert.rejects(store.setPendingSteer(cp.token, cp.scope, { text: "go", trusted: false, futureField: 1 }), (e) => e.code === "steering.invalid_content");
|
|
154
|
+
assert.deepEqual(readPendingSteerQueue((await store.get(cp.token)).state), []);
|
|
96
155
|
});
|
|
97
156
|
run("expire vs resolve on the same row → exactly one wins; reap expires past-deadline pending in scope", async () => {
|
|
98
157
|
const store = make();
|
|
@@ -41,6 +41,8 @@ export interface SystemInjection<TPayload = unknown> {
|
|
|
41
41
|
}
|
|
42
42
|
export declare function taskNotificationDedupKey(n: Pick<TaskNotificationPayload, "task_id" | "task_type" | "status" | "seq">): string;
|
|
43
43
|
export declare function taskNotificationLaneKey(n: Pick<TaskNotificationPayload, "task_id" | "task_type">): string;
|
|
44
|
+
export declare function attrEscape(value: string): string;
|
|
45
|
+
export declare const EXTERNAL_SOURCE_MAX = 120;
|
|
44
46
|
export declare function renderTaskNotificationXml(n: TaskNotificationPayload): string;
|
|
45
47
|
export declare const MAX_PENDING_EVENTS_PER_TASK = 50;
|
|
46
48
|
export declare const MAX_PENDING_EVENTS_PER_SESSION = 200;
|
|
@@ -38,10 +38,10 @@ export function taskNotificationDedupKey(n) {
|
|
|
38
38
|
export function taskNotificationLaneKey(n) {
|
|
39
39
|
return n.task_type === "external" ? `external:${n.task_id}` : n.task_id;
|
|
40
40
|
}
|
|
41
|
-
function attrEscape(value) {
|
|
41
|
+
export function attrEscape(value) {
|
|
42
42
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
43
43
|
}
|
|
44
|
-
const EXTERNAL_SOURCE_MAX = 120;
|
|
44
|
+
export const EXTERNAL_SOURCE_MAX = 120;
|
|
45
45
|
export function renderTaskNotificationXml(n) {
|
|
46
46
|
const usage = n.usage === undefined
|
|
47
47
|
? undefined
|
|
@@ -54,7 +54,9 @@ export function renderTaskNotificationXml(n) {
|
|
|
54
54
|
}
|
|
55
55
|
})();
|
|
56
56
|
const open = n.task_type === "external"
|
|
57
|
-
? `<task-notification type="external"${n.source !== undefined
|
|
57
|
+
? `<task-notification type="external"${n.source !== undefined
|
|
58
|
+
? ` from="${attrEscape(inlineUntrusted(n.source, EXTERNAL_SOURCE_MAX))}" from-unverified="true"`
|
|
59
|
+
: ""}>`
|
|
58
60
|
: "<task-notification>";
|
|
59
61
|
return [
|
|
60
62
|
open,
|
|
@@ -28,6 +28,7 @@ export declare function reapDurableAgentsLane(core: DurableAgentCore, scope: str
|
|
|
28
28
|
}>;
|
|
29
29
|
export declare function releaseDurableTranscriptAnchorLane(core: DurableAgentCore, id: string): void;
|
|
30
30
|
export declare function bindBackgroundAgentSessionLane(core: DurableAgentCore, id: string, sessionId: string): void;
|
|
31
|
+
export declare function recordBackgroundAgentOrgAdmissionLane(core: DurableAgentCore, id: string, verdict: import("./memory-admission.js").OwnOrgAdmissionVerdict): void;
|
|
31
32
|
export declare function registerBackgroundAgentLane(core: DurableAgentCore, input: RegisterBackgroundAgentInput): string;
|
|
32
33
|
export declare function parkBackgroundAgentLane(core: DurableAgentCore, id: string, park: {
|
|
33
34
|
checkpointToken: string;
|
|
@@ -247,6 +247,12 @@ export function bindBackgroundAgentSessionLane(core, id, sessionId) {
|
|
|
247
247
|
handle.sessionId = sessionId;
|
|
248
248
|
durableAgentWriteLane(handle, { sessionId });
|
|
249
249
|
}
|
|
250
|
+
export function recordBackgroundAgentOrgAdmissionLane(core, id, verdict) {
|
|
251
|
+
const handle = core.handles.get(id);
|
|
252
|
+
if (!handle || handle.type !== "background_agent")
|
|
253
|
+
return;
|
|
254
|
+
durableAgentWriteLane(handle, { admittedOrgScopes: [...verdict.scopes], admittedOrgWriteScope: verdict.writeScope });
|
|
255
|
+
}
|
|
250
256
|
export function registerBackgroundAgentLane(core, input) {
|
|
251
257
|
assertOwnership(input, "registerBackgroundAgent");
|
|
252
258
|
if (input.id !== undefined && !DURABLE_AGENT_HANDLE_RE.test(input.id)) {
|
|
@@ -101,6 +101,7 @@ export declare class TaskRegistry {
|
|
|
101
101
|
releaseDurableTranscriptAnchor(id: string): void;
|
|
102
102
|
bindBackgroundAgentSession(id: string, sessionId: string): void;
|
|
103
103
|
registerBackgroundAgent(input: RegisterBackgroundAgentInput): string;
|
|
104
|
+
recordBackgroundAgentOrgAdmission(id: string, verdict: import("./memory-admission.js").OwnOrgAdmissionVerdict): void;
|
|
104
105
|
parkBackgroundAgent(id: string, park: {
|
|
105
106
|
checkpointToken: string;
|
|
106
107
|
seq?: number;
|
|
@@ -10,7 +10,7 @@ import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task
|
|
|
10
10
|
import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, renderSpoolBody, spoolDropNote, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
|
|
11
11
|
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
|
|
12
12
|
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
|
|
13
|
-
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
13
|
+
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, recordBackgroundAgentOrgAdmissionLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
14
14
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
15
15
|
const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
|
|
16
16
|
const BLOCK_MAX_TIMEOUT_MS = 600_000;
|
|
@@ -146,6 +146,9 @@ export class TaskRegistry {
|
|
|
146
146
|
registerBackgroundAgent(input) {
|
|
147
147
|
return registerBackgroundAgentLane(this.core, input);
|
|
148
148
|
}
|
|
149
|
+
recordBackgroundAgentOrgAdmission(id, verdict) {
|
|
150
|
+
return recordBackgroundAgentOrgAdmissionLane(this.core, id, verdict);
|
|
151
|
+
}
|
|
149
152
|
parkBackgroundAgent(id, park) {
|
|
150
153
|
return parkBackgroundAgentLane(this.core, id, park);
|
|
151
154
|
}
|
|
@@ -39,8 +39,9 @@ export function composeTaskOutputDescription(caps) {
|
|
|
39
39
|
}
|
|
40
40
|
parts.push("Retrieve output or status for a background task by task_id. ");
|
|
41
41
|
parts.push(caps.lanes
|
|
42
|
-
?
|
|
43
|
-
"
|
|
42
|
+
?
|
|
43
|
+
"Supports background Bash tasks and workflow tasks returned by Workflow. Returns only the caller's own task scope; unknown and out-of-scope ids are " +
|
|
44
|
+
"reported the same way. "
|
|
44
45
|
: "Reads a background shell started with Bash(run_in_background): returns the run status (running / exited(code) / killed / failed) plus NEW stdout/stderr since your last call. ");
|
|
45
46
|
if (!caps.notification) {
|
|
46
47
|
parts.push('When using a background command as a wait condition, call this repeatedly until status is no longer "running" — completion ' +
|
|
@@ -64,7 +65,7 @@ export function composeTaskOutputParams(caps) {
|
|
|
64
65
|
return Type.Object({
|
|
65
66
|
task_id: Type.Optional(Type.String({
|
|
66
67
|
description: caps.lanes
|
|
67
|
-
? "The task_id returned by Bash(run_in_background) or
|
|
68
|
+
? "The task_id returned by Bash(run_in_background) or Workflow."
|
|
68
69
|
: "The task_id returned by Bash(run_in_background).",
|
|
69
70
|
})),
|
|
70
71
|
...(caps.blockWait
|
|
@@ -80,12 +80,17 @@ export interface AskRequest {
|
|
|
80
80
|
toolCallId: string;
|
|
81
81
|
args: unknown;
|
|
82
82
|
readonly preview?: unknown;
|
|
83
|
+
readonly boundInputHash?: string;
|
|
83
84
|
message: string;
|
|
84
85
|
readonly principal?: string;
|
|
85
86
|
readonly sourceTaskId?: string;
|
|
86
87
|
readonly fromSubagent?: true;
|
|
87
88
|
readonly sourceAgentName?: string;
|
|
88
89
|
readonly requiresRealApproval?: boolean;
|
|
90
|
+
readonly riskAxes?: {
|
|
91
|
+
readonly irreversible?: boolean;
|
|
92
|
+
readonly egress?: boolean;
|
|
93
|
+
};
|
|
89
94
|
readonly delegation?: AskDelegationProvenance;
|
|
90
95
|
}
|
|
91
96
|
export type OnAsk = "deny" | "allow" | ((req: AskRequest, signal?: AbortSignal) => AskOutcome | Promise<AskOutcome>);
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { isAbsolute, join, normalize as normalizePath, sep } from "node:path";
|
|
3
3
|
import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
|
|
4
|
+
import { boundInputHashOf } from "./canonical-json.js";
|
|
4
5
|
import { writeTargetPath } from "../tools/fs/safety.js";
|
|
5
6
|
export function decisionText(d) {
|
|
6
7
|
return d.message;
|
|
@@ -606,7 +607,7 @@ export async function resolveAsk(req, onAsk, signal) {
|
|
|
606
607
|
decisionReason: "mode",
|
|
607
608
|
};
|
|
608
609
|
}
|
|
609
|
-
ok = await onAsk({ ...req, args: approverView.value }, signal);
|
|
610
|
+
ok = await onAsk({ ...req, boundInputHash: boundInputHashOf(presented.value), args: approverView.value }, signal);
|
|
610
611
|
}
|
|
611
612
|
catch (err) {
|
|
612
613
|
return {
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface TraceMechanismsSummary {
|
|
|
9
9
|
repetitionCuts?: number;
|
|
10
10
|
repetitionSpared?: number;
|
|
11
11
|
}
|
|
12
|
+
export interface ToolDisclosureManifest {
|
|
13
|
+
deferredTools: number;
|
|
14
|
+
strategy: "swap" | "static";
|
|
15
|
+
source: "spec" | "env" | "default" | "degraded_no_direct_lane";
|
|
16
|
+
}
|
|
12
17
|
export type TraceEvent = {
|
|
13
18
|
kind: "task.start";
|
|
14
19
|
version: 1;
|
|
@@ -47,6 +52,7 @@ export type TraceEvent = {
|
|
|
47
52
|
form: string;
|
|
48
53
|
intentionalDivergences: readonly string[];
|
|
49
54
|
};
|
|
55
|
+
toolDisclosure?: ToolDisclosureManifest;
|
|
50
56
|
ts: number;
|
|
51
57
|
} | {
|
|
52
58
|
kind: "prompt.snapshot_changed";
|