@anteros/core 0.0.44 → 0.0.47
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 +21 -0
- package/README.md +1 -1
- package/database/field.crypto.ts +38 -13
- package/database/mongodbadapter.ts +85 -25
- package/database/vars.ts +22 -2
- package/index.ts +22 -0
- package/lib/agent.ts +707 -79
- package/lib/agentMemory.ts +199 -0
- package/lib/agents.ts +133 -52
- package/lib/attachment.ts +24 -3
- package/lib/define.ts +5 -1
- package/lib/eval.ts +123 -0
- package/lib/files.ts +21 -15
- package/lib/jsonSchema.ts +8 -0
- package/lib/mcp.ts +52 -30
- package/lib/messages.ts +202 -19
- package/lib/partialJson.ts +79 -0
- package/lib/providers.ts +26 -2
- package/lib/redact.ts +11 -0
- package/lib/tracing.ts +20 -0
- package/lib/zip.ts +23 -3
- package/package.json +4 -5
- package/server/agents.ts +66 -1
- package/server/api.ts +302 -20
- package/server/boot.ts +13 -4
- package/server/hono.ts +146 -6
- package/tests/access-log-body.test.ts +253 -0
- package/tests/agent-checkpoint.test.ts +195 -0
- package/tests/agent-hardening.test.ts +238 -0
- package/tests/agent-memory.test.ts +175 -1
- package/tests/agent-stream-advanced.test.ts +246 -0
- package/tests/agent-tracing-eval.test.ts +219 -0
- package/tests/agent-working-memory.test.ts +7 -4
- package/tests/agent.test.ts +54 -3
- package/tests/agents-api.test.ts +21 -0
- package/tests/api-external-guards.test.ts +304 -0
- package/tests/attachment.test.ts +63 -0
- package/tests/audit-redact.test.ts +26 -1
- package/tests/field-encryption.test.ts +47 -3
- package/tests/filters-hardening.test.ts +81 -0
- package/tests/fixtures/ai-tenant/ai/agents/support/billing.agent.ts +9 -0
- package/tests/fixtures/ai-tenant/ai/agents/support/billing.tool.ts +9 -0
- package/tests/fixtures/ai-tenant/ai/agents/support.agent.ts +9 -0
- package/tests/fixtures/ai-tenant/ai/agents/support.tool.ts +9 -0
- package/tests/fixtures/ai-tenant/ai/mcp/resources/readme.resource.ts +9 -0
- package/tests/fixtures/ai-tenant/ai/mcp/tools/echo.tool.ts +9 -0
- package/tests/fixtures/fake-provider.ts +33 -0
- package/tests/fixtures/tool-tenant/ai/agents/report.agent.ts +12 -0
- package/tests/fixtures/tool-tenant/ai/agents/report.tool.ts +12 -0
- package/tests/fixtures/vars-tenant/vars/open.var.ts +18 -0
- package/tests/hono.test.ts +81 -1
- package/tests/jwt-tenant.test.ts +165 -0
- package/tests/logger.test.ts +62 -10
- package/tests/messages.test.ts +88 -32
- package/tests/tool-runtime.test.ts +291 -0
- package/tests/tool-types.ts +33 -0
- package/tests/upload.test.ts +175 -3
- package/tests/vars-api.test.ts +73 -1
- package/tests/vars.test.ts +1 -1
- package/types/agent.d.ts +386 -4
- package/types/config.d.ts +56 -2
- package/types/file.d.ts +1 -1
- package/types/mcp.d.ts +2 -2
- package/utils/func.ts +20 -0
- package/utils/logger.ts +125 -33
package/types/agent.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type Joi from "joi";
|
|
2
|
+
import type { z } from "zod";
|
|
2
3
|
import type { useRest } from "../database/rest";
|
|
3
4
|
import type { fn } from "../lib/error";
|
|
4
5
|
import type { jwt } from "../utils/func";
|
|
@@ -55,6 +56,75 @@ export type AgentProvider = {
|
|
|
55
56
|
headers?: Record<string, string>;
|
|
56
57
|
/** Sampling and transport — `{ temperature, maxTokens, topP, timeout, retries }`. */
|
|
57
58
|
options?: AgentOptions;
|
|
59
|
+
/**
|
|
60
|
+
* Tried **in order** when the primary fails for a reason another endpoint may not
|
|
61
|
+
* have: `429`, `5xx`, a network error or a timeout. `options` are merged over the
|
|
62
|
+
* primary's, so a fallback only states what differs.
|
|
63
|
+
*/
|
|
64
|
+
fallbacks?: AgentFallback[];
|
|
65
|
+
/**
|
|
66
|
+
* USD per **million** tokens — turns `usage` into `usage.cost`. A fallback may
|
|
67
|
+
* price differently: the model that answered is the one billed.
|
|
68
|
+
*/
|
|
69
|
+
pricing?: AgentPricing;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** One step of `provider.fallbacks` — what may differ from the primary. */
|
|
73
|
+
export type AgentFallback = {
|
|
74
|
+
model: string;
|
|
75
|
+
apiKey?: string;
|
|
76
|
+
protocol?: AgentProtocol;
|
|
77
|
+
baseUrl?: string;
|
|
78
|
+
headers?: Record<string, string>;
|
|
79
|
+
options?: AgentOptions;
|
|
80
|
+
pricing?: AgentPricing;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Cost of a model, per million tokens (USD). */
|
|
84
|
+
export type AgentPricing = {
|
|
85
|
+
/** USD per million **input** tokens. */
|
|
86
|
+
input: number;
|
|
87
|
+
/** USD per million **output** tokens. */
|
|
88
|
+
output: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A guardrail — a check that can **stop** a run. `check` throws to block; the
|
|
93
|
+
* thrown message is what the caller reads.
|
|
94
|
+
*/
|
|
95
|
+
export type AgentGuardrail = {
|
|
96
|
+
/** Named in the error, so a failing check is identifiable. */
|
|
97
|
+
name: string;
|
|
98
|
+
check: (value: string, ctx: AgentGuardrailContext) => void | Promise<void>;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Where a guardrail runs. The lists are additive: declaration first, then the call. */
|
|
102
|
+
export type AgentGuardrails = {
|
|
103
|
+
/** Before the first model call — the caller's prompt (the last user turn). */
|
|
104
|
+
input?: AgentGuardrail[];
|
|
105
|
+
/** Before the answer is returned or stored — what the model produced. */
|
|
106
|
+
output?: AgentGuardrail[];
|
|
107
|
+
/**
|
|
108
|
+
* After a tool **succeeded**, on its serialized result, before the model sees
|
|
109
|
+
* it (a tool result can carry what the prompt may not).
|
|
110
|
+
*
|
|
111
|
+
* A check that throws blocks **that result**, not the run: the model reads
|
|
112
|
+
* `Error [AGENT_GUARDRAIL_BLOCKED]: …` and goes on. A failed tool result is
|
|
113
|
+
* never checked.
|
|
114
|
+
*/
|
|
115
|
+
tool?: AgentGuardrail[];
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export type AgentGuardrailContext = {
|
|
119
|
+
agent: Agent;
|
|
120
|
+
tenant?: string;
|
|
121
|
+
thread?: string;
|
|
122
|
+
resource?: string;
|
|
123
|
+
signal: AbortSignal;
|
|
124
|
+
/** Set when the check runs on a **tool result** — the tool's name. */
|
|
125
|
+
tool?: string;
|
|
126
|
+
/** Set when the check runs on a tool result — the call it answers. */
|
|
127
|
+
toolCallId?: string;
|
|
58
128
|
};
|
|
59
129
|
|
|
60
130
|
/**
|
|
@@ -238,7 +308,13 @@ export type AgentToolMessage = {
|
|
|
238
308
|
/** Id of the `tool_use` / `tool_call` this message answers. */
|
|
239
309
|
toolCallId: string;
|
|
240
310
|
name: string;
|
|
241
|
-
|
|
311
|
+
/**
|
|
312
|
+
* What the model reads. Text is the default; a tool may also return **media**
|
|
313
|
+
* (`{ content: [{ type: 'image', … }] }`) — Anthropic receives the image as a
|
|
314
|
+
* `tool_result` block, OpenAI only text plus a marker (its tool message is
|
|
315
|
+
* text-only), and the stored thread keeps a marker, never the base64.
|
|
316
|
+
*/
|
|
317
|
+
content: string | AgentMediaPart[];
|
|
242
318
|
isError?: boolean;
|
|
243
319
|
};
|
|
244
320
|
|
|
@@ -291,6 +367,12 @@ export type AgentToolResultEntry = {
|
|
|
291
367
|
result?: any;
|
|
292
368
|
/** Set when the tool threw, was unknown, or its arguments were invalid. */
|
|
293
369
|
error?: string;
|
|
370
|
+
/**
|
|
371
|
+
* Stable code of that failure — `AGENT_TOOL_FAILED`, `AGENT_TOOL_TIMEOUT`,
|
|
372
|
+
* `AGENT_TOOL_ARGS_INVALID`, `AGENT_TOOL_UNKNOWN`, or the `AppError` code the
|
|
373
|
+
* tool threw. The model reads it as `Error [CODE]: message`, so it can branch.
|
|
374
|
+
*/
|
|
375
|
+
errorCode?: string;
|
|
294
376
|
durationMs: number;
|
|
295
377
|
};
|
|
296
378
|
|
|
@@ -303,6 +385,8 @@ export type AgentUsage = {
|
|
|
303
385
|
totalTokens: number;
|
|
304
386
|
/** How many model calls the run needed (1 + one per tool round). */
|
|
305
387
|
requests: number;
|
|
388
|
+
/** USD, when the provider declares `pricing` (delegated runs included). */
|
|
389
|
+
cost?: number;
|
|
306
390
|
};
|
|
307
391
|
|
|
308
392
|
/** One model call of a run, with the tools it asked for. */
|
|
@@ -364,6 +448,12 @@ export type AgentTool = {
|
|
|
364
448
|
/** Shown to the model — say what it does and when to use it. */
|
|
365
449
|
description?: string;
|
|
366
450
|
enabled?: boolean;
|
|
451
|
+
/**
|
|
452
|
+
* The tool only **reads** — it may run concurrently with the other read-only
|
|
453
|
+
* tools of the same step, even when `parallelTools` is off. A tool that writes
|
|
454
|
+
* stays sequential: two writes must not interleave.
|
|
455
|
+
*/
|
|
456
|
+
readOnly?: boolean;
|
|
367
457
|
/** Arguments schema: Joi, zod or a plain JSON Schema (defaults to an open object). */
|
|
368
458
|
inputSchema?: Joi.Schema | any;
|
|
369
459
|
/** Handler. The returned value is serialized back to the model. */
|
|
@@ -387,8 +477,26 @@ export type AgentTool = {
|
|
|
387
477
|
approval?: boolean;
|
|
388
478
|
};
|
|
389
479
|
|
|
390
|
-
/**
|
|
391
|
-
|
|
480
|
+
/**
|
|
481
|
+
* What a tool must carry to be **declared** — `define.Tool` enforces the `id`
|
|
482
|
+
* **and** the `description`: a tool the model cannot understand is a tool it cannot
|
|
483
|
+
* choose, so both are required at the type level.
|
|
484
|
+
*
|
|
485
|
+
* `TInput` is the handler's input. It is **inferred** from a zod schema
|
|
486
|
+
* (`z.output`); Joi has no type inference, so give it explicitly:
|
|
487
|
+
*
|
|
488
|
+
* ```ts
|
|
489
|
+
* define.Tool({ inputSchema: z.object({ city: z.string() }), execute: ({ city }) => … });
|
|
490
|
+
* define.Tool<{ city: string }>({ inputSchema: v.object({ city: … }), execute: ({ city }) => … });
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
export type AgentToolDefinition<TInput = any> = Omit<AgentTool, "inputSchema" | "execute"> & {
|
|
494
|
+
id: string;
|
|
495
|
+
description: string;
|
|
496
|
+
/** Joi (`v`), zod, or a plain JSON Schema. */
|
|
497
|
+
inputSchema?: Joi.Schema | z.ZodType | any;
|
|
498
|
+
execute?: (input: TInput, ctx: AgentToolContext) => any | Promise<any>;
|
|
499
|
+
};
|
|
392
500
|
|
|
393
501
|
export type AgentTools = Record<string, AgentTool> | AgentTool[];
|
|
394
502
|
|
|
@@ -457,6 +565,83 @@ export type AgentMemoryProcessor = {
|
|
|
457
565
|
) => AgentMessage[] | void | Promise<AgentMessage[] | void>;
|
|
458
566
|
};
|
|
459
567
|
|
|
568
|
+
/** One hit returned by the vector search of a semantic-recall processor. */
|
|
569
|
+
export type AgentRecallHit = {
|
|
570
|
+
/** Stable id of the indexed message — the one `upsert` stored. */
|
|
571
|
+
id?: string;
|
|
572
|
+
/** The text to put back in the prompt (the store may expand `messageRange`). */
|
|
573
|
+
text: string;
|
|
574
|
+
role?: string;
|
|
575
|
+
thread?: string;
|
|
576
|
+
resourceId?: string;
|
|
577
|
+
score?: number;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
/** One message handed to the vector store by the recall processor. */
|
|
581
|
+
export type AgentRecallRecord = {
|
|
582
|
+
id: string;
|
|
583
|
+
text: string;
|
|
584
|
+
vector: number[];
|
|
585
|
+
thread?: string;
|
|
586
|
+
resourceId?: string;
|
|
587
|
+
role?: string;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/** What a recall processor asks the vector store — Mastra's `semanticRecall` options. */
|
|
591
|
+
export type AgentRecallQuery = {
|
|
592
|
+
/** Number of similar messages to retrieve. */
|
|
593
|
+
topK: number;
|
|
594
|
+
/** Surrounding messages the store should include with each hit. */
|
|
595
|
+
messageRange: number;
|
|
596
|
+
/** Search the current thread only, or every thread of the caller. */
|
|
597
|
+
scope: "thread" | "resource";
|
|
598
|
+
/** Metadata criteria, applied by the store when it supports them. */
|
|
599
|
+
filter?: Record<string, unknown>;
|
|
600
|
+
/** The run's thread (scoping the search). */
|
|
601
|
+
thread?: string;
|
|
602
|
+
/** The run's caller (namespacing the search). */
|
|
603
|
+
resourceId?: string;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Options of `agents.memory.semanticRecall()` — the framework's processor-shaped
|
|
608
|
+
* projection of Mastra's `semanticRecall`.
|
|
609
|
+
*
|
|
610
|
+
* Mapping: Mastra's `embedder` → `embed`, `vector.search` → `search`,
|
|
611
|
+
* `vector.upsert` → `upsert`, and `options.semanticRecall` → `topK`,
|
|
612
|
+
* `messageRange`, `scope`, `filter`.
|
|
613
|
+
*/
|
|
614
|
+
export type AgentSemanticRecallOptions = {
|
|
615
|
+
/**
|
|
616
|
+
* Embed texts. `kind` separates the two sides of an asymmetric model:
|
|
617
|
+
* `'query'` for the current question, `'passage'` for indexed messages
|
|
618
|
+
* (E5 wants `query:` / `passage:` prefixes).
|
|
619
|
+
*/
|
|
620
|
+
embed: (texts: string[], kind: "query" | "passage") => Promise<number[][]> | number[][];
|
|
621
|
+
/**
|
|
622
|
+
* Vector search — return `[]` when nothing is close enough. `ctx` is the run
|
|
623
|
+
* context: a Mongo-backed store reaches the tenant database through `ctx.rest`.
|
|
624
|
+
*/
|
|
625
|
+
search: (vector: number[], query: AgentRecallQuery, ctx: AgentMemoryContext) => Promise<AgentRecallHit[]> | AgentRecallHit[];
|
|
626
|
+
/**
|
|
627
|
+
* Index messages. Optional: without it the processor only **recalls** from an
|
|
628
|
+
* index you fill elsewhere (a job, `remember()`, another writer). Should be
|
|
629
|
+
* idempotent — the same `id` may be written again after a restart. `ctx` is
|
|
630
|
+
* the run context, with the same `rest` as `search`.
|
|
631
|
+
*/
|
|
632
|
+
upsert?: (records: AgentRecallRecord[], ctx: AgentMemoryContext) => Promise<void> | void;
|
|
633
|
+
/** Similar messages retrieved (default `3`). */
|
|
634
|
+
topK?: number;
|
|
635
|
+
/** Messages around each hit, left to the store (default `2`). */
|
|
636
|
+
messageRange?: number;
|
|
637
|
+
/** `'thread'` (default) or `'resource'` — the caller's other conversations. */
|
|
638
|
+
scope?: "thread" | "resource";
|
|
639
|
+
/** Metadata filter forwarded to the store. */
|
|
640
|
+
filter?: Record<string, unknown>;
|
|
641
|
+
/** How the hits are rendered into the injected system message. */
|
|
642
|
+
format?: (hits: AgentRecallHit[]) => string;
|
|
643
|
+
};
|
|
644
|
+
|
|
460
645
|
/** What a memory store knows about the run asking for a thread. */
|
|
461
646
|
export type AgentMemoryContext = {
|
|
462
647
|
agentId?: string;
|
|
@@ -502,6 +687,36 @@ export type AgentMemoryOptions = {
|
|
|
502
687
|
scoped?: boolean;
|
|
503
688
|
};
|
|
504
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Options of `agents.memory.custom()` — your own storage behind the `AgentMemory`
|
|
692
|
+
* contract, with the framework's scoping (`scoped`) and message cap
|
|
693
|
+
* (`maxMessages`) applied for you.
|
|
694
|
+
*
|
|
695
|
+
* A handler receives the **resolved key**: `resourceId:threadId` when `scoped`
|
|
696
|
+
* (default) and a caller identity is present, the bare thread id otherwise. The
|
|
697
|
+
* caller's context (`thread`, `resourceId`, `agentId`, `tenant`, `rest`) travels
|
|
698
|
+
* as the second argument, so a handler can key on whatever it needs.
|
|
699
|
+
*/
|
|
700
|
+
export type CustomAgentMemoryOptions = AgentMemoryOptions & {
|
|
701
|
+
/** A label for logs and error messages (default `'custom'`). */
|
|
702
|
+
name?: string;
|
|
703
|
+
/** Read a thread — `[]` when it does not exist. */
|
|
704
|
+
get: (key: string, ctx: AgentMemoryContext) => Promise<AgentMessage[]> | AgentMessage[];
|
|
705
|
+
/** Persist a thread — `messages` is already capped to `maxMessages`. */
|
|
706
|
+
save: (key: string, messages: AgentMessage[], ctx: AgentMemoryContext) => Promise<void> | void;
|
|
707
|
+
/** Forget a thread. */
|
|
708
|
+
clear: (key: string, ctx: AgentMemoryContext) => Promise<void> | void;
|
|
709
|
+
/** The caller's threads, most recent first (needed by a chat UI). */
|
|
710
|
+
list?: (ctx: AgentMemoryContext & { limit?: number }) => Promise<AgentThread[]> | AgentThread[];
|
|
711
|
+
/** Working memory — one durable value per scope key. */
|
|
712
|
+
getState?: (key: string, ctx: AgentMemoryContext) => Promise<AgentMemoryState | undefined> | AgentMemoryState | undefined;
|
|
713
|
+
setState?: (key: string, value: AgentMemoryState, ctx: AgentMemoryContext) => Promise<void> | void;
|
|
714
|
+
/** Forget a scratchpad. */
|
|
715
|
+
clearState?: (key: string, ctx: AgentMemoryContext) => Promise<void> | void;
|
|
716
|
+
/** Storage collections to replicate (Mongo-backed stores only). */
|
|
717
|
+
collections?: () => string[];
|
|
718
|
+
};
|
|
719
|
+
|
|
505
720
|
/**
|
|
506
721
|
* The agent's **working memory** — a durable scratchpad it keeps up to date
|
|
507
722
|
* (a user profile, the state of a task):
|
|
@@ -632,6 +847,49 @@ export type AgentResume = {
|
|
|
632
847
|
decisions: Record<string, boolean>;
|
|
633
848
|
};
|
|
634
849
|
|
|
850
|
+
/**
|
|
851
|
+
* What a run leaves behind at a step boundary — enough to continue it later
|
|
852
|
+
* (`agent.resumeRun()`), whatever killed the process in between.
|
|
853
|
+
*
|
|
854
|
+
* The conversation is **always valid**: an assistant turn with unanswered tool
|
|
855
|
+
* calls is never stored, so replaying it back to the model is safe.
|
|
856
|
+
*/
|
|
857
|
+
export type AgentRunState = {
|
|
858
|
+
runId: string;
|
|
859
|
+
agentId?: string;
|
|
860
|
+
/** Completed steps. */
|
|
861
|
+
step: number;
|
|
862
|
+
/** The whole conversation as of the last completed step — always valid (no unanswered tool call). */
|
|
863
|
+
messages: AgentMessage[];
|
|
864
|
+
usage: AgentUsage;
|
|
865
|
+
toolStats: AgentToolStats;
|
|
866
|
+
model?: string;
|
|
867
|
+
finishReason?: string;
|
|
868
|
+
/** ISO timestamp. */
|
|
869
|
+
at: string;
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Durable checkpoint of a run — one state per run id, overwritten at every step
|
|
874
|
+
* boundary and cleared when the run completes.
|
|
875
|
+
*
|
|
876
|
+
* ```ts
|
|
877
|
+
* const checkpoint: AgentRunStore = {
|
|
878
|
+
* load: (runId) => runs.get(runId),
|
|
879
|
+
* save: (runId, state) => { runs.set(runId, state); },
|
|
880
|
+
* clear: (runId) => { runs.delete(runId); },
|
|
881
|
+
* };
|
|
882
|
+
* ```
|
|
883
|
+
*
|
|
884
|
+
* The three methods may be synchronous; a failure of `save` never fails the run
|
|
885
|
+
* (it is logged), and the run id is the caller's identity for that run.
|
|
886
|
+
*/
|
|
887
|
+
export type AgentRunStore = {
|
|
888
|
+
load(runId: string): Promise<AgentRunState | undefined> | AgentRunState | undefined;
|
|
889
|
+
save(runId: string, state: AgentRunState): Promise<void> | void;
|
|
890
|
+
clear(runId: string): Promise<void> | void;
|
|
891
|
+
};
|
|
892
|
+
|
|
635
893
|
export type AgentCallOptions = {
|
|
636
894
|
/** Thread to read from / write to the memory (same as `memory.thread`). */
|
|
637
895
|
thread?: string;
|
|
@@ -730,6 +988,23 @@ export type AgentCallOptions = {
|
|
|
730
988
|
};
|
|
731
989
|
/** Called after each model call, before its tools run. */
|
|
732
990
|
onStepFinish?: (step: AgentStep) => void | Promise<void>;
|
|
991
|
+
/**
|
|
992
|
+
* Observations of this run — see `AgentTraceEvent`. Overrides the agent's
|
|
993
|
+
* `onTrace`; a hook that throws is ignored (it never fails the run).
|
|
994
|
+
*/
|
|
995
|
+
onTrace?: (event: AgentTraceEvent) => void;
|
|
996
|
+
/**
|
|
997
|
+
* Identifies this run in the trace (`run.start.runId`) — and the key of its
|
|
998
|
+
* durable checkpoint: with a `checkpoint` store, the run saves its state after
|
|
999
|
+
* every completed step under this id, and `agent.resumeRun(runId)` continues it.
|
|
1000
|
+
* No `runId` means no checkpoint (a run without identity cannot be resumed).
|
|
1001
|
+
*/
|
|
1002
|
+
runId?: string;
|
|
1003
|
+
/**
|
|
1004
|
+
* Where this run's state is saved after each completed step — overrides the
|
|
1005
|
+
* agent's `checkpoint` for this call. Needs a `runId`.
|
|
1006
|
+
*/
|
|
1007
|
+
checkpoint?: AgentRunStore;
|
|
733
1008
|
/**
|
|
734
1009
|
* Give up on a single tool after N ms (default: the agent's `toolTimeoutMs`).
|
|
735
1010
|
* A tool that times out is reported to the model as an error, and the run goes on.
|
|
@@ -739,9 +1014,25 @@ export type AgentCallOptions = {
|
|
|
739
1014
|
* Run the tools of one step **in parallel** (default `false` — sequential).
|
|
740
1015
|
*
|
|
741
1016
|
* Only safe when the tools do not write to the same place: two calls of the same
|
|
742
|
-
* tool with the same target can interleave. Read-only tools
|
|
1017
|
+
* tool with the same target can interleave. Read-only tools (`readOnly: true`)
|
|
1018
|
+
* already run together without this flag.
|
|
743
1019
|
*/
|
|
744
1020
|
parallelTools?: boolean;
|
|
1021
|
+
/**
|
|
1022
|
+
* How many tool calls of one step may be in flight at once (default `8`).
|
|
1023
|
+
* A model that emits 50 calls in a turn must not open 50 outbound requests.
|
|
1024
|
+
*/
|
|
1025
|
+
maxParallelToolCalls?: number;
|
|
1026
|
+
/** Stop cleanly (`finishReason: 'budget'`) once the run used that many tokens. */
|
|
1027
|
+
maxTokens?: number;
|
|
1028
|
+
/** Stop cleanly once the run has lasted that long, in milliseconds. */
|
|
1029
|
+
maxDurationMs?: number;
|
|
1030
|
+
/** Stop cleanly once the run cost that much (USD) — needs `provider.pricing`. */
|
|
1031
|
+
maxCost?: number;
|
|
1032
|
+
/** Prompt budget in **tokens** — the oldest turns are dropped beyond it. */
|
|
1033
|
+
contextTokens?: number;
|
|
1034
|
+
/** Input/output guardrails for this run — added to the declared ones. */
|
|
1035
|
+
guardrails?: AgentGuardrails;
|
|
745
1036
|
/**
|
|
746
1037
|
* Store what the run produced even when it **fails** (default `false`).
|
|
747
1038
|
*
|
|
@@ -757,6 +1048,41 @@ export type AgentCallOptions = {
|
|
|
757
1048
|
resume?: AgentResume;
|
|
758
1049
|
};
|
|
759
1050
|
|
|
1051
|
+
/** Aggregate of the tool calls of one run — what `result.toolStats` carries. */
|
|
1052
|
+
export type AgentToolStats = {
|
|
1053
|
+
/** Calls executed (unknown tools and invalid arguments included). */
|
|
1054
|
+
calls: number;
|
|
1055
|
+
/** Calls that failed, whatever the cause. */
|
|
1056
|
+
errors: number;
|
|
1057
|
+
/** Failures by code (`AGENT_TOOL_TIMEOUT`, `AGENT_TOOL_UNKNOWN`…). */
|
|
1058
|
+
byCode: Record<string, number>;
|
|
1059
|
+
/** Abandoned at their deadline (`AGENT_TOOL_TIMEOUT`). */
|
|
1060
|
+
timeouts: number;
|
|
1061
|
+
/** Sum of the durations of every call, in milliseconds. */
|
|
1062
|
+
durationMs: number;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* One milestone of a run, handed to `onTrace` — a **plain** event, not a span.
|
|
1067
|
+
*
|
|
1068
|
+
* `at` is `Date.now()`; turning the events into spans (durations, parents,
|
|
1069
|
+
* attributes) is the caller's job, so no tracing dependency is involved.
|
|
1070
|
+
*
|
|
1071
|
+
* ```ts
|
|
1072
|
+
* const agent = new Agent({
|
|
1073
|
+
* …,
|
|
1074
|
+
* onTrace: (event) => console.log(event.type, event.at),
|
|
1075
|
+
* });
|
|
1076
|
+
* ```
|
|
1077
|
+
*/
|
|
1078
|
+
export type AgentTraceEvent =
|
|
1079
|
+
| { type: "run.start"; at: number; agentId?: string; runId?: string; thread?: string; resource?: string }
|
|
1080
|
+
| { type: "step.start"; at: number; step: number }
|
|
1081
|
+
| { type: "model.end"; at: number; step: number; durationMs: number; model: string; usage: AgentUsage; finishReason: string }
|
|
1082
|
+
| { type: "tool.start"; at: number; step: number; tool: string; toolCallId: string }
|
|
1083
|
+
| { type: "tool.end"; at: number; step: number; tool: string; toolCallId: string; durationMs: number; errorCode?: string }
|
|
1084
|
+
| { type: "run.end"; at: number; finishReason: string; durationMs: number; usage: AgentUsage; toolStats: AgentToolStats; error?: string };
|
|
1085
|
+
|
|
760
1086
|
export type AgentGenerateResult<T = any> = {
|
|
761
1087
|
text: string;
|
|
762
1088
|
/** Present when a `schema` / `structuredOutput` was requested. */
|
|
@@ -765,11 +1091,17 @@ export type AgentGenerateResult<T = any> = {
|
|
|
765
1091
|
reasoning?: string;
|
|
766
1092
|
toolCalls: AgentToolCall[];
|
|
767
1093
|
toolResults: AgentToolResultEntry[];
|
|
1094
|
+
/** Aggregate of every tool call of the run — counts, failures, time. */
|
|
1095
|
+
toolStats: AgentToolStats;
|
|
768
1096
|
steps: AgentStep[];
|
|
769
1097
|
usage: AgentUsage;
|
|
770
1098
|
finishReason: string;
|
|
771
1099
|
/** The complete conversation, ready to be persisted. */
|
|
772
1100
|
messages: AgentMessage[];
|
|
1101
|
+
/** The model that answered (a fallback may have been used). */
|
|
1102
|
+
model?: string;
|
|
1103
|
+
/** Present when the run stopped on a budget rather than on an answer. */
|
|
1104
|
+
budget?: { limit: "tokens" | "cost" | "duration"; value: number };
|
|
773
1105
|
/**
|
|
774
1106
|
* Calls waiting for a decision — present (and `finishReason`
|
|
775
1107
|
* `'approval_required'`) when a tool declaring `approval: true` was called.
|
|
@@ -787,6 +1119,12 @@ export type AgentStreamChunk =
|
|
|
787
1119
|
| { type: "tool_call"; toolCall: AgentToolCall }
|
|
788
1120
|
| { type: "tool_result"; toolResult: AgentToolResultEntry }
|
|
789
1121
|
| { type: "step"; step: AgentStep }
|
|
1122
|
+
/**
|
|
1123
|
+
* A chunk of a **delegated** run, re-emitted by the parent while the sub-agent
|
|
1124
|
+
* streams — `name` is the child's id. Only emitted when the parent streams;
|
|
1125
|
+
* the child's text never reaches the parent's `textStream`.
|
|
1126
|
+
*/
|
|
1127
|
+
| { type: "subagent"; name: string; chunk: AgentStreamChunk }
|
|
790
1128
|
| { type: "finish"; finishReason: string; usage: AgentUsage };
|
|
791
1129
|
|
|
792
1130
|
/**
|
|
@@ -804,6 +1142,12 @@ export type AgentStreamResult<T = any> = {
|
|
|
804
1142
|
object: Promise<T | undefined>;
|
|
805
1143
|
toolCalls: Promise<AgentToolCall[]>;
|
|
806
1144
|
toolResults: Promise<AgentToolResultEntry[]>;
|
|
1145
|
+
/** Aggregate of every tool call of the run — same as `generate()`. */
|
|
1146
|
+
toolStats: Promise<AgentToolStats>;
|
|
1147
|
+
/** The model that answered (a fallback may have been used). */
|
|
1148
|
+
model: Promise<string | undefined>;
|
|
1149
|
+
/** Present when the run stopped on a budget. */
|
|
1150
|
+
budget: Promise<{ limit: "tokens" | "cost" | "duration"; value: number } | undefined>;
|
|
807
1151
|
steps: Promise<AgentStep[]>;
|
|
808
1152
|
usage: Promise<AgentUsage>;
|
|
809
1153
|
finishReason: Promise<string>;
|
|
@@ -812,6 +1156,21 @@ export type AgentStreamResult<T = any> = {
|
|
|
812
1156
|
abort: (reason?: string) => void;
|
|
813
1157
|
};
|
|
814
1158
|
|
|
1159
|
+
/**
|
|
1160
|
+
* `agent.streamObject()` result — everything `stream()` returns, plus the
|
|
1161
|
+
* objects parsed from the text as it arrives.
|
|
1162
|
+
*
|
|
1163
|
+
* `partialObjectStream` **consumes** the run's text, so a caller chooses either
|
|
1164
|
+
* `textStream` or `partialObjectStream` — not both.
|
|
1165
|
+
*/
|
|
1166
|
+
export type AgentStreamObjectResult<T = any> = AgentStreamResult<T> & {
|
|
1167
|
+
/**
|
|
1168
|
+
* A best-effort parse of the answer so far — `undefined` until the partial
|
|
1169
|
+
* JSON parses. The final, **validated** object is `result.object`.
|
|
1170
|
+
*/
|
|
1171
|
+
partialObjectStream: AsyncIterable<T | undefined>;
|
|
1172
|
+
};
|
|
1173
|
+
|
|
815
1174
|
/** Instructions — static text, or computed per call (A/B tests, dynamic prompts). */
|
|
816
1175
|
export type AgentInstructions =
|
|
817
1176
|
| string
|
|
@@ -987,8 +1346,31 @@ export type AgentConfig = {
|
|
|
987
1346
|
toolTimeoutMs?: number;
|
|
988
1347
|
/** Run the tools of one step in parallel by default — see `AgentCallOptions`. */
|
|
989
1348
|
parallelTools?: boolean;
|
|
1349
|
+
/** Cap the tool calls in flight per step — see `AgentCallOptions`. */
|
|
1350
|
+
maxParallelToolCalls?: number;
|
|
1351
|
+
/** Default run budget in tokens — see `AgentCallOptions.maxTokens`. */
|
|
1352
|
+
maxTokens?: number;
|
|
1353
|
+
/** Default wall-clock budget of a run, in milliseconds. */
|
|
1354
|
+
maxDurationMs?: number;
|
|
1355
|
+
/** Default cost budget (USD) — needs `provider.pricing`. */
|
|
1356
|
+
maxCost?: number;
|
|
1357
|
+
/** Default prompt budget in tokens — see `AgentCallOptions.contextTokens`. */
|
|
1358
|
+
contextTokens?: number;
|
|
1359
|
+
/** Guardrails of every run — input before the first call, output before the answer. */
|
|
1360
|
+
guardrails?: AgentGuardrails;
|
|
990
1361
|
/** Store a failed run's conversation by default — see `AgentCallOptions`. */
|
|
991
1362
|
savePartial?: boolean;
|
|
1363
|
+
/**
|
|
1364
|
+
* Default durable checkpoint — a run that declares a `runId` saves its state
|
|
1365
|
+
* after each completed step there, and `resumeRun()` continues it. A call's own
|
|
1366
|
+
* `checkpoint` wins. See `AgentRunStore`.
|
|
1367
|
+
*/
|
|
1368
|
+
checkpoint?: AgentRunStore;
|
|
1369
|
+
/**
|
|
1370
|
+
* Observe every run of this agent — see `AgentTraceEvent`. A call's own
|
|
1371
|
+
* `onTrace` takes precedence; a hook that throws never fails the run.
|
|
1372
|
+
*/
|
|
1373
|
+
onTrace?: (event: AgentTraceEvent) => void;
|
|
992
1374
|
/**
|
|
993
1375
|
* Other agents exposed to this one **as tools** — the model decides when to
|
|
994
1376
|
* delegate: `agents: [researcher]` (an agent definition, usually the default
|
package/types/config.d.ts
CHANGED
|
@@ -110,9 +110,42 @@ export type ServerConfig = {
|
|
|
110
110
|
denyList?: IPRestrictionRule[];
|
|
111
111
|
allowList?: IPRestrictionRule[];
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Security headers (Hono `secureHeaders`).
|
|
115
|
+
*
|
|
116
|
+
* `crossOriginResourcePolicy` sets `Cross-Origin-Resource-Policy` on the
|
|
117
|
+
* API responses. The default (`'same-origin'`) blocks another origin (a
|
|
118
|
+
* front served from a different host) from embedding them — an `<img
|
|
119
|
+
* src="https://api…">` then fails with `corp-not-same-origin`.
|
|
120
|
+
* Set `'cross-origin'` to allow embedding, or keep the default for an
|
|
121
|
+
* API only consumed by same-origin scripts.
|
|
122
|
+
*/
|
|
123
|
+
security?: {
|
|
124
|
+
crossOriginResourcePolicy?: 'same-origin' | 'same-site' | 'cross-origin';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* File serving (`GET /api/:tenant_id/files/:collection/:filename`).
|
|
128
|
+
*/
|
|
129
|
+
files?: {
|
|
130
|
+
/**
|
|
131
|
+
* `Cross-Origin-Resource-Policy` on file responses (default:
|
|
132
|
+
* `'cross-origin'` — files are meant to be embedded by the app,
|
|
133
|
+
* possibly served from another origin). Set `'same-origin'` to
|
|
134
|
+
* forbid cross-origin embedding (tightest), or `'same-site'`.
|
|
135
|
+
*/
|
|
136
|
+
crossOriginResourcePolicy?: 'same-origin' | 'same-site' | 'cross-origin';
|
|
137
|
+
}
|
|
113
138
|
jwt?: {
|
|
114
139
|
secret?: string;
|
|
115
140
|
expiresIn?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Reject a token that carries **no tenant claim** (default `false`).
|
|
143
|
+
*
|
|
144
|
+
* Tokens issued by the login action always carry `tenant` (the claim is
|
|
145
|
+
* injected by the framework). Legacy tokens issued before that change have
|
|
146
|
+
* no claim: set this to `true` once they have expired to require one.
|
|
147
|
+
*/
|
|
148
|
+
requireTenantClaim?: boolean;
|
|
116
149
|
}
|
|
117
150
|
rateLimit?: {
|
|
118
151
|
enabled?: boolean;
|
|
@@ -139,16 +172,37 @@ export type ServerConfig = {
|
|
|
139
172
|
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
140
173
|
/** Also write to the console (default: true) */
|
|
141
174
|
console?: boolean;
|
|
142
|
-
/** File logging: true → <dir>/
|
|
175
|
+
/** File logging: true → <dir>/app.log, string → custom path, false → disabled (default: true) */
|
|
143
176
|
file?: boolean | string;
|
|
144
177
|
/** Directory used when `file` is true (default: '.logs') */
|
|
145
178
|
dir?: string;
|
|
146
|
-
/**
|
|
179
|
+
/** Rotation trigger (default: 'size'): size | hourly | daily | weekly | monthly | yearly */
|
|
180
|
+
rotate?: 'size' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
181
|
+
/** Rotate when the log file exceeds this size in bytes — `rotate: 'size'` only (default: 10MB) */
|
|
147
182
|
maxSize?: number;
|
|
148
183
|
/** Keep this many rotated files (default: 5) */
|
|
149
184
|
maxFiles?: number;
|
|
150
185
|
/** Log database operations slower than this many ms (default: 200) */
|
|
151
186
|
slowQueryMs?: number;
|
|
187
|
+
/** HTTP access-log options (everything about requests) */
|
|
188
|
+
http?: {
|
|
189
|
+
/** Write the per-request access log (default: true) */
|
|
190
|
+
enabled?: boolean;
|
|
191
|
+
/** Capture raw request bodies in the access log (default: false) — may contain secrets, file only */
|
|
192
|
+
body?: boolean;
|
|
193
|
+
/** HTTP methods whose body is captured (default: GET, POST, PUT, PATCH, DELETE); [] disables it */
|
|
194
|
+
methods?: string[];
|
|
195
|
+
/** Max bytes captured per request body (default: 65536); beyond is truncated */
|
|
196
|
+
maxBodyBytes?: number;
|
|
197
|
+
/** @deprecated Use `logging.http.body`. */
|
|
198
|
+
captureBody?: boolean;
|
|
199
|
+
/** @deprecated Use `logging.http.methods`. */
|
|
200
|
+
captureMethods?: string[];
|
|
201
|
+
};
|
|
202
|
+
/** @deprecated Use `logging.http.captureBody`. */
|
|
203
|
+
captureBody?: boolean;
|
|
204
|
+
/** @deprecated Use `logging.http.maxBodyBytes`. */
|
|
205
|
+
maxBodyBytes?: number;
|
|
152
206
|
};
|
|
153
207
|
/** DB query caching (find + useCache) — driver + defaults for the query cache */
|
|
154
208
|
cache?: {
|
package/types/file.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type FileCollection = {
|
|
|
50
50
|
/** Allowed MIME types for upload.
|
|
51
51
|
* @example ['image/jpeg', 'image/png', 'application/pdf'] */
|
|
52
52
|
allowedMimeTypes?: string[];
|
|
53
|
-
/** Maximum file size in bytes. Default:
|
|
53
|
+
/** Maximum file size in bytes. Default: 100MB (a larger body than `server.body.maxSize` still hits the request body limit) */
|
|
54
54
|
maxSize?: number;
|
|
55
55
|
/**
|
|
56
56
|
* Image transformations (resize, format, etc.).
|
package/types/mcp.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type McpToolResult = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* MCP tool definition — auto-loaded from `{tenant.dir}/mcp/tools/**\/*.tool.ts`.
|
|
19
|
+
* MCP tool definition — auto-loaded from `{tenant.dir}/ai/mcp/tools/**\/*.tool.ts`.
|
|
20
20
|
* Served over the MCP protocol at `GET/POST /api/:tenant_id/mcp` (Streamable HTTP).
|
|
21
21
|
*/
|
|
22
22
|
export type McpTool = {
|
|
@@ -58,7 +58,7 @@ export type McpResourceResult = {
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* MCP resource — read-only data exposed by URI (like a file the LLM can open).
|
|
61
|
-
* Auto-loaded from `{tenant.dir}/mcp/resources/**\/*.resource.ts`.
|
|
61
|
+
* Auto-loaded from `{tenant.dir}/ai/mcp/resources/**\/*.resource.ts`.
|
|
62
62
|
*
|
|
63
63
|
* The `uri` can be static (`orders://summary`) or a template
|
|
64
64
|
* (`users://{id}`) — template params are extracted and passed to `read`.
|
package/utils/func.ts
CHANGED
|
@@ -912,6 +912,24 @@ function clone<T>(data: T): T {
|
|
|
912
912
|
const MAX_REGEX_LENGTH = 1000;
|
|
913
913
|
const MAX_IN_ARRAY_SIZE = 10000;
|
|
914
914
|
|
|
915
|
+
/** Filter operators a client must never reach MongoDB with (JS execution). */
|
|
916
|
+
const FORBIDDEN_MATCH_KEYS = ['$where', '$expr', '$accumulator', '$function'];
|
|
917
|
+
|
|
918
|
+
/** Bound and sanitize a raw Mongo filter (#45) — same rules as `find`.
|
|
919
|
+
* Used by internal methods that accept a filter directly (`findOneAndUpdate`,
|
|
920
|
+
* `bulkWrite`, `bulkUpdate`, `countDocuments`). */
|
|
921
|
+
function assertSafeFilter(filter: unknown): void {
|
|
922
|
+
if (filter === null || filter === undefined || typeof filter !== 'object') return;
|
|
923
|
+
const forbidden = hasUnauthorizedKeys(filter, FORBIDDEN_MATCH_KEYS);
|
|
924
|
+
if (forbidden.length > 0) {
|
|
925
|
+
throw new AppError('Unauthorized match keys: ' + forbidden.join(', '), {
|
|
926
|
+
code: 'UNAUTHORIZED_MATCH_KEYS',
|
|
927
|
+
status: 400,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
boundMatchOperators(filter);
|
|
931
|
+
}
|
|
932
|
+
|
|
915
933
|
function boundMatchOperators(node: unknown): void {
|
|
916
934
|
if (node === null || node === undefined) return;
|
|
917
935
|
|
|
@@ -1240,6 +1258,8 @@ function parseDuration(value: string | number): number | null {
|
|
|
1240
1258
|
}
|
|
1241
1259
|
|
|
1242
1260
|
export {
|
|
1261
|
+
assertSafeFilter,
|
|
1262
|
+
boundMatchOperators,
|
|
1243
1263
|
buildPipeline,
|
|
1244
1264
|
reorder,
|
|
1245
1265
|
omit,
|