@sema-agent/core 5.17.0-pre.0 → 5.18.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 +186 -1
- package/dist/agents/subagent.d.ts +8 -0
- package/dist/agents/subagent.js +93 -2
- package/dist/core/ask-question.js +6 -1
- package/dist/core/auto-compaction.d.ts +6 -0
- package/dist/core/auto-compaction.js +15 -1
- package/dist/core/canonical-json.js +176 -14
- package/dist/core/checkpoint-store.d.ts +15 -0
- package/dist/core/checkpoint-store.js +73 -0
- package/dist/core/governance-codes.js +1 -0
- package/dist/core/hooks.d.ts +12 -1
- package/dist/core/hooks.js +41 -6
- package/dist/core/mcp.d.ts +1 -0
- package/dist/core/mcp.js +18 -3
- package/dist/core/memory-engine/content-origin.d.ts +27 -0
- package/dist/core/memory-engine/content-origin.js +38 -0
- package/dist/core/memory-engine/engine.d.ts +12 -2
- package/dist/core/memory-engine/engine.js +172 -12
- package/dist/core/memory-engine/file-backend.d.ts +4 -0
- package/dist/core/memory-engine/file-backend.js +25 -3
- package/dist/core/memory-engine/index.d.ts +2 -1
- package/dist/core/memory-engine/index.js +2 -1
- package/dist/core/memory-engine/layout.d.ts +16 -0
- package/dist/core/memory-engine/layout.js +90 -2
- package/dist/core/memory-engine/sync-client.d.ts +1 -0
- package/dist/core/memory-engine/sync-client.js +23 -5
- package/dist/core/memory-engine/tools.d.ts +55 -0
- package/dist/core/memory-engine/tools.js +307 -0
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/memory.d.ts +4 -0
- package/dist/core/memory.js +15 -2
- package/dist/core/permission-rule-consent.d.ts +131 -0
- package/dist/core/permission-rule-consent.js +307 -0
- package/dist/core/permission-rule-model.d.ts +66 -0
- package/dist/core/permission-rule-model.js +135 -0
- package/dist/core/permission-rule-store.d.ts +89 -0
- package/dist/core/permission-rule-store.js +145 -0
- package/dist/core/permission-rules.d.ts +3 -2
- package/dist/core/permission-rules.js +9 -4
- package/dist/core/runner/prepare-memory.d.ts +3 -1
- package/dist/core/runner/prepare-memory.js +54 -14
- package/dist/core/runner/prepare-task.d.ts +12 -0
- package/dist/core/runner/prepare-task.js +270 -32
- package/dist/core/runner/runtask.js +27 -1
- package/dist/core/runner/tool-output-projection.js +1 -1
- package/dist/core/runner/turn-attachments.d.ts +2 -1
- package/dist/core/runner/turn-attachments.js +9 -6
- package/dist/core/session-reconcile.js +19 -1
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/tool-policy.d.ts +17 -1
- package/dist/core/tool-policy.js +64 -8
- package/dist/core/tools.js +1 -0
- package/dist/core/trace.d.ts +20 -0
- package/dist/core/types.d.ts +15 -0
- package/dist/core/wiring-manifest.d.ts +5 -1
- package/dist/core/wiring-manifest.js +4 -2
- package/dist/engine/llm/validation.js +121 -5
- package/dist/engine/loop/agent-loop.d.ts +2 -0
- package/dist/engine/loop/agent-loop.js +17 -4
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -1
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +1 -1
- package/dist/stores/file/checkpoint-store.d.ts +1 -0
- package/dist/stores/file/checkpoint-store.js +1 -0
- package/dist/stores/file/permission-rule-store.d.ts +32 -0
- package/dist/stores/file/permission-rule-store.js +213 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +11 -10
- package/dist/tools/fs/fs-bash.js +17 -10
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +15 -0
- package/dist/tools/fs/fs-shared.js +73 -2
- package/dist/tools/fs/fs-write.js +8 -8
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/safety.d.ts +1 -0
- package/dist/tools/fs/safety.js +9 -2
- package/dist/tools/web.js +2 -0
- package/package.json +1 -1
|
@@ -62,7 +62,7 @@ const CC_DETAIL_TYPES = new Set([
|
|
|
62
62
|
"agent", "task", "task-list", "task-output", "workflow-run",
|
|
63
63
|
"web-fetch", "web-search", "todo", "cron-create", "cron-delete", "cron-list", "image",
|
|
64
64
|
"task-stop", "tool-search", "repo-map", "fork", "enter-plan-mode", "exit-plan-mode",
|
|
65
|
-
"monitor-start", "path_not_in_root", "readonly_out_of_root",
|
|
65
|
+
"monitor-start", "path_not_in_root", "readonly_out_of_root", "bash_invalid_timeout",
|
|
66
66
|
"report-findings", "schedule-wakeup", "send-message", "agent-transcript", "a2a", "document",
|
|
67
67
|
]);
|
|
68
68
|
export const structuredFrom = (result) => {
|
|
@@ -94,6 +94,7 @@ export interface AttachmentInputs {
|
|
|
94
94
|
mcpInstructions?: boolean;
|
|
95
95
|
};
|
|
96
96
|
undiscoveredTools?: readonly string[];
|
|
97
|
+
deferDirectCall?: boolean;
|
|
97
98
|
changedFiles?: ReadonlyArray<{
|
|
98
99
|
path: string;
|
|
99
100
|
mtimeMs: number;
|
|
@@ -118,7 +119,7 @@ export interface AttachmentInputs {
|
|
|
118
119
|
mcpDroppedTools?: ReadonlyArray<McpDroppedTool>;
|
|
119
120
|
}
|
|
120
121
|
export declare function collectDueAttachments(state: AttachmentState, inp: AttachmentInputs): readonly TurnAttachment[];
|
|
121
|
-
export declare function renderToolSearchUsageReminder(undiscovered: readonly string[]): string;
|
|
122
|
+
export declare function renderToolSearchUsageReminder(undiscovered: readonly string[], directCallEnabled?: boolean): string;
|
|
122
123
|
export declare function renderDateChange(newDate: string): string;
|
|
123
124
|
export declare function collectDateChange(state: DateChangeState, today: string): TurnAttachment | undefined;
|
|
124
125
|
export declare function collectInstructionsChange(state: InstructionsChangeState, probed: ReadonlyArray<{
|
|
@@ -145,7 +145,7 @@ export function collectDueAttachments(state, inp) {
|
|
|
145
145
|
const sinceUse = state.toolSearchLastUseTurn === undefined ? Number.POSITIVE_INFINITY : cadenceNow - state.toolSearchLastUseTurn;
|
|
146
146
|
if (sinceUse >= TOOL_SEARCH_REMINDER_CONFIG.EVERY_N_TURNS && cadenceNow - state.lastToolSearchReminderTurn >= TOOL_SEARCH_REMINDER_CONFIG.EVERY_N_TURNS) {
|
|
147
147
|
state.lastToolSearchReminderTurn = cadenceNow;
|
|
148
|
-
(out ??= []).push({ source: "tool_search_usage_reminder", body: renderToolSearchUsageReminder(inp.undiscoveredTools) });
|
|
148
|
+
(out ??= []).push({ source: "tool_search_usage_reminder", body: renderToolSearchUsageReminder(inp.undiscoveredTools, inp.deferDirectCall !== false) });
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
if (inp.config.planModeReminder && inp.planActive) {
|
|
@@ -248,14 +248,17 @@ function renderTaskReminder(state) {
|
|
|
248
248
|
}
|
|
249
249
|
return message;
|
|
250
250
|
}
|
|
251
|
-
export function renderToolSearchUsageReminder(undiscovered) {
|
|
251
|
+
export function renderToolSearchUsageReminder(undiscovered, directCallEnabled = true) {
|
|
252
252
|
const shown = undiscovered.slice(0, TOOL_SEARCH_REMINDER_CONFIG.MAX_NAMES);
|
|
253
253
|
const more = undiscovered.length - shown.length;
|
|
254
254
|
const names = `${shown.join(", ")}${more > 0 ? ` (+${more} more)` : ""}`;
|
|
255
255
|
return (`Some available tools' schemas are not loaded in this conversation yet: ${names}. Before concluding a ` +
|
|
256
256
|
`capability is missing or building a workaround, use ${TOOL_SEARCH_TOOL_NAME} to find and load relevant tools — ` +
|
|
257
|
-
`keywords to search, or query "select:<name>[,<name>...]" for specific tools.
|
|
258
|
-
|
|
257
|
+
`keywords to search, or query "select:<name>[,<name>...]" for specific tools. ` +
|
|
258
|
+
(directCallEnabled
|
|
259
|
+
? `You do not have these tools' parameters, so activate one rather than guessing its arguments. `
|
|
260
|
+
: `Calling a tool before its schema is loaded will fail. `) +
|
|
261
|
+
`This is just a gentle reminder - ignore if not applicable to the current work.`);
|
|
259
262
|
}
|
|
260
263
|
export function renderDateChange(newDate) {
|
|
261
264
|
return `The date has changed. Today's date is now ${newDate}. DO NOT mention this to the user explicitly because they are already aware.`;
|
|
@@ -400,8 +403,8 @@ export function renderToolsDelta(input) {
|
|
|
400
403
|
if (added.length > 0) {
|
|
401
404
|
const swapped = input.staticFace === true ? (input.swappedUnderStatic ?? []).filter((n) => added.includes(n)) : [];
|
|
402
405
|
blocks.push((input.staticFace === true
|
|
403
|
-
? "The following deferred tools are now active — call them directly.
|
|
404
|
-
|
|
406
|
+
? "The following deferred tools are now active — call them directly. The tools list keeps compact " +
|
|
407
|
+
'placeholder entries, so re-run ToolSearch ("select:<name>") if you need a parameter schema:\n'
|
|
405
408
|
: "The following deferred tools are now available. Their full schemas are loaded — call them " +
|
|
406
409
|
"directly like any other tool:\n") +
|
|
407
410
|
added.map((n) => `- ${n}`).join("\n") +
|
|
@@ -10,6 +10,20 @@ const INTERRUPTED_IDEMPOTENT = "[INTERRUPTED] The previous run ended before this
|
|
|
10
10
|
"what you intended. If you decide not to re-issue it, verify the current state first.";
|
|
11
11
|
const INTERRUPTED_NEVER_STARTED = "[INTERRUPTED] The run was aborted before this tool call started. It was never executed and had " +
|
|
12
12
|
"no side effects — it is safe to re-issue this call if you still need it.";
|
|
13
|
+
const INTERRUPTED_REPEAT_UNKNOWN = "[INTERRUPTED] Same as an earlier interrupted call in this batch: no result was recorded and the outcome is " +
|
|
14
|
+
"UNKNOWN — verify before relying on it.";
|
|
15
|
+
const INTERRUPTED_REPEAT_SAFE = "[INTERRUPTED] Same as an earlier interrupted call in this batch: this tool is read-only/idempotent, so just " +
|
|
16
|
+
"call it again if you still need the result.";
|
|
17
|
+
const INTERRUPTED_REPEAT_IDEMPOTENT = "[INTERRUPTED] Same as an earlier interrupted call in this batch: it is safe to REPLAY — re-issue it, or verify " +
|
|
18
|
+
"the current state if you decide not to.";
|
|
19
|
+
const INTERRUPTED_REPEAT_NEVER_STARTED = "[INTERRUPTED] Same as an earlier interrupted call in this batch: it never started and had no side effects — " +
|
|
20
|
+
"safe to re-issue.";
|
|
21
|
+
const REPEAT_TEXT = {
|
|
22
|
+
never_started: INTERRUPTED_REPEAT_NEVER_STARTED,
|
|
23
|
+
read: INTERRUPTED_REPEAT_SAFE,
|
|
24
|
+
idempotent: INTERRUPTED_REPEAT_IDEMPOTENT,
|
|
25
|
+
unknown: INTERRUPTED_REPEAT_UNKNOWN,
|
|
26
|
+
};
|
|
13
27
|
export function findOrphanToolCalls(messages, suspendedBatch) {
|
|
14
28
|
const resolvedAt = new Map();
|
|
15
29
|
const callSitesAt = new Map();
|
|
@@ -70,11 +84,15 @@ export async function reconcileInterruptedSession(session, toolEffects, suspende
|
|
|
70
84
|
const { messages } = await session.buildContext();
|
|
71
85
|
const orphans = findOrphanToolCalls(messages, suspendedBatch).filter((o) => o.kind !== "result");
|
|
72
86
|
const recovered = [];
|
|
87
|
+
const statedInFull = new Set();
|
|
73
88
|
for (const orphan of orphans) {
|
|
74
89
|
const effect = toolEffects?.get(orphan.toolName) ?? "write";
|
|
75
90
|
const effectText = effect === "read" ? INTERRUPTED_SAFE : effect === "idempotent" ? INTERRUPTED_IDEMPOTENT : INTERRUPTED_UNKNOWN;
|
|
76
91
|
const neverStarted = startedToolCallIds !== undefined && !startedToolCallIds.has(orphan.toolCallId);
|
|
77
|
-
const
|
|
92
|
+
const fullText = neverStarted ? INTERRUPTED_NEVER_STARTED : effectText;
|
|
93
|
+
const cls = neverStarted ? "never_started" : effect === "read" ? "read" : effect === "idempotent" ? "idempotent" : "unknown";
|
|
94
|
+
const text = statedInFull.has(cls) ? REPEAT_TEXT[cls] : fullText;
|
|
95
|
+
statedInFull.add(cls);
|
|
78
96
|
const errorKind = neverStarted ? "interrupted_never_started" : "interrupted_outcome_unknown";
|
|
79
97
|
const entryId = await session.appendMessage({
|
|
80
98
|
role: "toolResult",
|
|
@@ -982,6 +982,7 @@ export async function reviveBackgroundAgentLane(core, id, access, abort) {
|
|
|
982
982
|
handle.errorRetryAfterMs = undefined;
|
|
983
983
|
handle.resultIsPartial = undefined;
|
|
984
984
|
handle.stopSource = undefined;
|
|
985
|
+
handle.stoppedBy = undefined;
|
|
985
986
|
handle.completionId = undefined;
|
|
986
987
|
handle.terminalNotified = undefined;
|
|
987
988
|
handle.reviveCycle = (handle.reviveCycle ?? 0) + 1;
|
|
@@ -8,7 +8,7 @@ export interface ToolCallRequest {
|
|
|
8
8
|
suspendCount: number;
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
export type DecisionReason = "rule" | "mode" | "hook" | "safety" | "classifier";
|
|
11
|
+
export type DecisionReason = "rule" | "mode" | "hook" | "safety" | "classifier" | "persisted_rule";
|
|
12
12
|
export type PermissionResult = {
|
|
13
13
|
action: "allow";
|
|
14
14
|
updatedInput?: unknown;
|
|
@@ -43,6 +43,12 @@ export declare function toolPolicyNameSets(p: ToolPolicy | undefined): readonly
|
|
|
43
43
|
export declare function createAllowDenyPolicy(opts: {
|
|
44
44
|
allow?: string[];
|
|
45
45
|
deny?: string[];
|
|
46
|
+
onInvalidName?: "throw" | "skip";
|
|
47
|
+
onInvalidNameIssue?: (issue: {
|
|
48
|
+
entry: string;
|
|
49
|
+
list: "allow" | "deny";
|
|
50
|
+
message: string;
|
|
51
|
+
}) => void;
|
|
46
52
|
}): NamedToolPolicy;
|
|
47
53
|
export declare function createApprovalPolicy(opts: {
|
|
48
54
|
requireApproval: string[];
|
|
@@ -81,6 +87,7 @@ export interface AskRequest {
|
|
|
81
87
|
args: unknown;
|
|
82
88
|
readonly preview?: unknown;
|
|
83
89
|
readonly boundInputHash?: string;
|
|
90
|
+
readonly ruleSuggestions?: readonly import("./permission-rule-model.js").RuleSuggestion[];
|
|
84
91
|
message: string;
|
|
85
92
|
readonly principal?: string;
|
|
86
93
|
readonly sourceTaskId?: string;
|
|
@@ -100,6 +107,15 @@ export type AskOutcome = boolean | "unavailable" | {
|
|
|
100
107
|
};
|
|
101
108
|
export declare function withDelegationProvenance(onAsk: OnAsk, delegation: AskDelegationProvenance): OnAsk;
|
|
102
109
|
export declare function askApproverIdentity(onAsk: unknown): unknown;
|
|
110
|
+
export declare function tryCloneArgs<T>(v: T): {
|
|
111
|
+
ok: true;
|
|
112
|
+
value: T;
|
|
113
|
+
} | {
|
|
114
|
+
ok: false;
|
|
115
|
+
reason: string;
|
|
116
|
+
cause?: unknown;
|
|
117
|
+
};
|
|
118
|
+
export declare function describeThrown(err: unknown): string;
|
|
103
119
|
export type ResolvedAsk = PermissionResult & {
|
|
104
120
|
approverUnavailable?: true;
|
|
105
121
|
presentedInput?: unknown;
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -2,6 +2,8 @@ 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
4
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
5
|
+
import { inlineUntrusted } from "./untrusted-text.js";
|
|
6
|
+
import { parsePermissionRule } from "./permission-rules.js";
|
|
5
7
|
import { writeTargetPath } from "../tools/fs/safety.js";
|
|
6
8
|
export function decisionText(d) {
|
|
7
9
|
return d.message;
|
|
@@ -43,6 +45,41 @@ export function toolPolicyNameSets(p) {
|
|
|
43
45
|
return Array.isArray(sets) ? sets : [];
|
|
44
46
|
}
|
|
45
47
|
export function createAllowDenyPolicy(opts) {
|
|
48
|
+
const invalid = [];
|
|
49
|
+
const screen = (entries, list) => {
|
|
50
|
+
if (entries === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
const kept = [];
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (parsePermissionRule(entry).ruleContent === undefined) {
|
|
55
|
+
kept.push(entry);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
invalid.push({
|
|
59
|
+
entry,
|
|
60
|
+
list,
|
|
61
|
+
message: `"${entry}" is a rule CONTENT form, not a tool name — a name set matches raw tool names, so this entry ` +
|
|
62
|
+
`can never match any mounted tool (in an allow list it removes the tool entirely). Use the tool NAME here, ` +
|
|
63
|
+
`and route the narrowing through the lane that speaks this grammar: parameter rules via ` +
|
|
64
|
+
`createPermissionRulePolicy, Bash command prefixes via the persisted allow-rule lane.`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return kept;
|
|
68
|
+
};
|
|
69
|
+
const screenedAllow = screen(opts.allow, "allow");
|
|
70
|
+
const screenedDeny = screen(opts.deny, "deny");
|
|
71
|
+
if (invalid.length > 0) {
|
|
72
|
+
if ((opts.onInvalidName ?? "throw") === "throw") {
|
|
73
|
+
const e = new Error(`createAllowDenyPolicy: ${invalid.length} entr${invalid.length === 1 ? "y is" : "ies are"} not tool name(s):\n` +
|
|
74
|
+
invalid.map((i) => ` [${i.list}] ${i.message}`).join("\n"));
|
|
75
|
+
e.code = "config.invalid_tool_name_set";
|
|
76
|
+
e.issues = invalid;
|
|
77
|
+
throw e;
|
|
78
|
+
}
|
|
79
|
+
for (const issue of invalid)
|
|
80
|
+
opts.onInvalidNameIssue?.(issue);
|
|
81
|
+
}
|
|
82
|
+
opts = { ...opts, ...(screenedAllow ? { allow: screenedAllow } : {}), ...(screenedDeny ? { deny: screenedDeny } : {}) };
|
|
46
83
|
const allow = opts.allow ? new Set(opts.allow) : undefined;
|
|
47
84
|
const deny = new Set(opts.deny ?? []);
|
|
48
85
|
return {
|
|
@@ -90,7 +127,7 @@ export function createApprovalPolicy(opts) {
|
|
|
90
127
|
catch (err) {
|
|
91
128
|
return {
|
|
92
129
|
action: "deny",
|
|
93
|
-
message: `approval errored for "${req.toolName}": ${
|
|
130
|
+
message: `approval errored for "${req.toolName}": ${describeThrown(err)}`,
|
|
94
131
|
};
|
|
95
132
|
}
|
|
96
133
|
const okRaw = ok;
|
|
@@ -528,17 +565,36 @@ export function withDelegationProvenance(onAsk, delegation) {
|
|
|
528
565
|
export function askApproverIdentity(onAsk) {
|
|
529
566
|
return typeof onAsk === "function" ? (delegatedApproverRoot.get(onAsk) ?? onAsk) : onAsk;
|
|
530
567
|
}
|
|
531
|
-
function tryCloneArgs(v) {
|
|
568
|
+
export function tryCloneArgs(v) {
|
|
532
569
|
try {
|
|
533
570
|
const value = structuredClone(v);
|
|
534
571
|
if (containsSharedMemory(value))
|
|
535
|
-
return { ok: false };
|
|
572
|
+
return { ok: false, reason: "the value carries shared memory (SharedArrayBuffer), which a clone still aliases" };
|
|
536
573
|
return { ok: true, value };
|
|
537
574
|
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
return { ok: false, reason: describeThrown(err), cause: err };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
export function describeThrown(err) {
|
|
580
|
+
try {
|
|
581
|
+
if (typeof err === "string")
|
|
582
|
+
return containThrownText(err);
|
|
583
|
+
if (err instanceof Error) {
|
|
584
|
+
const message = err.message;
|
|
585
|
+
if (typeof message === "string" && message !== "")
|
|
586
|
+
return containThrownText(message);
|
|
587
|
+
}
|
|
588
|
+
return `a non-Error value was thrown (${err === null ? "null" : typeof err})`;
|
|
589
|
+
}
|
|
538
590
|
catch {
|
|
539
|
-
return
|
|
591
|
+
return "the value could not be described";
|
|
540
592
|
}
|
|
541
593
|
}
|
|
594
|
+
const THROWN_TEXT_MAX = 300;
|
|
595
|
+
function containThrownText(text) {
|
|
596
|
+
return inlineUntrusted(text, THROWN_TEXT_MAX);
|
|
597
|
+
}
|
|
542
598
|
function containsSharedMemory(v, seen = new Set()) {
|
|
543
599
|
if (typeof SharedArrayBuffer !== "undefined" && v instanceof SharedArrayBuffer)
|
|
544
600
|
return true;
|
|
@@ -593,7 +649,7 @@ export async function resolveAsk(req, onAsk, signal) {
|
|
|
593
649
|
if (!presented.ok) {
|
|
594
650
|
return {
|
|
595
651
|
action: "deny",
|
|
596
|
-
message: `approval for "${req.toolName}" could not present the args safely (unclonable value) — denied fail-closed`,
|
|
652
|
+
message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${presented.reason}) — denied fail-closed`,
|
|
597
653
|
decisionReason: "mode",
|
|
598
654
|
};
|
|
599
655
|
}
|
|
@@ -603,7 +659,7 @@ export async function resolveAsk(req, onAsk, signal) {
|
|
|
603
659
|
if (!approverView.ok) {
|
|
604
660
|
return {
|
|
605
661
|
action: "deny",
|
|
606
|
-
message: `approval for "${req.toolName}" could not present the args safely (unclonable value) — denied fail-closed`,
|
|
662
|
+
message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${approverView.reason}) — denied fail-closed`,
|
|
607
663
|
decisionReason: "mode",
|
|
608
664
|
};
|
|
609
665
|
}
|
|
@@ -612,7 +668,7 @@ export async function resolveAsk(req, onAsk, signal) {
|
|
|
612
668
|
catch (err) {
|
|
613
669
|
return {
|
|
614
670
|
action: "deny",
|
|
615
|
-
message: `approval errored for "${req.toolName}": ${
|
|
671
|
+
message: `approval errored for "${req.toolName}": ${describeThrown(err)}`,
|
|
616
672
|
decisionReason: "mode",
|
|
617
673
|
};
|
|
618
674
|
}
|
|
@@ -635,7 +691,7 @@ export async function resolveAsk(req, onAsk, signal) {
|
|
|
635
691
|
if (!edit.ok) {
|
|
636
692
|
return {
|
|
637
693
|
action: "deny",
|
|
638
|
-
message: `the approved edit for "${req.toolName}" is not safely clonable — denied fail-closed`,
|
|
694
|
+
message: `the approved edit for "${req.toolName}" is not safely clonable (${edit.reason}) — denied fail-closed`,
|
|
639
695
|
decisionReason: "mode",
|
|
640
696
|
};
|
|
641
697
|
}
|
package/dist/core/tools.js
CHANGED
|
@@ -45,6 +45,7 @@ export function defineTool(spec, options) {
|
|
|
45
45
|
executionMode,
|
|
46
46
|
...(spec.isConcurrencySafe ? { isConcurrencySafe: spec.isConcurrencySafe } : {}),
|
|
47
47
|
...(spec.effect ? { effect: spec.effect } : {}),
|
|
48
|
+
...(spec.contentOrigin ? { contentOrigin: spec.contentOrigin } : {}),
|
|
48
49
|
...(spec.prepareArguments ? { prepareArguments: spec.prepareArguments } : {}),
|
|
49
50
|
...(spec.approvalPreview ? { approvalPreview: spec.approvalPreview } : {}),
|
|
50
51
|
execute: async (toolCallId, rawParams, signal) => {
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -181,6 +181,20 @@ export type TraceEvent = {
|
|
|
181
181
|
site: string;
|
|
182
182
|
message: string;
|
|
183
183
|
ts: number;
|
|
184
|
+
} | {
|
|
185
|
+
kind: "permission.persisted_rule_allowed";
|
|
186
|
+
version: 1;
|
|
187
|
+
taskId: string;
|
|
188
|
+
toolName: string;
|
|
189
|
+
toolCallId: string;
|
|
190
|
+
rule: string;
|
|
191
|
+
ts: number;
|
|
192
|
+
} | {
|
|
193
|
+
kind: "permission.rule_store_unreadable";
|
|
194
|
+
version: 1;
|
|
195
|
+
taskId: string;
|
|
196
|
+
message: string;
|
|
197
|
+
ts: number;
|
|
184
198
|
} | {
|
|
185
199
|
kind: "brain.failover";
|
|
186
200
|
version: 1;
|
|
@@ -217,6 +231,12 @@ export type TraceEvent = {
|
|
|
217
231
|
estTokens: number;
|
|
218
232
|
floor: number;
|
|
219
233
|
ts: number;
|
|
234
|
+
} | {
|
|
235
|
+
kind: "compaction.unevaluable";
|
|
236
|
+
version: 1;
|
|
237
|
+
taskId: string;
|
|
238
|
+
estTokens: number;
|
|
239
|
+
ts: number;
|
|
220
240
|
} | {
|
|
221
241
|
kind: "compaction.blocked";
|
|
222
242
|
version: 1;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface Brain {
|
|
|
20
20
|
complete?: CompleteSimpleFn;
|
|
21
21
|
}
|
|
22
22
|
export type ToolEffect = "read" | "write" | "idempotent";
|
|
23
|
+
export type ToolContentOrigin = "external" | "execution" | "local";
|
|
23
24
|
export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
24
25
|
name: string;
|
|
25
26
|
aliases?: string[];
|
|
@@ -30,6 +31,17 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
30
31
|
name: string;
|
|
31
32
|
description: string;
|
|
32
33
|
}>;
|
|
34
|
+
agentToolFaces?: ReadonlyArray<{
|
|
35
|
+
name: string;
|
|
36
|
+
allowTools?: readonly string[];
|
|
37
|
+
denyTools?: readonly string[];
|
|
38
|
+
canRedelegate?: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
agentToolPool?: ReadonlyArray<{
|
|
41
|
+
name: string;
|
|
42
|
+
aliases?: readonly string[];
|
|
43
|
+
contentOrigin?: ToolContentOrigin;
|
|
44
|
+
}>;
|
|
33
45
|
approvalPreview?: (args: unknown) => unknown;
|
|
34
46
|
agentModels?: readonly string[];
|
|
35
47
|
withAgents?: (agents: ReadonlyArray<AgentDefinition>) => ToolSpec;
|
|
@@ -42,6 +54,7 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
42
54
|
} | Promise<{
|
|
43
55
|
reversible: boolean;
|
|
44
56
|
}>;
|
|
57
|
+
contentOrigin?: ToolContentOrigin;
|
|
45
58
|
executionMode?: "sequential" | "parallel";
|
|
46
59
|
isConcurrencySafe?: (args: unknown) => boolean;
|
|
47
60
|
parameters: TParams;
|
|
@@ -174,6 +187,7 @@ export interface HandsBandOptions {
|
|
|
174
187
|
readImageDownsampler?: ((input: Buffer, mimeType: string) => Promise<import("./mcp.js").DownsampledImage | undefined>) | false;
|
|
175
188
|
beforeWrite?: BeforeWriteHook;
|
|
176
189
|
autoBackgroundOnTimeout?: boolean;
|
|
190
|
+
readCyberReminder?: boolean;
|
|
177
191
|
}
|
|
178
192
|
export interface AgentDefinition {
|
|
179
193
|
name: string;
|
|
@@ -771,6 +785,7 @@ export interface RunnerDeps {
|
|
|
771
785
|
checkpointStore?: import("./checkpoint-store.js").CheckpointStore;
|
|
772
786
|
fileSnapshotStore?: import("./file-snapshot-store.js").FileSnapshotStore;
|
|
773
787
|
sessionPolicyStore?: import("./session-policy-store.js").SessionPolicyStore;
|
|
788
|
+
permissionRuleStore?: import("./permission-rule-store.js").PermissionRuleStoreProvider;
|
|
774
789
|
runtimeCapsResolver?: (principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>;
|
|
775
790
|
lockedConfig?: import("./locked-config.js").LockedConfig;
|
|
776
791
|
compliancePostureResolver?: (principal: string | undefined) => import("./compliance.js").CompliancePosture | undefined | Promise<import("./compliance.js").CompliancePosture | undefined>;
|
|
@@ -43,6 +43,9 @@ export interface WiringManifest {
|
|
|
43
43
|
backgroundAgentStore: boolean;
|
|
44
44
|
hostChildEventSink: boolean;
|
|
45
45
|
};
|
|
46
|
+
permissionRules: {
|
|
47
|
+
storeWired: boolean;
|
|
48
|
+
};
|
|
46
49
|
governance: {
|
|
47
50
|
audience: "operator";
|
|
48
51
|
lockedConfig: boolean;
|
|
@@ -71,13 +74,14 @@ export interface WiringFacts {
|
|
|
71
74
|
checkpointDurability?: StoreDurability;
|
|
72
75
|
sessionDurability: StoreDurability;
|
|
73
76
|
backgroundAgentStoreWired: boolean;
|
|
77
|
+
permissionRuleStoreWired: boolean;
|
|
74
78
|
hostChildEventSinkWired: boolean;
|
|
75
79
|
lockedConfigWired: boolean;
|
|
76
80
|
complianceWired: boolean;
|
|
77
81
|
memoryAdmissionWired: boolean;
|
|
78
82
|
retentionPolicyWired: boolean;
|
|
79
83
|
}
|
|
80
|
-
export type StaticWiringDeps = Pick<RunnerDeps, "onAsk" | "onQuestion" | "interactionPosture" | "onElicit" | "checkpointStore" | "sessionStore" | "backgroundAgentStore" | "onBackgroundChildEvent" | "lockedConfig" | "compliancePostureResolver" | "memoryScopeAdmission" | "retentionPolicy">;
|
|
84
|
+
export type StaticWiringDeps = Pick<RunnerDeps, "onAsk" | "onQuestion" | "interactionPosture" | "onElicit" | "checkpointStore" | "sessionStore" | "backgroundAgentStore" | "onBackgroundChildEvent" | "lockedConfig" | "compliancePostureResolver" | "memoryScopeAdmission" | "retentionPolicy" | "permissionRuleStore">;
|
|
81
85
|
export type StaticWiringSpec = Pick<TaskSpec, "onAsk" | "onQuestion" | "checkpointStore" | "durableApproval" | "mcp" | "interactiveTools" | "interactionPosture">;
|
|
82
86
|
export declare function resolveDeclaredDurability(store: {
|
|
83
87
|
readonly durability?: StoreDurability;
|
|
@@ -19,9 +19,9 @@ export function deriveAskEffective(form, parkEffective) {
|
|
|
19
19
|
case "callback":
|
|
20
20
|
return "human_reachable";
|
|
21
21
|
case "allow":
|
|
22
|
-
return "auto_allow";
|
|
22
|
+
return parkEffective === true ? "park_only" : parkEffective === "unresolved" ? "unresolved" : "auto_allow";
|
|
23
23
|
case "deny":
|
|
24
|
-
return "auto_deny";
|
|
24
|
+
return parkEffective === true ? "park_only" : parkEffective === "unresolved" ? "unresolved" : "auto_deny";
|
|
25
25
|
case "absent":
|
|
26
26
|
return parkEffective === true ? "park_only" : parkEffective === "unresolved" ? "unresolved" : "auto_deny";
|
|
27
27
|
default: {
|
|
@@ -86,6 +86,7 @@ export function deriveWiringManifest(facts) {
|
|
|
86
86
|
parkLane,
|
|
87
87
|
session: { store: manifestDurabilityOf(facts.sessionDurability) },
|
|
88
88
|
fleet: { backgroundAgentStore: facts.backgroundAgentStoreWired, hostChildEventSink: facts.hostChildEventSinkWired },
|
|
89
|
+
permissionRules: { storeWired: facts.permissionRuleStoreWired },
|
|
89
90
|
governance: {
|
|
90
91
|
audience: "operator",
|
|
91
92
|
lockedConfig: facts.lockedConfigWired,
|
|
@@ -177,6 +178,7 @@ export function describeStaticWiring(deps, spec = {}) {
|
|
|
177
178
|
...(capable ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
|
|
178
179
|
sessionDurability: resolveDeclaredDurability(deps.sessionStore, "sessionStore"),
|
|
179
180
|
backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
|
|
181
|
+
permissionRuleStoreWired: deps.permissionRuleStore !== undefined,
|
|
180
182
|
hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
|
|
181
183
|
lockedConfigWired: deps.lockedConfig !== undefined,
|
|
182
184
|
complianceWired: deps.compliancePostureResolver !== undefined,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Compile } from "typebox/compile";
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
|
+
import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
|
|
3
4
|
const validatorCache = new WeakMap();
|
|
4
5
|
const TYPEBOX_KIND = Symbol.for("TypeBox.Kind");
|
|
5
6
|
function isRecord(value) {
|
|
@@ -253,6 +254,123 @@ function formatValidationPath(error) {
|
|
|
253
254
|
const path = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
|
254
255
|
return path || "root";
|
|
255
256
|
}
|
|
257
|
+
const INSTANCE_PATH_MAX_BRANCHES = 256;
|
|
258
|
+
function resolveInstancePath(root, instancePath) {
|
|
259
|
+
if (instancePath === "")
|
|
260
|
+
return { found: true, value: root, segments: [] };
|
|
261
|
+
const body = instancePath.startsWith("/") ? instancePath.slice(1) : instancePath;
|
|
262
|
+
const found = [];
|
|
263
|
+
let budget = INSTANCE_PATH_MAX_BRANCHES;
|
|
264
|
+
const walk = (cursor, remaining, segments) => {
|
|
265
|
+
if (found.length > 1 || budget <= 0)
|
|
266
|
+
return;
|
|
267
|
+
if (cursor === null || typeof cursor !== "object")
|
|
268
|
+
return;
|
|
269
|
+
const container = cursor;
|
|
270
|
+
for (let cut = remaining.indexOf("/");; cut = remaining.indexOf("/", cut + 1)) {
|
|
271
|
+
const key = cut === -1 ? remaining : remaining.slice(0, cut);
|
|
272
|
+
if (budget <= 0)
|
|
273
|
+
return;
|
|
274
|
+
budget--;
|
|
275
|
+
if (Object.prototype.hasOwnProperty.call(container, key)) {
|
|
276
|
+
if (cut === -1)
|
|
277
|
+
found.push({ value: container[key], segments: [...segments, key] });
|
|
278
|
+
else
|
|
279
|
+
walk(container[key], remaining.slice(key.length + 1), [...segments, key]);
|
|
280
|
+
if (found.length > 1)
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (cut === -1)
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
walk(root, body, []);
|
|
288
|
+
if (found.length !== 1 || budget <= 0)
|
|
289
|
+
return { found: false };
|
|
290
|
+
return { found: true, value: found[0].value, segments: found[0].segments };
|
|
291
|
+
}
|
|
292
|
+
function accessorFrom(segments, property) {
|
|
293
|
+
const all = property === undefined ? [...segments] : [...segments, property];
|
|
294
|
+
return all.join(".");
|
|
295
|
+
}
|
|
296
|
+
function jsonTypeNameOf(value) {
|
|
297
|
+
if (value === null)
|
|
298
|
+
return "null";
|
|
299
|
+
if (value === undefined)
|
|
300
|
+
return "undefined";
|
|
301
|
+
if (Array.isArray(value))
|
|
302
|
+
return "array";
|
|
303
|
+
const t = typeof value;
|
|
304
|
+
return t === "object" ? "object" : t;
|
|
305
|
+
}
|
|
306
|
+
function synthesizeValidationIssues(errors, checkedValue) {
|
|
307
|
+
const sentences = [];
|
|
308
|
+
const rest = [];
|
|
309
|
+
for (const error of errors) {
|
|
310
|
+
const resolved = resolveInstancePath(checkedValue, error.instancePath);
|
|
311
|
+
const segments = resolved.found ? resolved.segments : undefined;
|
|
312
|
+
if (segments !== undefined && error.keyword === "required") {
|
|
313
|
+
const properties = error.params.requiredProperties ?? [];
|
|
314
|
+
for (const property of properties) {
|
|
315
|
+
sentences.push(`The required parameter \`${accessorFrom(segments, property)}\` is missing`);
|
|
316
|
+
}
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (segments !== undefined && error.keyword === "additionalProperties") {
|
|
320
|
+
const properties = error.params.additionalProperties ?? [];
|
|
321
|
+
for (const property of properties) {
|
|
322
|
+
sentences.push(`An unexpected parameter \`${accessorFrom(segments, property)}\` was provided`);
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (segments !== undefined && error.keyword === "type" && resolved.found) {
|
|
327
|
+
const expected = error.params.type;
|
|
328
|
+
const expectedText = Array.isArray(expected) ? expected.join(" | ") : (expected ?? "the declared type");
|
|
329
|
+
const provided = jsonTypeNameOf(resolved.value);
|
|
330
|
+
const accessor = accessorFrom(segments) || "root";
|
|
331
|
+
sentences.push(`The parameter \`${accessor}\` type is expected as \`${expectedText}\` but provided as \`${provided}\``);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
rest.push(` - ${formatValidationPath(error)}: ${error.message}`);
|
|
335
|
+
}
|
|
336
|
+
let restText = "";
|
|
337
|
+
if (rest.length > 0) {
|
|
338
|
+
const marker = (n) => ` … ${n} more issue${n === 1 ? "" : "s"} omitted`;
|
|
339
|
+
const kept = [];
|
|
340
|
+
let used = 0;
|
|
341
|
+
for (const line of rest) {
|
|
342
|
+
const cost = kept.length === 0 ? line.length : line.length + 1;
|
|
343
|
+
if (used + cost > VALIDATION_ERROR_ISSUES_MAX_CHARS)
|
|
344
|
+
break;
|
|
345
|
+
kept.push(line);
|
|
346
|
+
used += cost;
|
|
347
|
+
}
|
|
348
|
+
if (kept.length === rest.length) {
|
|
349
|
+
restText = kept.join("\n");
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
while (kept.length > 0 && used + marker(rest.length - kept.length).length + 1 > VALIDATION_ERROR_ISSUES_MAX_CHARS) {
|
|
353
|
+
const removed = kept.pop();
|
|
354
|
+
used -= kept.length === 0 ? removed.length : removed.length + 1;
|
|
355
|
+
}
|
|
356
|
+
if (kept.length === 0) {
|
|
357
|
+
const clip = "… (issue truncated)";
|
|
358
|
+
const others = rest.length - 1;
|
|
359
|
+
const markerCost = others > 0 ? marker(others).length + 1 : 0;
|
|
360
|
+
const room = VALIDATION_ERROR_ISSUES_MAX_CHARS - markerCost - clip.length;
|
|
361
|
+
if (room > 0) {
|
|
362
|
+
kept.push(`${sliceHeadSafe(rest[0], room)}${clip}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const omitted = rest.length - kept.length;
|
|
366
|
+
if (omitted > 0)
|
|
367
|
+
kept.push(marker(omitted));
|
|
368
|
+
restText = kept.join("\n");
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const all = [...sentences, ...(restText ? [restText] : [])];
|
|
372
|
+
return all.join("\n") || "Unknown validation error";
|
|
373
|
+
}
|
|
256
374
|
export function validateToolCall(tools, toolCall) {
|
|
257
375
|
const tool = findToolByName(tools, toolCall.name);
|
|
258
376
|
if (!tool) {
|
|
@@ -284,10 +402,7 @@ export function validateToolArguments(tool, toolCall) {
|
|
|
284
402
|
if (validator.Check(args)) {
|
|
285
403
|
return args;
|
|
286
404
|
}
|
|
287
|
-
const errors = validator
|
|
288
|
-
.Errors(args)
|
|
289
|
-
.map((error) => ` - ${formatValidationPath(error)}: ${error.message}`)
|
|
290
|
-
.join("\n") || "Unknown validation error";
|
|
405
|
+
const errors = synthesizeValidationIssues([...validator.Errors(args)], args);
|
|
291
406
|
const schemaJson = (() => {
|
|
292
407
|
try {
|
|
293
408
|
const s = JSON.stringify(tool.parameters);
|
|
@@ -297,6 +412,7 @@ export function validateToolArguments(tool, toolCall) {
|
|
|
297
412
|
return "(schema not serializable)";
|
|
298
413
|
}
|
|
299
414
|
})();
|
|
300
|
-
throw new Error(`Validation failed for tool "${toolCall.name}":\n${errors}\n\
|
|
415
|
+
throw new Error(`Validation failed for tool "${toolCall.name}":\n${errors}\n\nExpected parameter schema:\n${schemaJson}`);
|
|
301
416
|
}
|
|
302
417
|
const VALIDATION_ERROR_SCHEMA_MAX_CHARS = 4_000;
|
|
418
|
+
const VALIDATION_ERROR_ISSUES_MAX_CHARS = 2_000;
|
|
@@ -12,3 +12,5 @@ export type LoopStep = {
|
|
|
12
12
|
};
|
|
13
13
|
export type LoopTraceSink = (step: LoopStep) => void;
|
|
14
14
|
export declare function runAgentLoop(prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, emit: AgentEventSink, signal?: AbortSignal, streamFn?: StreamFn, runtime?: AgentCoreStreamRuntimeDeps, trace?: LoopTraceSink): Promise<AgentMessage[]>;
|
|
15
|
+
export declare const ROSTER_LISTING_MAX = 25;
|
|
16
|
+
export declare const ROSTER_SEARCH_HINT_NAME = "ToolSearch";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { findToolByName, validateToolArguments } from "../llm/index.js";
|
|
2
|
+
import { truncateError } from "../../core/tool-errors.js";
|
|
2
3
|
import { resolveAgentCoreStreamFn } from "./runtime-deps.js";
|
|
3
4
|
function appendTextDeltaToAssistantMessage(message, contentIndex, delta) {
|
|
4
5
|
const content = [...message.content];
|
|
@@ -831,14 +832,26 @@ function prepareToolCallArguments(tool, toolCall) {
|
|
|
831
832
|
arguments: preparedArguments,
|
|
832
833
|
};
|
|
833
834
|
}
|
|
835
|
+
export const ROSTER_LISTING_MAX = 25;
|
|
836
|
+
export const ROSTER_SEARCH_HINT_NAME = "ToolSearch";
|
|
837
|
+
function formatRosterRecovery(availableTools) {
|
|
838
|
+
if (availableTools.length === 0)
|
|
839
|
+
return "";
|
|
840
|
+
const shown = availableTools.slice(0, ROSTER_LISTING_MAX);
|
|
841
|
+
const withheld = availableTools.length - shown.length;
|
|
842
|
+
const listing = withheld > 0 ? `${shown.join(", ")} … and ${withheld} more` : shown.join(", ");
|
|
843
|
+
const hint = availableTools.includes(ROSTER_SEARCH_HINT_NAME)
|
|
844
|
+
? `. Use ${ROSTER_SEARCH_HINT_NAME} to look up a tool by name.`
|
|
845
|
+
: "";
|
|
846
|
+
return ` Available tools: ${listing}${hint}`;
|
|
847
|
+
}
|
|
834
848
|
async function prepareToolCall(currentContext, assistantMessage, toolCall, config, signal) {
|
|
835
849
|
const tool = findToolByName(currentContext.tools, toolCall.name);
|
|
836
850
|
if (!tool) {
|
|
837
851
|
const availableTools = (currentContext.tools ?? []).map((t) => t.name);
|
|
838
|
-
const available = availableTools.join(", ");
|
|
839
852
|
return {
|
|
840
853
|
kind: "immediate",
|
|
841
|
-
result: createErrorToolResult(`Tool ${toolCall.name} not found.${
|
|
854
|
+
result: createErrorToolResult(`Tool ${toolCall.name} not found.${formatRosterRecovery(availableTools)}`, { details: { code: "tool.not_found", toolName: toolCall.name, availableTools } }),
|
|
842
855
|
isError: true,
|
|
843
856
|
};
|
|
844
857
|
}
|
|
@@ -1043,7 +1056,7 @@ async function finalizeExecutedToolCall(currentContext, assistantMessage, prepar
|
|
|
1043
1056
|
}
|
|
1044
1057
|
}
|
|
1045
1058
|
catch (error) {
|
|
1046
|
-
const note = `[post-tool processing failed (the tool already executed): ${error instanceof Error ? error.message : String(error)}]
|
|
1059
|
+
const note = truncateError(`[post-tool processing failed (the tool already executed): ${error instanceof Error ? error.message : String(error)}]`);
|
|
1047
1060
|
result = { ...result, content: [...result.content, { type: "text", text: note }] };
|
|
1048
1061
|
}
|
|
1049
1062
|
if (deliveryMark !== undefined && readDeliveryFailureMark(result.details) === undefined) {
|
|
@@ -1095,7 +1108,7 @@ function createErrorToolResult(message, source) {
|
|
|
1095
1108
|
}
|
|
1096
1109
|
}
|
|
1097
1110
|
return {
|
|
1098
|
-
content: [{ type: "text", text: message }],
|
|
1111
|
+
content: [{ type: "text", text: truncateError(message) }],
|
|
1099
1112
|
details,
|
|
1100
1113
|
};
|
|
1101
1114
|
}
|