@vendoai/agent 0.4.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/LICENSE +202 -0
- package/README.md +26 -0
- package/dist/agent.d.ts +62 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +443 -0
- package/dist/agent.js.map +1 -0
- package/dist/capability-miss.d.ts +24 -0
- package/dist/capability-miss.d.ts.map +1 -0
- package/dist/capability-miss.js +133 -0
- package/dist/capability-miss.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/pack.d.ts +37 -0
- package/dist/pack.d.ts.map +1 -0
- package/dist/pack.js +225 -0
- package/dist/pack.js.map +1 -0
- package/dist/prompt.d.ts +8 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +43 -0
- package/dist/prompt.js.map +1 -0
- package/dist/runner.d.ts +20 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +114 -0
- package/dist/runner.js.map +1 -0
- package/dist/test-helpers.d.ts +55 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +230 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/threads.d.ts +48 -0
- package/dist/threads.d.ts.map +1 -0
- package/dist/threads.js +251 -0
- package/dist/threads.js.map +1 -0
- package/dist/tool-pack.d.ts +41 -0
- package/dist/tool-pack.d.ts.map +1 -0
- package/dist/tool-pack.js +17 -0
- package/dist/tool-pack.js.map +1 -0
- package/dist/tool-search.d.ts +72 -0
- package/dist/tool-search.d.ts.map +1 -0
- package/dist/tool-search.js +135 -0
- package/dist/tool-search.js.map +1 -0
- package/dist/tools.d.ts +22 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +151 -0
- package/dist/tools.js.map +1 -0
- package/package.json +63 -0
package/dist/agent.js
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { VendoError, toVendoWirePart, } from "@vendoai/core";
|
|
2
|
+
import { memoryStoreAdapter } from "@vendoai/core/conformance";
|
|
3
|
+
import { convertToModelMessages, createUIMessageStream, createUIMessageStreamResponse, isToolUIPart, stepCountIs, streamText, } from "ai";
|
|
4
|
+
import { assembleSystemPrompt } from "./prompt.js";
|
|
5
|
+
import { createRunner } from "./runner.js";
|
|
6
|
+
import { ThreadRepository } from "./threads.js";
|
|
7
|
+
import { addAgentTool, buildAgentTools } from "./tools.js";
|
|
8
|
+
import { createCapabilityMissDetector, latestUserIntent, } from "./capability-miss.js";
|
|
9
|
+
import { createToolSearchSession } from "./tool-search.js";
|
|
10
|
+
const THREAD_ID_HEADER = "x-vendo-thread-id";
|
|
11
|
+
// AGENT-7: the default agent-loop step cap (unchanged from the previously
|
|
12
|
+
// hardcoded value); hosts raise or lower it via context.maxSteps.
|
|
13
|
+
const DEFAULT_MAX_STEPS = 20;
|
|
14
|
+
// ENG-309: backoff between persist attempts after a completed stream. Short and
|
|
15
|
+
// bounded — long waits would hold the response open for nothing (the user
|
|
16
|
+
// already has the reply); a store blip that outlives ~600ms is a real outage.
|
|
17
|
+
const PERSIST_RETRY_DELAYS_MS = [100, 500];
|
|
18
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
19
|
+
/** ENG-309: persist the finished turn with bounded retry, and surface a final
|
|
20
|
+
* failure LOUDLY instead of swallowing it. By the time onFinish runs, the
|
|
21
|
+
* response headers and the SSE `[DONE]` are already on the wire, so no
|
|
22
|
+
* additive wire signal can reach this turn's client — never throw here (that
|
|
23
|
+
* would corrupt the already-delivered stream / crash the transport), but a
|
|
24
|
+
* thread silently vanishing after a successful reply is data loss, so the
|
|
25
|
+
* structured error names the thread. */
|
|
26
|
+
async function persistFinishedTurn(threads, thread, messages, ctx) {
|
|
27
|
+
for (let attempt = 0;; attempt += 1) {
|
|
28
|
+
try {
|
|
29
|
+
await threads.persist(thread, messages);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const delay = PERSIST_RETRY_DELAYS_MS[attempt];
|
|
34
|
+
if (delay === undefined) {
|
|
35
|
+
console.error("[vendo] agent: thread persist failed after completed stream — this turn was NOT saved", {
|
|
36
|
+
threadId: thread.id,
|
|
37
|
+
subject: ctx.principal.subject,
|
|
38
|
+
attempts: attempt + 1,
|
|
39
|
+
error: error instanceof Error ? error.message : String(error),
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await wait(delay);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Anthropic prompt-caching breakpoint. providerOptions.anthropic is ignored by every
|
|
48
|
+
// other provider (and by the test mocks), so marking breakpoints degrades to a no-op.
|
|
49
|
+
const CACHE_BREAKPOINT = { anthropic: { cacheControl: { type: "ephemeral" } } };
|
|
50
|
+
function validateConfig(config) {
|
|
51
|
+
const { maxOutputTokens, toolOutputCap, historyWindow } = config.context ?? {};
|
|
52
|
+
if (maxOutputTokens !== undefined && (!Number.isInteger(maxOutputTokens) || maxOutputTokens < 1)) {
|
|
53
|
+
throw new VendoError("validation", "maxOutputTokens must be a positive integer");
|
|
54
|
+
}
|
|
55
|
+
if (toolOutputCap !== undefined && (!Number.isInteger(toolOutputCap) || toolOutputCap < 0)) {
|
|
56
|
+
throw new VendoError("validation", "toolOutputCap must be a non-negative integer");
|
|
57
|
+
}
|
|
58
|
+
if (historyWindow !== undefined && (!Number.isInteger(historyWindow) || historyWindow < 1)) {
|
|
59
|
+
throw new VendoError("validation", "historyWindow must be a positive integer");
|
|
60
|
+
}
|
|
61
|
+
const { maxSteps } = config.context ?? {};
|
|
62
|
+
if (maxSteps !== undefined && (!Number.isInteger(maxSteps) || maxSteps < 1)) {
|
|
63
|
+
throw new VendoError("validation", "maxSteps must be a positive integer");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// System-role messages are rejected: the system prompt is assembled server-side
|
|
67
|
+
// (03 §3); accepting one from the client would be a prompt-injection channel.
|
|
68
|
+
function validateMessage(message) {
|
|
69
|
+
if (!message
|
|
70
|
+
|| typeof message.id !== "string"
|
|
71
|
+
|| message.id.length === 0
|
|
72
|
+
|| !["user", "assistant"].includes(message.role)
|
|
73
|
+
|| !Array.isArray(message.parts)) {
|
|
74
|
+
throw new VendoError("validation", "stream requires a valid message");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function upsertMessage(messages, message) {
|
|
78
|
+
const index = messages.findIndex((candidate) => candidate.id === message.id);
|
|
79
|
+
if (index === -1)
|
|
80
|
+
messages.push(message);
|
|
81
|
+
else
|
|
82
|
+
messages[index] = message;
|
|
83
|
+
}
|
|
84
|
+
/** Structural JSON equality, key-order independent (both sides are
|
|
85
|
+
* wire-serializable UIMessage parts). */
|
|
86
|
+
function jsonEqual(left, right) {
|
|
87
|
+
if (left === right)
|
|
88
|
+
return true;
|
|
89
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
90
|
+
return Array.isArray(left) && Array.isArray(right)
|
|
91
|
+
&& left.length === right.length
|
|
92
|
+
&& left.every((item, index) => jsonEqual(item, right[index]));
|
|
93
|
+
}
|
|
94
|
+
if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const leftRecord = left;
|
|
98
|
+
const rightRecord = right;
|
|
99
|
+
const keys = Object.keys(leftRecord);
|
|
100
|
+
return keys.length === Object.keys(rightRecord).length
|
|
101
|
+
&& keys.every((key) => jsonEqual(leftRecord[key], rightRecord[key]));
|
|
102
|
+
}
|
|
103
|
+
/** AGENT-12: is `incoming` the one client-writable change to a stored part —
|
|
104
|
+
* answering a pending approval? The verdict payload is exactly
|
|
105
|
+
* `{ id (unchanged), approved, reason? }` and EVERY other field of the part
|
|
106
|
+
* must stay byte-identical — no fabricated output or altered props may ride
|
|
107
|
+
* along on the flip. */
|
|
108
|
+
function isApprovalResponse(stored, incoming) {
|
|
109
|
+
const before = stored;
|
|
110
|
+
const after = incoming;
|
|
111
|
+
if (before.state !== "approval-requested" || after.state !== "approval-responded")
|
|
112
|
+
return false;
|
|
113
|
+
const beforeApproval = before.approval;
|
|
114
|
+
const afterApproval = after.approval;
|
|
115
|
+
if (beforeApproval === undefined || afterApproval === undefined)
|
|
116
|
+
return false;
|
|
117
|
+
if (afterApproval.id !== beforeApproval.id
|
|
118
|
+
|| typeof afterApproval.approved !== "boolean"
|
|
119
|
+
|| (afterApproval.reason !== undefined && typeof afterApproval.reason !== "string")
|
|
120
|
+
|| Object.keys(afterApproval).some((key) => !["id", "approved", "reason"].includes(key))) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
// Reverting the flip must reproduce the stored part exactly.
|
|
124
|
+
return jsonEqual({ ...after, state: before.state, approval: before.approval }, before);
|
|
125
|
+
}
|
|
126
|
+
/** AGENT-12: clients may add fresh USER messages and answer approvals — they
|
|
127
|
+
* may not author assistant content or rewrite history by replaying a known
|
|
128
|
+
* message id with different parts. */
|
|
129
|
+
function validateUpsert(messages, message) {
|
|
130
|
+
const existing = messages.find((candidate) => candidate.id === message.id);
|
|
131
|
+
if (existing === undefined) {
|
|
132
|
+
if (message.role !== "user") {
|
|
133
|
+
throw new VendoError("validation", "assistant messages are server-authored; a new message must be role user");
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (existing.role !== message.role) {
|
|
138
|
+
throw new VendoError("validation", "a message upsert cannot change the message role");
|
|
139
|
+
}
|
|
140
|
+
// Serialize both sides so explicit-undefined props (which JSON drops on the
|
|
141
|
+
// wire anyway) never make an identical part read as different.
|
|
142
|
+
const stored = JSON.parse(JSON.stringify(existing.parts));
|
|
143
|
+
const incoming = JSON.parse(JSON.stringify(message.parts));
|
|
144
|
+
if (message.role === "user") {
|
|
145
|
+
if (!jsonEqual(stored, incoming)) {
|
|
146
|
+
throw new VendoError("validation", "an existing user message cannot be rewritten");
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (stored.length !== incoming.length
|
|
151
|
+
|| !stored.every((part, index) => jsonEqual(part, incoming[index]) || isApprovalResponse(part, incoming[index]))) {
|
|
152
|
+
throw new VendoError("validation", "an assistant message upsert may only answer pending approvals");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function abandonPendingApprovals(messages) {
|
|
156
|
+
const abandonedToolCallIds = [];
|
|
157
|
+
for (const message of messages) {
|
|
158
|
+
message.parts = message.parts.map((part) => {
|
|
159
|
+
if (!isToolUIPart(part))
|
|
160
|
+
return part;
|
|
161
|
+
// Parts flipped on an EARLIER turn re-collect too: guard-side resolution
|
|
162
|
+
// is best-effort per turn, so a failed abandonApprovals call retries on
|
|
163
|
+
// the next fresh turn (the guard method is idempotent — an
|
|
164
|
+
// already-denied id is a no-op there).
|
|
165
|
+
if (part.state === "approval-responded"
|
|
166
|
+
&& part.approval?.approved === false
|
|
167
|
+
&& part.approval.reason === "abandoned") {
|
|
168
|
+
abandonedToolCallIds.push(part.toolCallId);
|
|
169
|
+
return part;
|
|
170
|
+
}
|
|
171
|
+
if (part.state !== "approval-requested")
|
|
172
|
+
return part;
|
|
173
|
+
abandonedToolCallIds.push(part.toolCallId);
|
|
174
|
+
return {
|
|
175
|
+
...part,
|
|
176
|
+
state: "approval-responded",
|
|
177
|
+
approval: {
|
|
178
|
+
id: part.approval.id,
|
|
179
|
+
approved: false,
|
|
180
|
+
reason: "abandoned",
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return abandonedToolCallIds;
|
|
186
|
+
}
|
|
187
|
+
/** AGENT-6: the guard's approval ids for abandoned tool calls. The native tool
|
|
188
|
+
* part's `approval.id` is the ai-SDK's own handle; the GUARD's approvalId
|
|
189
|
+
* rides the data-vendo-approval part beside it, keyed by toolCallId — read it
|
|
190
|
+
* from either the persisted nested envelope or the flat §16 shape. */
|
|
191
|
+
function guardApprovalIds(messages, toolCallIds) {
|
|
192
|
+
if (toolCallIds.length === 0)
|
|
193
|
+
return [];
|
|
194
|
+
const wanted = new Set(toolCallIds);
|
|
195
|
+
const ids = [];
|
|
196
|
+
for (const message of messages) {
|
|
197
|
+
for (const part of message.parts) {
|
|
198
|
+
if (part.type !== "data-vendo-approval")
|
|
199
|
+
continue;
|
|
200
|
+
const payload = ("data" in part ? part.data : part);
|
|
201
|
+
if (typeof payload.toolCallId === "string" && wanted.has(payload.toolCallId)
|
|
202
|
+
&& typeof payload.approvalId === "string") {
|
|
203
|
+
ids.push(payload.approvalId);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return ids;
|
|
208
|
+
}
|
|
209
|
+
function providerHistory(messages) {
|
|
210
|
+
return messages.map((message) => ({
|
|
211
|
+
...message,
|
|
212
|
+
parts: message.parts.map((part) => {
|
|
213
|
+
if (!isToolUIPart(part)
|
|
214
|
+
|| part.state !== "approval-responded"
|
|
215
|
+
|| part.approval.approved !== false
|
|
216
|
+
|| part.approval.reason !== "abandoned") {
|
|
217
|
+
return part;
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
...part,
|
|
221
|
+
state: "output-denied",
|
|
222
|
+
approval: { ...part.approval, approved: false },
|
|
223
|
+
};
|
|
224
|
+
}),
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
/** 03-agent §1 */
|
|
228
|
+
export function createAgent(config) {
|
|
229
|
+
validateConfig(config);
|
|
230
|
+
// kill-list B5: a host that omits `store` still gets thread persistence —
|
|
231
|
+
// core's in-memory reference StoreAdapter, scoped to this agent instance's
|
|
232
|
+
// process lifetime — through the exact same ThreadRepository code path a
|
|
233
|
+
// store-backed composition uses. No separate memory-only branch survives.
|
|
234
|
+
const threads = new ThreadRepository(config.store ?? memoryStoreAdapter());
|
|
235
|
+
// ENG-252: per-thread set of tools loaded in via `vendo_tools_search`. It
|
|
236
|
+
// persists across turns within a run so a discovered tool stays callable, and
|
|
237
|
+
// is reclaimed on thread delete + session eviction. The LRU cap bounds memory
|
|
238
|
+
// for long-lived, store-backed processes where threads never get evicted (a
|
|
239
|
+
// reused/live thread is touched to the end, so only cold threads are dropped).
|
|
240
|
+
const loadedTools = new Map();
|
|
241
|
+
const MAX_LOADED_THREADS = 1024;
|
|
242
|
+
const loadedFor = (threadId) => {
|
|
243
|
+
const existing = loadedTools.get(threadId);
|
|
244
|
+
if (existing !== undefined) {
|
|
245
|
+
loadedTools.delete(threadId);
|
|
246
|
+
loadedTools.set(threadId, existing); // touch: most-recently-used
|
|
247
|
+
return existing;
|
|
248
|
+
}
|
|
249
|
+
const fresh = new Set();
|
|
250
|
+
loadedTools.set(threadId, fresh);
|
|
251
|
+
while (loadedTools.size > MAX_LOADED_THREADS) {
|
|
252
|
+
const oldest = loadedTools.keys().next().value;
|
|
253
|
+
if (oldest === undefined)
|
|
254
|
+
break;
|
|
255
|
+
loadedTools.delete(oldest);
|
|
256
|
+
}
|
|
257
|
+
return fresh;
|
|
258
|
+
};
|
|
259
|
+
return {
|
|
260
|
+
async stream(input) {
|
|
261
|
+
validateMessage(input?.message);
|
|
262
|
+
const thread = await threads.resolve(input.threadId, input.ctx);
|
|
263
|
+
validateUpsert(thread.messages, input.message);
|
|
264
|
+
if (input.message.role === "user"
|
|
265
|
+
&& !thread.messages.some((message) => message.id === input.message.id)) {
|
|
266
|
+
const abandonedCalls = abandonPendingApprovals(thread.messages);
|
|
267
|
+
// AGENT-6: resolve the abandoned asks guard-side too (denied, no
|
|
268
|
+
// grant), so the pending queue tracks the thread. Best-effort — the
|
|
269
|
+
// fresh turn must stream even when the guard write fails.
|
|
270
|
+
const approvalIds = guardApprovalIds(thread.messages, abandonedCalls);
|
|
271
|
+
if (approvalIds.length > 0 && config.guard.abandonApprovals !== undefined) {
|
|
272
|
+
try {
|
|
273
|
+
await config.guard.abandonApprovals(approvalIds, input.ctx);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// The thread already reflects abandonment; queue cleanup retries
|
|
277
|
+
// implicitly on the next abandoned turn.
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
upsertMessage(thread.messages, input.message);
|
|
282
|
+
const system = await assembleSystemPrompt(config.guard, input.ctx, config.system, config.capabilityMiss !== undefined);
|
|
283
|
+
const stream = createUIMessageStream({
|
|
284
|
+
originalMessages: thread.messages,
|
|
285
|
+
execute: async ({ writer }) => {
|
|
286
|
+
// AGENT-3: a client that disconnected before the turn started gets no
|
|
287
|
+
// provider call at all — the stream closes empty but well-formed.
|
|
288
|
+
if (input.signal?.aborted)
|
|
289
|
+
return;
|
|
290
|
+
const missDetector = config.capabilityMiss === undefined
|
|
291
|
+
? undefined
|
|
292
|
+
: createCapabilityMissDetector({
|
|
293
|
+
config: config.capabilityMiss,
|
|
294
|
+
ctx: input.ctx,
|
|
295
|
+
threadId: thread.id,
|
|
296
|
+
intent: latestUserIntent(thread.messages),
|
|
297
|
+
});
|
|
298
|
+
// Connection-scoped loadout (spec 2026-07-20): resolve + expand the
|
|
299
|
+
// principal's connected toolkits FIRST so the built toolset includes
|
|
300
|
+
// them; a failed seed degrades to the risk/name fallback, never the
|
|
301
|
+
// turn.
|
|
302
|
+
let seedNames;
|
|
303
|
+
if (config.toolSearch?.seed !== undefined) {
|
|
304
|
+
try {
|
|
305
|
+
seedNames = await config.toolSearch.seed(input.ctx);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
seedNames = undefined;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const bridgeOptions = {
|
|
312
|
+
registry: config.tools,
|
|
313
|
+
guard: config.guard,
|
|
314
|
+
ctx: input.ctx,
|
|
315
|
+
writer,
|
|
316
|
+
toolOutputCap: config.context?.toolOutputCap,
|
|
317
|
+
...(missDetector === undefined ? {} : { onCall: missDetector.onCall }),
|
|
318
|
+
};
|
|
319
|
+
const tools = await buildAgentTools(bridgeOptions);
|
|
320
|
+
missDetector?.attach(tools);
|
|
321
|
+
const toolSearch = config.toolSearch === undefined
|
|
322
|
+
? undefined
|
|
323
|
+
: createToolSearchSession({
|
|
324
|
+
config: config.toolSearch,
|
|
325
|
+
descriptors: await config.tools.descriptors(),
|
|
326
|
+
loaded: loadedFor(thread.id),
|
|
327
|
+
...(seedNames === undefined ? {} : { seedNames }),
|
|
328
|
+
// Search hits expanded mid-turn resolve to full descriptors and
|
|
329
|
+
// materialize into the LIVE toolset — prepareStep re-reads the
|
|
330
|
+
// active names each step, so they are callable next step.
|
|
331
|
+
resolve: async (names) => (await config.tools.descriptors()).filter((d) => names.includes(d.name)),
|
|
332
|
+
materialize: (descriptor) => addAgentTool(tools, descriptor, bridgeOptions),
|
|
333
|
+
});
|
|
334
|
+
toolSearch?.attach(tools);
|
|
335
|
+
// History windowing: bound what is re-sent per turn to the last N whole messages.
|
|
336
|
+
// Slicing whole UIMessages keeps each turn's tool-call/result pairing intact.
|
|
337
|
+
const window = config.context?.historyWindow;
|
|
338
|
+
const history = window !== undefined && thread.messages.length > window
|
|
339
|
+
? thread.messages.slice(-window)
|
|
340
|
+
: thread.messages;
|
|
341
|
+
const converted = (await convertToModelMessages(providerHistory(history)))
|
|
342
|
+
.filter((message) => message.content.length > 0);
|
|
343
|
+
// Cache the stable history prefix (everything but the final message) alongside the
|
|
344
|
+
// static system prompt below, so Anthropic re-reads the cached prefix instead of
|
|
345
|
+
// re-billing the whole growing thread each turn.
|
|
346
|
+
if (converted.length >= 2) {
|
|
347
|
+
const prefixEnd = converted[converted.length - 2];
|
|
348
|
+
prefixEnd.providerOptions = { ...prefixEnd.providerOptions, ...CACHE_BREAKPOINT };
|
|
349
|
+
}
|
|
350
|
+
const modelMessages = [
|
|
351
|
+
{ role: "system", content: system, providerOptions: CACHE_BREAKPOINT },
|
|
352
|
+
...converted,
|
|
353
|
+
];
|
|
354
|
+
const maxSteps = config.context?.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
355
|
+
const result = streamText({
|
|
356
|
+
model: config.model,
|
|
357
|
+
messages: modelMessages,
|
|
358
|
+
tools,
|
|
359
|
+
stopWhen: stepCountIs(maxSteps),
|
|
360
|
+
maxOutputTokens: config.context?.maxOutputTokens,
|
|
361
|
+
// ENG-252 loadout: restrict what the model may pick to the current
|
|
362
|
+
// loadout. `prepareStep` re-reads it each step so a tool loaded via
|
|
363
|
+
// `vendo_tools_search` becomes callable on the very next step. This
|
|
364
|
+
// gates the model's CHOICE only — every tool still executes through
|
|
365
|
+
// the guard-bound registry, so there is no unguarded path.
|
|
366
|
+
...(toolSearch === undefined
|
|
367
|
+
? {}
|
|
368
|
+
: {
|
|
369
|
+
activeTools: toolSearch.activeToolNames(),
|
|
370
|
+
prepareStep: () => ({ activeTools: toolSearch.activeToolNames() }),
|
|
371
|
+
}),
|
|
372
|
+
// AGENT-3: cancellation reaches the provider call itself; the loop
|
|
373
|
+
// never starts another step once the signal fires.
|
|
374
|
+
abortSignal: input.signal,
|
|
375
|
+
});
|
|
376
|
+
writer.merge(result.toUIMessageStream({
|
|
377
|
+
originalMessages: thread.messages,
|
|
378
|
+
// Raw provider/model error strings never reach the wire (they can
|
|
379
|
+
// carry request internals); the error part is a fixed generic message.
|
|
380
|
+
onError: () => "An error occurred while generating the response.",
|
|
381
|
+
}));
|
|
382
|
+
// AGENT-7: exhausting the step cap is VISIBLE. A run that still wants
|
|
383
|
+
// tool calls after its final permitted step ended because of the cap,
|
|
384
|
+
// not because the model finished — stream a renderable notice.
|
|
385
|
+
try {
|
|
386
|
+
const [finishReason, steps] = await Promise.all([result.finishReason, result.steps]);
|
|
387
|
+
if (finishReason === "tool-calls" && steps.length >= maxSteps) {
|
|
388
|
+
writer.write(toVendoWirePart({
|
|
389
|
+
type: "data-vendo-step-limit",
|
|
390
|
+
limit: maxSteps,
|
|
391
|
+
message: `Stopped after reaching the ${maxSteps}-step limit for one turn. Reply to continue.`,
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// The merged stream already surfaced the run failure; the notice is
|
|
397
|
+
// best-effort and must never replace or mask that error.
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
onFinish: async ({ messages }) => {
|
|
401
|
+
await persistFinishedTurn(threads, thread, messages, input.ctx);
|
|
402
|
+
},
|
|
403
|
+
onError: () => "An error occurred while generating the response.",
|
|
404
|
+
});
|
|
405
|
+
const response = createUIMessageStreamResponse({ stream });
|
|
406
|
+
// ENG-211: a caller may begin without an id, in which case resolve()
|
|
407
|
+
// mints one. Return the effective id on every turn so fetch clients can
|
|
408
|
+
// adopt it without changing the ai-SDK SSE part contract.
|
|
409
|
+
response.headers.set(THREAD_ID_HEADER, thread.id);
|
|
410
|
+
return response;
|
|
411
|
+
},
|
|
412
|
+
threads: {
|
|
413
|
+
get: (id, ctx) => threads.get(id, ctx),
|
|
414
|
+
list: (ctx) => threads.list(ctx),
|
|
415
|
+
delete: async (id, ctx) => {
|
|
416
|
+
loadedTools.delete(id);
|
|
417
|
+
await threads.delete(id, ctx);
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
evictSubject: (subject) => {
|
|
421
|
+
// kill-list B5: eviction now goes through the store (threads.evictSubject
|
|
422
|
+
// is async — a list+delete against the store), so
|
|
423
|
+
// this stays fire-and-forget to keep the public signature synchronous
|
|
424
|
+
// (03-agent §1). Release each evicted thread's searched-in loadout so a
|
|
425
|
+
// reused id can't inherit stale tools, and so memory is reclaimed on
|
|
426
|
+
// session sweep.
|
|
427
|
+
threads.evictSubject(subject)
|
|
428
|
+
.then((ids) => {
|
|
429
|
+
for (const id of ids) {
|
|
430
|
+
loadedTools.delete(id);
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
.catch((error) => {
|
|
434
|
+
console.error("[vendo] agent: evictSubject failed", {
|
|
435
|
+
subject,
|
|
436
|
+
error: error instanceof Error ? error.message : String(error),
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
},
|
|
440
|
+
asRunner: () => createRunner(config),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CapabilityMissEvent, type RunContext, type ThreadId, type ToolCall, type ToolOutcome } from "@vendoai/core";
|
|
2
|
+
import { type ToolSet, type UIMessage } from "ai";
|
|
3
|
+
export declare const CAPABILITY_MISS_TOOL_NAME = "vendo_report_capability_miss";
|
|
4
|
+
export interface CapabilityMissConfig {
|
|
5
|
+
hostId: string;
|
|
6
|
+
surface: Promise<CapabilityMissEvent["surface"]>;
|
|
7
|
+
emit(event: CapabilityMissEvent): void | Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
interface DetectorOptions {
|
|
10
|
+
config: CapabilityMissConfig;
|
|
11
|
+
ctx: RunContext;
|
|
12
|
+
intent: string;
|
|
13
|
+
threadId?: ThreadId;
|
|
14
|
+
}
|
|
15
|
+
/** Deterministic, deliberately conservative removal of common credential/PII forms. */
|
|
16
|
+
export declare function scrubCapabilityMissText(value: string): string;
|
|
17
|
+
export declare function latestUserIntent(messages: UIMessage[]): string;
|
|
18
|
+
export interface CapabilityMissDetector {
|
|
19
|
+
onCall(call: ToolCall): (outcome: ToolOutcome) => void;
|
|
20
|
+
attach(tools: ToolSet): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function createCapabilityMissDetector(options: DetectorOptions): CapabilityMissDetector;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=capability-miss.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-miss.d.ts","sourceRoot":"","sources":["../src/capability-miss.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAGxB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,WAAW,EACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAA2B,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,IAAI,CAAC;AAE3E,eAAO,MAAM,yBAAyB,iCAAiC,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,GAAG,EAAE,UAAU,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AA6BD,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AASD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAI9D;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACvD,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,GAAG,sBAAsB,CAgF7F"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { TOOL_NAME_PATTERN, VendoError, } from "@vendoai/core";
|
|
2
|
+
import { dynamicTool, jsonSchema } from "ai";
|
|
3
|
+
export const CAPABILITY_MISS_TOOL_NAME = "vendo_report_capability_miss";
|
|
4
|
+
const REPORT_INPUT_SCHEMA = {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
kind: { type: "string", enum: ["no-matching-tool", "agent-give-up"] },
|
|
8
|
+
toolsConsidered: {
|
|
9
|
+
type: "array",
|
|
10
|
+
items: { type: "string", pattern: TOOL_NAME_PATTERN.source },
|
|
11
|
+
maxItems: 100,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
required: ["kind", "toolsConsidered"],
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
};
|
|
17
|
+
function record(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
19
|
+
? value
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
function toolNames(value) {
|
|
23
|
+
if (!Array.isArray(value))
|
|
24
|
+
return [];
|
|
25
|
+
return [...new Set(value
|
|
26
|
+
.filter((name) => typeof name === "string" && TOOL_NAME_PATTERN.test(name))
|
|
27
|
+
.slice(0, 100))];
|
|
28
|
+
}
|
|
29
|
+
/** Deterministic, deliberately conservative removal of common credential/PII forms. */
|
|
30
|
+
export function scrubCapabilityMissText(value) {
|
|
31
|
+
const scrubbed = value
|
|
32
|
+
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted-email]")
|
|
33
|
+
.replace(/(\bBearer\s+)[A-Za-z0-9._~+\/-]{8,}/gi, "$1[redacted-secret]")
|
|
34
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted-secret]")
|
|
35
|
+
.replace(/\b(?:sk|pk|rk|vnd|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{8,}\b/gi, "[redacted-secret]")
|
|
36
|
+
.replace(/\b(api[_-]?key|access[_-]?token|secret|password)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted-secret]")
|
|
37
|
+
.replace(/\+?\d[\d\s().-]{8,}\d/g, "[redacted-phone]")
|
|
38
|
+
.trim()
|
|
39
|
+
.slice(0, 1_000);
|
|
40
|
+
return scrubbed || "Unspecified request";
|
|
41
|
+
}
|
|
42
|
+
function textFromPart(part) {
|
|
43
|
+
const candidate = part;
|
|
44
|
+
return candidate.type === "text" && typeof candidate.text === "string"
|
|
45
|
+
? candidate.text
|
|
46
|
+
: undefined;
|
|
47
|
+
}
|
|
48
|
+
export function latestUserIntent(messages) {
|
|
49
|
+
const message = [...messages].reverse().find((candidate) => candidate.role === "user");
|
|
50
|
+
if (!message)
|
|
51
|
+
return "Unspecified request";
|
|
52
|
+
return scrubCapabilityMissText(message.parts.map(textFromPart).filter(Boolean).join(" "));
|
|
53
|
+
}
|
|
54
|
+
export function createCapabilityMissDetector(options) {
|
|
55
|
+
const attempted = [];
|
|
56
|
+
const failures = new Map();
|
|
57
|
+
let reported = false;
|
|
58
|
+
const report = (trigger) => {
|
|
59
|
+
if (reported)
|
|
60
|
+
return false;
|
|
61
|
+
reported = true;
|
|
62
|
+
void (async () => {
|
|
63
|
+
const surface = await options.config.surface;
|
|
64
|
+
const event = {
|
|
65
|
+
format: "vendo/capability-miss@1",
|
|
66
|
+
id: `mis_${globalThis.crypto.randomUUID().replaceAll("-", "")}`,
|
|
67
|
+
at: new Date().toISOString(),
|
|
68
|
+
hostId: options.config.hostId,
|
|
69
|
+
...(options.ctx.appId === undefined ? {} : { appId: options.ctx.appId }),
|
|
70
|
+
sessionId: options.ctx.sessionId,
|
|
71
|
+
...(options.threadId === undefined ? {} : { threadId: options.threadId }),
|
|
72
|
+
intent: scrubCapabilityMissText(options.intent),
|
|
73
|
+
surface,
|
|
74
|
+
trigger,
|
|
75
|
+
};
|
|
76
|
+
await options.config.emit(event);
|
|
77
|
+
})().catch(() => {
|
|
78
|
+
// Reporting is deliberately fire-and-forget. It cannot alter the agent turn.
|
|
79
|
+
});
|
|
80
|
+
return true;
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
onCall(call) {
|
|
84
|
+
if (!attempted.includes(call.tool))
|
|
85
|
+
attempted.push(call.tool);
|
|
86
|
+
return (outcome) => {
|
|
87
|
+
if (reported || outcome.status !== "error")
|
|
88
|
+
return;
|
|
89
|
+
const toolFailures = failures.get(call.tool) ?? [];
|
|
90
|
+
toolFailures.push({
|
|
91
|
+
tool: call.tool,
|
|
92
|
+
attempt: toolFailures.length + 1,
|
|
93
|
+
failure: {
|
|
94
|
+
...(outcome.error.code.length === 0 ? {} : { code: outcome.error.code }),
|
|
95
|
+
message: scrubCapabilityMissText(outcome.error.message),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
failures.set(call.tool, toolFailures);
|
|
99
|
+
if (toolFailures.length < 2)
|
|
100
|
+
return;
|
|
101
|
+
report({
|
|
102
|
+
kind: "repeated-tool-failure",
|
|
103
|
+
toolsConsidered: [...attempted],
|
|
104
|
+
attempts: [...toolFailures],
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
attach(tools) {
|
|
109
|
+
if (tools[CAPABILITY_MISS_TOOL_NAME] !== undefined) {
|
|
110
|
+
throw new VendoError("conflict", `Reserved internal tool name: ${CAPABILITY_MISS_TOOL_NAME}`);
|
|
111
|
+
}
|
|
112
|
+
const availableTools = new Set(Object.keys(tools));
|
|
113
|
+
tools[CAPABILITY_MISS_TOOL_NAME] = dynamicTool({
|
|
114
|
+
description: "Report that the current user ask cannot be fulfilled. Use only for no matching tool or an explicit terminal give-up.",
|
|
115
|
+
inputSchema: jsonSchema(REPORT_INPUT_SCHEMA),
|
|
116
|
+
execute: async (input) => {
|
|
117
|
+
const parsed = record(input);
|
|
118
|
+
const kind = parsed?.kind;
|
|
119
|
+
if (kind !== "no-matching-tool" && kind !== "agent-give-up") {
|
|
120
|
+
return { status: "error", error: { code: "validation", message: "Invalid capability-miss trigger" } };
|
|
121
|
+
}
|
|
122
|
+
const toolsConsidered = toolNames(parsed?.toolsConsidered)
|
|
123
|
+
.filter((name) => availableTools.has(name));
|
|
124
|
+
const emitted = kind === "no-matching-tool"
|
|
125
|
+
? report({ kind, toolsConsidered })
|
|
126
|
+
: report({ kind, toolsConsidered, toolsAttempted: [...attempted] });
|
|
127
|
+
return { status: "ok", output: { reported: emitted } };
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=capability-miss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-miss.js","sourceRoot":"","sources":["../src/capability-miss.ts"],"names":[],"mappings":"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"}
|