@vib-rato/agent-core 0.16.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 +852 -0
- package/README.md +493 -0
- package/dist/types/agent-loop.d.ts +229 -0
- package/dist/types/agent.d.ts +533 -0
- package/dist/types/append-only-context.d.ts +141 -0
- package/dist/types/attempt-scope.d.ts +84 -0
- package/dist/types/compaction/adaptive.d.ts +31 -0
- package/dist/types/compaction/branch-summarization.d.ts +103 -0
- package/dist/types/compaction/compaction.d.ts +330 -0
- package/dist/types/compaction/entries.d.ts +124 -0
- package/dist/types/compaction/errors.d.ts +26 -0
- package/dist/types/compaction/index.d.ts +12 -0
- package/dist/types/compaction/messages.d.ts +61 -0
- package/dist/types/compaction/openai.d.ts +65 -0
- package/dist/types/compaction/pruning.d.ts +130 -0
- package/dist/types/compaction/utils.d.ts +32 -0
- package/dist/types/compaction.d.ts +1 -0
- package/dist/types/harmony-leak.d.ts +100 -0
- package/dist/types/heap-eviction-retainers.test.d.ts +1 -0
- package/dist/types/image-placeholder-guard.d.ts +4 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/proxy.d.ts +95 -0
- package/dist/types/run-collector.d.ts +223 -0
- package/dist/types/run-resource-ledger.d.ts +2 -0
- package/dist/types/telemetry.d.ts +605 -0
- package/dist/types/thinking.d.ts +18 -0
- package/dist/types/tool-dispatch-identity.d.ts +27 -0
- package/dist/types/types.d.ts +790 -0
- package/package.json +72 -0
- package/src/agent-loop.ts +5632 -0
- package/src/agent.ts +2437 -0
- package/src/append-only-context.ts +496 -0
- package/src/attempt-scope.ts +195 -0
- package/src/compaction/adaptive.ts +92 -0
- package/src/compaction/branch-summarization.ts +358 -0
- package/src/compaction/compaction.ts +1569 -0
- package/src/compaction/entries.ts +158 -0
- package/src/compaction/errors.ts +31 -0
- package/src/compaction/index.ts +13 -0
- package/src/compaction/messages.ts +212 -0
- package/src/compaction/openai.ts +580 -0
- package/src/compaction/prompts/auto-handoff-threshold-focus.md +1 -0
- package/src/compaction/prompts/branch-summary-context.md +5 -0
- package/src/compaction/prompts/branch-summary-preamble.md +2 -0
- package/src/compaction/prompts/branch-summary.md +30 -0
- package/src/compaction/prompts/compaction-short-summary.md +9 -0
- package/src/compaction/prompts/compaction-summary-context.md +5 -0
- package/src/compaction/prompts/compaction-summary.md +38 -0
- package/src/compaction/prompts/compaction-turn-prefix.md +17 -0
- package/src/compaction/prompts/compaction-update-summary.md +45 -0
- package/src/compaction/prompts/file-operations.md +10 -0
- package/src/compaction/prompts/handoff-document.md +56 -0
- package/src/compaction/prompts/summarization-system.md +3 -0
- package/src/compaction/pruning.ts +1026 -0
- package/src/compaction/utils.ts +189 -0
- package/src/compaction.ts +1 -0
- package/src/harmony-leak.ts +457 -0
- package/src/heap-eviction-retainers.test.ts +293 -0
- package/src/image-placeholder-guard.ts +20 -0
- package/src/index.ts +23 -0
- package/src/prompts/escaped-nonascii-recovery.md +3 -0
- package/src/prompts/repeated-tool-failure-recovery.md +1 -0
- package/src/proxy.ts +408 -0
- package/src/run-collector.ts +728 -0
- package/src/run-resource-ledger.ts +345 -0
- package/src/telemetry.ts +2161 -0
- package/src/thinking.ts +20 -0
- package/src/tool-dispatch-identity.ts +87 -0
- package/src/types.ts +882 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { AssistantMessage, Message, ToolResultMessage } from "@vib-rato/ai";
|
|
3
|
+
import { getBundledModel } from "@vib-rato/ai";
|
|
4
|
+
import { AssistantMessageEventStream } from "@vib-rato/ai/utils/event-stream";
|
|
5
|
+
import { Agent } from "./agent";
|
|
6
|
+
import { agentLoop } from "./agent-loop";
|
|
7
|
+
import { AppendOnlyContextManager } from "./append-only-context";
|
|
8
|
+
import type { SessionEntry, SessionMessageEntry } from "./compaction/entries";
|
|
9
|
+
import {
|
|
10
|
+
commitToolOutputPrune,
|
|
11
|
+
type PruneConfig,
|
|
12
|
+
planToolOutputPrune,
|
|
13
|
+
type ToolOutputPrunePlan,
|
|
14
|
+
} from "./compaction/pruning";
|
|
15
|
+
import type { AgentMessage, AgentTool, ContextMaintenanceResult } from "./types";
|
|
16
|
+
|
|
17
|
+
const PRUNE_CONFIG: PruneConfig = {
|
|
18
|
+
protectTokens: 0,
|
|
19
|
+
minimumSavings: 0,
|
|
20
|
+
protectedTools: [],
|
|
21
|
+
protectRecentTurns: 0,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function toolResult(text: string, toolCallId = "call-1"): ToolResultMessage {
|
|
25
|
+
return {
|
|
26
|
+
role: "toolResult",
|
|
27
|
+
toolCallId,
|
|
28
|
+
toolName: "bash",
|
|
29
|
+
content: [{ type: "text", text }],
|
|
30
|
+
isError: false,
|
|
31
|
+
timestamp: Date.now(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function sessionEntry(message: AgentMessage, id: string): SessionMessageEntry {
|
|
36
|
+
return {
|
|
37
|
+
type: "message",
|
|
38
|
+
id,
|
|
39
|
+
parentId: null,
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
message,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function jsonBytes(value: unknown): string {
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function containsText(value: unknown, needle: string, seen = new WeakSet<object>()): boolean {
|
|
50
|
+
if (typeof value === "string") return value.includes(needle);
|
|
51
|
+
if (value === null || typeof value !== "object") return false;
|
|
52
|
+
if (seen.has(value)) return false;
|
|
53
|
+
seen.add(value);
|
|
54
|
+
for (const child of Object.values(value)) {
|
|
55
|
+
if (containsText(child, needle, seen)) return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function forceGc(): void {
|
|
61
|
+
if (typeof Bun.gc === "function") Bun.gc(true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function assistantMessage(
|
|
65
|
+
content: AssistantMessage["content"],
|
|
66
|
+
stopReason: AssistantMessage["stopReason"],
|
|
67
|
+
): AssistantMessage {
|
|
68
|
+
return {
|
|
69
|
+
role: "assistant",
|
|
70
|
+
content,
|
|
71
|
+
api: "google-generative-ai",
|
|
72
|
+
provider: "google",
|
|
73
|
+
model: "gemini-2.5-flash-lite-preview-06-17",
|
|
74
|
+
usage: {
|
|
75
|
+
input: 1,
|
|
76
|
+
output: 1,
|
|
77
|
+
cacheRead: 0,
|
|
78
|
+
cacheWrite: 0,
|
|
79
|
+
totalTokens: 2,
|
|
80
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
81
|
+
},
|
|
82
|
+
stopReason,
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function streamDone(message: AssistantMessage): AssistantMessageEventStream {
|
|
88
|
+
const stream = new AssistantMessageEventStream();
|
|
89
|
+
queueMicrotask(() =>
|
|
90
|
+
stream.push({
|
|
91
|
+
type: "done",
|
|
92
|
+
reason: message.stopReason === "length" ? "length" : message.stopReason === "toolUse" ? "toolUse" : "stop",
|
|
93
|
+
message,
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
return stream;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
describe("W4 heap eviction acceptance: Agent retainers and rewrite boundaries", () => {
|
|
100
|
+
test("historyRewrite releases Agent, append-only, loop, conversion, and prune retainers", () => {
|
|
101
|
+
const marker = `w4-heap-marker-${crypto.randomUUID()}-${"x".repeat(8_192)}`;
|
|
102
|
+
let markerHolder: { marker: string } | undefined = { marker };
|
|
103
|
+
const markerHolderRef = new WeakRef(markerHolder!);
|
|
104
|
+
let original = toolResult(marker);
|
|
105
|
+
// The holder is intentionally non-enumerable: it models a diagnostic/closure
|
|
106
|
+
// retainer that JSON-based digest plans must not preserve.
|
|
107
|
+
Object.defineProperty(original, "__w4MarkerHolder", { value: markerHolder, configurable: true });
|
|
108
|
+
|
|
109
|
+
const appendOnly = new AppendOnlyContextManager();
|
|
110
|
+
const agent = new Agent({
|
|
111
|
+
initialState: { messages: [original] },
|
|
112
|
+
appendOnlyContext: appendOnly,
|
|
113
|
+
});
|
|
114
|
+
const providerMessage = structuredClone(original) as Message;
|
|
115
|
+
appendOnly.syncMessages([providerMessage]);
|
|
116
|
+
const currentContext = { systemPrompt: [], messages: appendOnly.log.toMessages(), tools: [] };
|
|
117
|
+
appendOnly.build(currentContext, { intentTracing: false });
|
|
118
|
+
const convertedContextCache: Message[] = [structuredClone(providerMessage) as Message];
|
|
119
|
+
const newMessages: AgentMessage[] = [original];
|
|
120
|
+
|
|
121
|
+
const planEntries = [sessionEntry(structuredClone(original) as AgentMessage, "marker-entry")];
|
|
122
|
+
const plan = planToolOutputPrune(planEntries, PRUNE_CONFIG);
|
|
123
|
+
expect(plan.digests).toHaveLength(1);
|
|
124
|
+
expect(plan.digests[0]).toMatchObject({ entryId: "marker-entry" });
|
|
125
|
+
expect((plan as unknown as Record<string, unknown>).originalText).toBeUndefined();
|
|
126
|
+
expect(JSON.stringify(plan)).not.toContain("originalText");
|
|
127
|
+
expect(JSON.stringify(plan)).not.toContain(marker);
|
|
128
|
+
|
|
129
|
+
// Commit uses the digest-only plan against the original entry, then the
|
|
130
|
+
// owning Agent performs the sole history rewrite boundary.
|
|
131
|
+
const commitEntries = [sessionEntry(structuredClone(original) as AgentMessage, "marker-entry")];
|
|
132
|
+
const commit = commitToolOutputPrune(commitEntries, plan);
|
|
133
|
+
expect(commit).toEqual([{ entryId: "marker-entry", outcome: "committed" }]);
|
|
134
|
+
expect(JSON.stringify(commit)).not.toContain("originalText");
|
|
135
|
+
agent.replaceMessages([], { historyRewrite: { reason: "w4-eviction" } });
|
|
136
|
+
currentContext.messages.length = 0;
|
|
137
|
+
newMessages.length = 0;
|
|
138
|
+
convertedContextCache.length = 0;
|
|
139
|
+
original = undefined as unknown as ToolResultMessage;
|
|
140
|
+
markerHolder = undefined;
|
|
141
|
+
|
|
142
|
+
const retainers = [
|
|
143
|
+
agent.state,
|
|
144
|
+
appendOnly.log.toMessages(),
|
|
145
|
+
currentContext,
|
|
146
|
+
newMessages,
|
|
147
|
+
convertedContextCache,
|
|
148
|
+
plan,
|
|
149
|
+
commit,
|
|
150
|
+
];
|
|
151
|
+
expect(retainers.some(value => containsText(value, marker))).toBe(false);
|
|
152
|
+
expect(agent.state.messages).toEqual([]);
|
|
153
|
+
expect(appendOnly.log.length).toBe(0);
|
|
154
|
+
expect(containsText(appendOnly.log.toMessages(), marker)).toBe(false);
|
|
155
|
+
|
|
156
|
+
forceGc();
|
|
157
|
+
expect(markerHolderRef.deref()).toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("provider-normalized bytes remain append-only until replaceMessages crosses historyRewrite", () => {
|
|
161
|
+
const marker = `provider-stable-${crypto.randomUUID()}`;
|
|
162
|
+
const source = toolResult(marker);
|
|
163
|
+
const appendOnly = new AppendOnlyContextManager();
|
|
164
|
+
const agent = new Agent({ initialState: { messages: [source] }, appendOnlyContext: appendOnly });
|
|
165
|
+
const normalized = structuredClone(source) as Message;
|
|
166
|
+
appendOnly.syncMessages([normalized]);
|
|
167
|
+
const before = jsonBytes(appendOnly.log.toMessages());
|
|
168
|
+
|
|
169
|
+
// Mutating Agent-owned history in place must not mutate the already-normalized
|
|
170
|
+
// provider snapshot. A converter normally owns this clone boundary.
|
|
171
|
+
source.content = [{ type: "text", text: `${marker}-mutated` }];
|
|
172
|
+
agent.touchContext();
|
|
173
|
+
expect(jsonBytes(appendOnly.log.toMessages())).toBe(before);
|
|
174
|
+
|
|
175
|
+
appendOnly.syncMessages([normalized, { role: "user", content: "next", timestamp: Date.now() }]);
|
|
176
|
+
expect(jsonBytes(appendOnly.log.toMessages()).startsWith(before.slice(0, -1))).toBe(true);
|
|
177
|
+
|
|
178
|
+
agent.replaceMessages([], { historyRewrite: { reason: "provider-rewrite" } });
|
|
179
|
+
expect(appendOnly.log.length).toBe(0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("append-only log clones nested provider messages at sync and rebase boundaries", () => {
|
|
183
|
+
const message = {
|
|
184
|
+
role: "user",
|
|
185
|
+
content: [{ type: "text", text: "nested-source" }],
|
|
186
|
+
metadata: { nested: { enabled: true } },
|
|
187
|
+
} as unknown as Message;
|
|
188
|
+
const manager = new AppendOnlyContextManager();
|
|
189
|
+
manager.syncMessages([message]);
|
|
190
|
+
message.content = [{ type: "text", text: "mutated-source" }];
|
|
191
|
+
(message as unknown as { metadata: { nested: { enabled: boolean } } }).metadata.nested.enabled = false;
|
|
192
|
+
expect(manager.log.toMessages()[0]).toMatchObject({
|
|
193
|
+
content: [{ type: "text", text: "nested-source" }],
|
|
194
|
+
metadata: { nested: { enabled: true } },
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
manager.seedNormalizedMessages([message], { reset: true });
|
|
198
|
+
message.content = [{ type: "text", text: "mutated-after-rebase" }];
|
|
199
|
+
expect(manager.log.toMessages()[0]).toMatchObject({ content: [{ type: "text", text: "mutated-source" }] });
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("seeded fork prefixes survive a child history rewrite", () => {
|
|
203
|
+
const prefix: Message[] = [{ role: "user", content: "seeded-prefix", timestamp: Date.now() }];
|
|
204
|
+
const manager = AppendOnlyContextManager.forkFromSeed({
|
|
205
|
+
messages: prefix,
|
|
206
|
+
options: { intentTracing: false },
|
|
207
|
+
});
|
|
208
|
+
const agent = new Agent({
|
|
209
|
+
initialState: { messages: prefix as AgentMessage[] },
|
|
210
|
+
appendOnlyContext: manager,
|
|
211
|
+
});
|
|
212
|
+
const prefixBytes = jsonBytes(manager.log.toMessages()[0]);
|
|
213
|
+
|
|
214
|
+
agent.replaceMessages(
|
|
215
|
+
[prefix[0] as AgentMessage, { role: "user", content: "child-before-rewrite", timestamp: Date.now() }],
|
|
216
|
+
{ historyRewrite: { reason: "child-rewrite", preserveSeededPrefix: true } },
|
|
217
|
+
);
|
|
218
|
+
expect(jsonBytes(manager.log.toMessages()[0])).toBe(prefixBytes);
|
|
219
|
+
expect(manager.log.length).toBe(1);
|
|
220
|
+
|
|
221
|
+
manager.syncMessages([prefix[0], { role: "user", content: "child-after-rewrite", timestamp: Date.now() }]);
|
|
222
|
+
expect(jsonBytes(manager.log.toMessages()[0])).toBe(prefixBytes);
|
|
223
|
+
expect(manager.log.toMessages().at(-1)).toMatchObject({ content: "child-after-rewrite" });
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("digest mismatch aborts only the tampered entry while another commits", () => {
|
|
227
|
+
const first = sessionEntry(toolResult("first-output-".repeat(2_000), "call-first"), "first");
|
|
228
|
+
const second = sessionEntry(toolResult("second-output-".repeat(2_000), "call-second"), "second");
|
|
229
|
+
const planEntries = structuredClone([first, second]) as SessionEntry[];
|
|
230
|
+
const plan: ToolOutputPrunePlan = planToolOutputPrune(planEntries, PRUNE_CONFIG);
|
|
231
|
+
expect(plan.digests.map(digest => digest.entryId)).toEqual(["second", "first"]);
|
|
232
|
+
expect(plan.digests.every(digest => Object.keys(digest).sort().join(",") === "bytes,entryId,sha256")).toBe(true);
|
|
233
|
+
|
|
234
|
+
const commitEntries = structuredClone([first, second]) as SessionEntry[];
|
|
235
|
+
const tampered = commitEntries[0];
|
|
236
|
+
if (tampered.type === "message" && tampered.message.role === "toolResult") {
|
|
237
|
+
tampered.message.content = [{ type: "text", text: "tampered" }];
|
|
238
|
+
}
|
|
239
|
+
const outcomes = commitToolOutputPrune(commitEntries, plan);
|
|
240
|
+
expect(outcomes.find(outcome => outcome.entryId === "first")).toMatchObject({ outcome: "mismatch" });
|
|
241
|
+
expect(outcomes.find(outcome => outcome.entryId === "second")).toEqual({
|
|
242
|
+
entryId: "second",
|
|
243
|
+
outcome: "committed",
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("ContextMaintenanceResult releaseCurrentContext clears loop context and newMessages", async () => {
|
|
248
|
+
let maintenanceCalls = 0;
|
|
249
|
+
const events: Array<{ type: string; messages?: AgentMessage[]; stopReason?: string }> = [];
|
|
250
|
+
const tool: AgentTool<any> = {
|
|
251
|
+
name: "w4_probe",
|
|
252
|
+
label: "W4 probe",
|
|
253
|
+
description: "W4 maintenance probe",
|
|
254
|
+
parameters: { type: "object", properties: {}, additionalProperties: false } as any,
|
|
255
|
+
execute: async () => ({ content: [{ type: "text", text: "probe-result" }] }),
|
|
256
|
+
};
|
|
257
|
+
let calls = 0;
|
|
258
|
+
const streamFn = () => {
|
|
259
|
+
const message =
|
|
260
|
+
calls++ === 0
|
|
261
|
+
? assistantMessage([{ type: "toolCall", id: "w4-call", name: "w4_probe", arguments: {} }], "toolUse")
|
|
262
|
+
: assistantMessage([{ type: "text", text: "unexpected second provider call" }], "stop");
|
|
263
|
+
return streamDone(message);
|
|
264
|
+
};
|
|
265
|
+
const stream = agentLoop(
|
|
266
|
+
[],
|
|
267
|
+
{ systemPrompt: [], messages: [], tools: [tool] },
|
|
268
|
+
{
|
|
269
|
+
model: getBundledModel("google", "gemini-2.5-flash-lite-preview-06-17"),
|
|
270
|
+
maintainContext: (): ContextMaintenanceResult => {
|
|
271
|
+
maintenanceCalls++;
|
|
272
|
+
return { outcome: "pruned", releaseCurrentContext: true };
|
|
273
|
+
},
|
|
274
|
+
convertToLlm: messages =>
|
|
275
|
+
messages.filter(
|
|
276
|
+
(message): message is Message =>
|
|
277
|
+
message.role === "user" || message.role === "assistant" || message.role === "toolResult",
|
|
278
|
+
),
|
|
279
|
+
},
|
|
280
|
+
undefined,
|
|
281
|
+
streamFn,
|
|
282
|
+
false,
|
|
283
|
+
);
|
|
284
|
+
for await (const event of stream) {
|
|
285
|
+
if (event.type === "agent_end") events.push(event);
|
|
286
|
+
}
|
|
287
|
+
const result = await stream.result();
|
|
288
|
+
expect(maintenanceCalls).toBe(1);
|
|
289
|
+
expect(calls).toBe(1);
|
|
290
|
+
expect(result).toEqual([]);
|
|
291
|
+
expect(events.at(-1)).toMatchObject({ stopReason: "maintenance", messages: [] });
|
|
292
|
+
});
|
|
293
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ImageContent, TextContent } from "@vib-rato/ai";
|
|
2
|
+
|
|
3
|
+
export const IMAGE_PLACEHOLDER_ATTACHMENT_GUIDANCE =
|
|
4
|
+
"Image placeholder text was submitted without an image payload. Paste the image with #paste-image, attach it with @path/to/image.png, or save the image and provide the saved file path.";
|
|
5
|
+
|
|
6
|
+
const IMAGE_PLACEHOLDER_ONLY_PATTERN = /^\s*(?:\[image\s+\d+\]\s*)+$/i;
|
|
7
|
+
|
|
8
|
+
export function isImagePlaceholderOnlyText(text: string): boolean {
|
|
9
|
+
return IMAGE_PLACEHOLDER_ONLY_PATTERN.test(text);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function assertImagePlaceholdersHavePayload(
|
|
13
|
+
text: string,
|
|
14
|
+
content: readonly (TextContent | ImageContent)[] | undefined,
|
|
15
|
+
): void {
|
|
16
|
+
if (!isImagePlaceholderOnlyText(text)) return;
|
|
17
|
+
const hasImagePayload = content?.some(part => part.type === "image") ?? false;
|
|
18
|
+
if (hasImagePayload) return;
|
|
19
|
+
throw new Error(IMAGE_PLACEHOLDER_ATTACHMENT_GUIDANCE);
|
|
20
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Core Agent
|
|
2
|
+
export * from "./agent";
|
|
3
|
+
// Loop functions
|
|
4
|
+
export * from "./agent-loop";
|
|
5
|
+
// Append-only context mode
|
|
6
|
+
export * from "./append-only-context";
|
|
7
|
+
// Compaction
|
|
8
|
+
export * from "./compaction";
|
|
9
|
+
export * from "./harmony-leak";
|
|
10
|
+
export * from "./image-placeholder-guard";
|
|
11
|
+
// Proxy utilities
|
|
12
|
+
export * from "./proxy";
|
|
13
|
+
// Run-level telemetry collector + aggregators
|
|
14
|
+
export * from "./run-collector";
|
|
15
|
+
export * from "./run-resource-ledger";
|
|
16
|
+
// Telemetry
|
|
17
|
+
export * from "./telemetry";
|
|
18
|
+
// Thinking selectors
|
|
19
|
+
export * from "./thinking";
|
|
20
|
+
// Dispatch-bound tool identity (non-serializable side channel keyed by event object)
|
|
21
|
+
export * from "./tool-dispatch-identity";
|
|
22
|
+
// Types
|
|
23
|
+
export * from "./types";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Your previous response was discarded before execution: its tool-call arguments spelled printable text as `\uXXXX` escape sequences instead of literal UTF-8 characters. Escaped text cannot be verified — a single mistyped hex digit silently becomes a different, equally valid character, including an ASCII character — so such calls are never executed.
|
|
2
|
+
|
|
3
|
+
Re-issue the same tool call now, writing every printable character literally (for example 한글, 日本語, émoji, and ordinary ASCII — never `\uXXXX`). Do not change the intent or content of the call; only the spelling of the text.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The immediately preceding tool calls failed because their arguments were malformed. Do not call any tools. Answer the original user request now using the conversation and any successful tool results already available. If the evidence is incomplete, state that limitation instead of returning an empty response.
|