@dudousxd/nestjs-agent 0.11.1 → 0.12.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/README.md +60 -0
- package/dist/{agent-deps.factory-D7GdhqdK.d.cts → agent-deps.factory-EROMcBNh.d.cts} +182 -5
- package/dist/{agent-deps.factory-D7GdhqdK.d.ts → agent-deps.factory-EROMcBNh.d.ts} +182 -5
- package/dist/durable/index.cjs +1733 -497
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +78 -32
- package/dist/durable/index.d.ts +78 -32
- package/dist/durable/index.js +1673 -437
- package/dist/durable/index.js.map +1 -1
- package/dist/index.cjs +1349 -284
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +415 -26
- package/dist/index.d.ts +415 -26
- package/dist/index.js +1289 -234
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DynamicModule, OnModuleInit, Type, OnApplicationBootstrap } from '@nestjs/common';
|
|
2
|
-
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory } from './agent-deps.factory-
|
|
3
|
-
export {
|
|
4
|
-
import { AgentRunner, AgentStore, QuotaStore, Actor,
|
|
2
|
+
import { b as AgentModuleOptions, c as AgentModuleAsyncOptions, A as AgentDepsFactory, d as AgentSkillsOptions } from './agent-deps.factory-EROMcBNh.js';
|
|
3
|
+
export { e as AGENT_TOOL_BRAND, f as AgentAttachmentsOptions, g as AgentDeps, h as AgentMemoryOptions, a as AgentSurface, F as FunctionalTool, i as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-EROMcBNh.js';
|
|
4
|
+
import { AgentRunner, AgentStore, QuotaStore, AttachmentStagingStore, Actor, AttachmentRef, PageContext, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, StagedAttachment, QuotaView, AgentApprovalPort, ActorResolver, MessageAttachment, AgentHistoryWindow, AgentIntake, SkillContext, ToolRegistry, AgentRegistry, PromptContributor, SkillsConfig, SkillCatalogEntry, MemoryConfig, MemoryDigestEntry, SkillProvider, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
|
|
5
5
|
export * from '@dudousxd/nestjs-agent-core';
|
|
6
6
|
import { Request } from 'express';
|
|
7
7
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
@@ -17,8 +17,12 @@ interface ChatParams {
|
|
|
17
17
|
message: string;
|
|
18
18
|
threadId?: string;
|
|
19
19
|
agentName?: string;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Files attached to this message (image/PDF) for a vision-capable model, named by the `mediaId`
|
|
22
|
+
* the upload returned. Only the id is read: the url, content type and name are resolved from the
|
|
23
|
+
* bound {@link AttachmentStagingStore}, so nothing a caller says decides what the model fetches.
|
|
24
|
+
*/
|
|
25
|
+
attachments?: AttachmentRef[];
|
|
22
26
|
pageContext?: PageContext;
|
|
23
27
|
/** Re-run the last exchange instead of adding a new message. Requires an existing `threadId`. */
|
|
24
28
|
regenerate?: boolean;
|
|
@@ -28,20 +32,69 @@ interface ChatParams {
|
|
|
28
32
|
*/
|
|
29
33
|
transient?: boolean;
|
|
30
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* A store that can answer a thread's default agent on its own, without materializing the thread.
|
|
37
|
+
*
|
|
38
|
+
* Probed STRUCTURALLY rather than declared on `AgentStore`: the projection is an optimization a
|
|
39
|
+
* store either offers or doesn't, and a store that predates it still answers correctly through
|
|
40
|
+
* `getThread`. Every adapter in this repo implements it.
|
|
41
|
+
*/
|
|
42
|
+
interface ThreadDefaultAgentReader {
|
|
43
|
+
defaultAgentForThread(threadId: string): Promise<string | null>;
|
|
44
|
+
}
|
|
31
45
|
/** The orchestration facade the controllers call. */
|
|
32
46
|
declare class AgentService {
|
|
33
47
|
private readonly runner;
|
|
34
48
|
private readonly store;
|
|
35
49
|
private readonly deps;
|
|
36
50
|
private readonly quota;
|
|
37
|
-
|
|
51
|
+
private readonly staging?;
|
|
52
|
+
constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, quota: QuotaStore | undefined, staging?: AttachmentStagingStore | undefined);
|
|
38
53
|
chat(params: ChatParams): Promise<{
|
|
39
54
|
runId: string;
|
|
40
55
|
threadId: string;
|
|
41
56
|
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Rebuild this turn's attachments from the staging store, keyed by the ids the caller claimed.
|
|
59
|
+
* The model provider fetches {@link MessageAttachment.url} verbatim, so that url is only ever
|
|
60
|
+
* allowed to come from the host's own store — a caller that could name it could point the server
|
|
61
|
+
* at anything it can reach (cloud metadata, internal services) and read the response back out of
|
|
62
|
+
* the model's answer.
|
|
63
|
+
*
|
|
64
|
+
* No store bound means no way to tell an id the actor owns from one they don't, so a turn simply
|
|
65
|
+
* cannot carry attachments: 501 rather than trusting the request.
|
|
66
|
+
*/
|
|
67
|
+
private resolveAttachments;
|
|
68
|
+
/**
|
|
69
|
+
* The run's live token stream, WITHOUT an ownership check — for in-process callers that are
|
|
70
|
+
* already authorized (the MCP bridge, a host's own job code), the same split as
|
|
71
|
+
* {@link signalToolCall} vs {@link approve}. Anything reachable from a request must go through
|
|
72
|
+
* {@link subscribeAs} instead: a runId is the only thing standing between a caller and another
|
|
73
|
+
* actor's answer, tool arguments and tool results.
|
|
74
|
+
*/
|
|
42
75
|
subscribe(runId: string): AsyncIterable<Uint8Array>;
|
|
76
|
+
/**
|
|
77
|
+
* {@link subscribe}, gated the way {@link cancel} is: only the actor whose thread is currently
|
|
78
|
+
* streaming this run may read it. A run that has already finished is reported missing rather than
|
|
79
|
+
* replayed — the runner clears the thread's active stream as the run ends, and that field is what
|
|
80
|
+
* ownership is derived from.
|
|
81
|
+
*/
|
|
82
|
+
subscribeAs(actor: Actor, runId: string): Promise<AsyncIterable<Uint8Array>>;
|
|
43
83
|
approve(actor: Actor, toolCallId: string): Promise<void>;
|
|
44
84
|
reject(actor: Actor, toolCallId: string, reason?: string): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Answer a parked question set. An omitted question takes the request's own pre-picked default —
|
|
87
|
+
* resolved by the LOOP against the request it already holds, so "the user just submitted" and
|
|
88
|
+
* "the user picked exactly the defaults" persist as the same answers, and no client has to
|
|
89
|
+
* re-send what it was shown.
|
|
90
|
+
*/
|
|
91
|
+
answer(actor: Actor, toolCallId: string, answers?: Record<string, string[]>): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Decline to answer and let the agent proceed on its own assumptions. NOT the same as confirming
|
|
94
|
+
* them: the run records this as a rejection, so a reader auditing what the agent was told can
|
|
95
|
+
* tell a choice the user made from one they refused to make.
|
|
96
|
+
*/
|
|
97
|
+
skip(actor: Actor, toolCallId: string): Promise<void>;
|
|
45
98
|
/**
|
|
46
99
|
* The decision core behind approve/reject, WITHOUT an ownership check: resolves the run
|
|
47
100
|
* currently awaiting `toolCallId` and signals it. `approve`/`reject` above call this after their
|
|
@@ -50,7 +103,7 @@ declare class AgentService {
|
|
|
50
103
|
* re-deriving thread ownership here would reject a legitimate operator decision (an operator is
|
|
51
104
|
* not the thread's own actor).
|
|
52
105
|
*/
|
|
53
|
-
signalToolCall(toolCallId: string,
|
|
106
|
+
signalToolCall(toolCallId: string, reply: HumanReply): Promise<void>;
|
|
54
107
|
cancel(actor: Actor, runId: string): Promise<void>;
|
|
55
108
|
listThreads(actorRef: string): Promise<ThreadSummary[]>;
|
|
56
109
|
getThread(actor: Actor, threadId: string): Promise<ThreadDetail | null>;
|
|
@@ -69,9 +122,16 @@ declare class AgentService {
|
|
|
69
122
|
/**
|
|
70
123
|
* Agent-selection precedence for a `chat()` call with no explicit `agentName`: the thread's own
|
|
71
124
|
* `defaultAgent` (if the thread exists and one is set) wins over the module's configured default.
|
|
72
|
-
* Skips the
|
|
125
|
+
* Skips the store entirely when the caller already named an agent or there's no thread yet.
|
|
126
|
+
*
|
|
127
|
+
* What this needs is one nullable scalar, so it asks for one. `getThread` returns the entire
|
|
128
|
+
* transcript — every message, every persisted tool output — and this discards all of it; on a
|
|
129
|
+
* long thread with large tool results that is hundreds of kilobytes read per turn, outside the
|
|
130
|
+
* workflow, for a string. A store predating {@link ThreadDefaultAgentReader} still answers, via
|
|
131
|
+
* the full read.
|
|
73
132
|
*/
|
|
74
133
|
private resolveAgentName;
|
|
134
|
+
private threadDefaultAgent;
|
|
75
135
|
private toSummaryView;
|
|
76
136
|
private toDetailView;
|
|
77
137
|
promoteThread(actor: Actor, threadId: string): Promise<void>;
|
|
@@ -81,6 +141,41 @@ declare class AgentService {
|
|
|
81
141
|
* thread-scoped mutations.
|
|
82
142
|
*/
|
|
83
143
|
truncateThreadFrom(actor: Actor, threadId: string, messageId: string): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Every file this actor has staged, newest first — across threads, including uploads that were
|
|
146
|
+
* never sent, which nothing else on this surface can see.
|
|
147
|
+
*
|
|
148
|
+
* Metadata only. A url is minted per turn by `resolve` so it can be short-lived, and handing one
|
|
149
|
+
* back per entry would undo that for the price of rendering a list.
|
|
150
|
+
*/
|
|
151
|
+
listAttachments(actor: Actor, options?: {
|
|
152
|
+
limit?: number;
|
|
153
|
+
}): Promise<StagedAttachment[]>;
|
|
154
|
+
/**
|
|
155
|
+
* This actor's staged media that no live message references and that is old enough not to be an
|
|
156
|
+
* upload in flight — the candidate set for a sweep. Returns them; never deletes them. The bytes
|
|
157
|
+
* are the host's, and so is the decision.
|
|
158
|
+
*
|
|
159
|
+
* `olderThan` is required and has no default. How long a composer may sit open with a file
|
|
160
|
+
* attached is the host's knowledge, and a library-chosen grace period would eventually delete a
|
|
161
|
+
* file someone was about to send. The staging store is asked to apply it, and the result is
|
|
162
|
+
* filtered again here: a store that ignores the hint would otherwise turn this into exactly that
|
|
163
|
+
* bug, silently.
|
|
164
|
+
*
|
|
165
|
+
* Both halves must be answerable or this refuses. An unanswerable reference query means "cannot
|
|
166
|
+
* tell", and treating it as "nothing is referenced" would hand back every attachment the actor
|
|
167
|
+
* ever sent, marked safe to delete.
|
|
168
|
+
*/
|
|
169
|
+
collectableAttachments(actor: Actor, options: {
|
|
170
|
+
olderThan: Date;
|
|
171
|
+
limit?: number;
|
|
172
|
+
}): Promise<StagedAttachment[]>;
|
|
173
|
+
/**
|
|
174
|
+
* The bound staging store's `list`, or a refusal. Absence is never answered with an empty list:
|
|
175
|
+
* "this host keeps no inventory" and "this actor has no files" are opposite facts that would
|
|
176
|
+
* otherwise render identically — as a clean file list, and as a sweep that collects nothing.
|
|
177
|
+
*/
|
|
178
|
+
private attachmentInventory;
|
|
84
179
|
/**
|
|
85
180
|
* The day's usage for the badge: tokens + summed USD cost from the store, and the configured
|
|
86
181
|
* limit from the quota store (null → unlimited). `withinLimit` comes from the quota store so it
|
|
@@ -99,9 +194,11 @@ declare class AgentService {
|
|
|
99
194
|
*/
|
|
100
195
|
private assertOwnsToolCall;
|
|
101
196
|
/**
|
|
102
|
-
* Resolve the run
|
|
103
|
-
* sub-agent's own child run when the pending
|
|
104
|
-
*
|
|
197
|
+
* Resolve the run awaiting `toolCallId`. Derived server-side from the tool call alone — the client
|
|
198
|
+
* never knows or supplies a runId, and that run is the sub-agent's own child run when the pending
|
|
199
|
+
* call belongs to a delegated agent. The store answers from the CALL'S OWN `runId` where it has
|
|
200
|
+
* one; see `AgentStore.runForToolCall` for why that matters once a thread can hold more than one
|
|
201
|
+
* live run.
|
|
105
202
|
*/
|
|
106
203
|
private resolveRunForToolCall;
|
|
107
204
|
/** Authorization seam for `cancel`: the caller must own the thread currently streaming this run. */
|
|
@@ -135,6 +232,12 @@ declare class AgentApprovalPortAdapter implements AgentApprovalPort {
|
|
|
135
232
|
|
|
136
233
|
/** Default per-file size cap when `AgentModuleOptions.attachments.maxBytes` is omitted (20 MiB). */
|
|
137
234
|
declare const DEFAULT_MAX_ATTACHMENT_BYTES: number;
|
|
235
|
+
/**
|
|
236
|
+
* How many entries `GET /agent/attachments` returns. Fixed rather than caller-supplied: this route
|
|
237
|
+
* exists so a composer can show the files someone has uploaded, and a host that needs real paging
|
|
238
|
+
* over a large inventory has `AgentService.listAttachments` and its own store's `list` to page with.
|
|
239
|
+
*/
|
|
240
|
+
declare const ATTACHMENT_PAGE_SIZE = 50;
|
|
138
241
|
/**
|
|
139
242
|
* Default allowlist: what multimodal model providers commonly accept as native image/file parts.
|
|
140
243
|
*/
|
|
@@ -143,14 +246,22 @@ declare const DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES: readonly string[];
|
|
|
143
246
|
* Optional `POST /agent/attachments` upload surface — mounted only when `AgentModuleOptions
|
|
144
247
|
* .attachments.upload` (or the async config's `attachmentsUpload`) is `true` (see `agent.module.ts`).
|
|
145
248
|
* Multipart single file field `'file'`, buffered in memory (no disk write — `FileInterceptor`'s
|
|
146
|
-
* default storage), validated against the configured
|
|
147
|
-
* to the bound {@link AttachmentStagingStore} to
|
|
249
|
+
* default storage) up to {@link HARD_MAX_ATTACHMENT_BYTES}, then validated against the configured
|
|
250
|
+
* size cap + content-type allowlist and handed to the bound {@link AttachmentStagingStore} to
|
|
251
|
+
* persist and turn into a model-fetchable URL. Both size gates answer `413`: the ceiling aborts the
|
|
252
|
+
* upload mid-stream, the configured cap rejects what was read.
|
|
253
|
+
*
|
|
254
|
+
* `GET /agent/attachments` rides the same flag and returns the caller's own staged files as
|
|
255
|
+
* metadata. Collection is NOT a route: a sweep needs a host-chosen age threshold and ends in the
|
|
256
|
+
* host deleting bytes, so it stays an in-process call (`AgentService.collectableAttachments`)
|
|
257
|
+
* rather than something reachable with a session cookie.
|
|
148
258
|
*/
|
|
149
259
|
declare class AttachmentsController implements OnModuleInit {
|
|
150
260
|
private readonly options;
|
|
151
261
|
private readonly actorResolver;
|
|
262
|
+
private readonly agents;
|
|
152
263
|
private readonly staging;
|
|
153
|
-
constructor(options: AgentModuleOptions, actorResolver: ActorResolver, staging: AttachmentStagingStore | undefined);
|
|
264
|
+
constructor(options: AgentModuleOptions, actorResolver: ActorResolver, agents: AgentService, staging: AttachmentStagingStore | undefined);
|
|
154
265
|
/**
|
|
155
266
|
* `attachments.upload: true` is what mounts this controller at all (see `agent.module.ts`), so by
|
|
156
267
|
* the time an instance exists the host clearly intends uploads to work. Fail boot loudly rather
|
|
@@ -158,6 +269,16 @@ declare class AttachmentsController implements OnModuleInit {
|
|
|
158
269
|
* `AGENT_ATTACHMENT_STAGING`.
|
|
159
270
|
*/
|
|
160
271
|
onModuleInit(): void;
|
|
272
|
+
/**
|
|
273
|
+
* The caller's own staged files, newest first — the one question `GET /agent/threads/:id` cannot
|
|
274
|
+
* answer, since an upload that was never sent belongs to no thread.
|
|
275
|
+
*
|
|
276
|
+
* Scoped by the RESOLVED actor and nothing else. There is deliberately no `threadId` filter: a
|
|
277
|
+
* thread's attachments already ride on its messages in the thread payload, and re-serving them
|
|
278
|
+
* from here would add a second ownership path to get wrong for information the client already
|
|
279
|
+
* holds. Metadata only — see {@link StagedAttachment}.
|
|
280
|
+
*/
|
|
281
|
+
list(req: Request): Promise<StagedAttachment[]>;
|
|
161
282
|
upload(req: Request, file: Express.Multer.File | undefined): Promise<MessageAttachment>;
|
|
162
283
|
}
|
|
163
284
|
|
|
@@ -244,10 +365,77 @@ interface AgentOptions {
|
|
|
244
365
|
model?: string;
|
|
245
366
|
/** Max model→tool iterations for this agent's turn. Default 8. */
|
|
246
367
|
maxSteps?: number;
|
|
368
|
+
/**
|
|
369
|
+
* How deep delegation may nest below this agent before further hops are refused. Default 5.
|
|
370
|
+
*
|
|
371
|
+
* Not a budget for how MANY agents a turn delegates to — that is the model's, one tool call each,
|
|
372
|
+
* and nothing caps it. This bounds the chain a `delegatesTo` cycle would otherwise run forever.
|
|
373
|
+
*/
|
|
374
|
+
maxDelegationDepth?: number;
|
|
375
|
+
/**
|
|
376
|
+
* How many times one agent may appear on a single delegation chain below this one. Default 1.
|
|
377
|
+
*
|
|
378
|
+
* This is the exact form of what {@link maxDelegationDepth} approximates: the loop compares a
|
|
379
|
+
* delegation target against the chain that reached it, so a repeat is a cycle by inspection.
|
|
380
|
+
* It counts APPEARANCES, so 2 admits exactly one deliberate return to an earlier agent.
|
|
381
|
+
*/
|
|
382
|
+
maxAgentAppearances?: number;
|
|
247
383
|
/** Allow-list of global tool names this agent may use. Omit → every tool its role allows. */
|
|
248
384
|
tools?: string[];
|
|
249
|
-
/**
|
|
250
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Other `@Agent` classes this agent may hand off to (auto-exposed as handoff tools). A bare class
|
|
387
|
+
* is the delegation that has always existed: this agent's turn waits for the answer.
|
|
388
|
+
*
|
|
389
|
+
* `{ agent, detached: true }` is the other shape — the delegate is STARTED and this agent's turn
|
|
390
|
+
* ends without its answer, which arrives in the conversation later as its own message. Declared
|
|
391
|
+
* per edge, by the author, and never by the model: the same worker is worth waiting for in one
|
|
392
|
+
* orchestrator and worth backgrounding in another, and only the person wiring the edge knows
|
|
393
|
+
* which side of that the user is sitting on.
|
|
394
|
+
*/
|
|
395
|
+
handoff?: (Type | {
|
|
396
|
+
agent: Type;
|
|
397
|
+
detached?: boolean;
|
|
398
|
+
})[];
|
|
399
|
+
/**
|
|
400
|
+
* How much of the thread this agent's turns carry, overriding `AgentModule.forRoot({ history })`.
|
|
401
|
+
* `{ maxMessages }` and/or `{ maxTokens }` keep the newest that fit; `{ summarize: true }` folds
|
|
402
|
+
* the rest into a leading summary, at one extra model call per run. Omit → the module-wide
|
|
403
|
+
* ceiling, or none.
|
|
404
|
+
*/
|
|
405
|
+
history?: AgentHistoryWindow;
|
|
406
|
+
/**
|
|
407
|
+
* Constrain this agent's final answer to a schema (any [Standard Schema](https://standardschema.dev)
|
|
408
|
+
* — Zod, Valibot, ArkType). The turn calls its tools exactly as it would without one; the schema is
|
|
409
|
+
* satisfied by one extra formatting call afterwards, so it works on providers that refuse a
|
|
410
|
+
* response format and a tool set in the same request. The validated value comes back as `object`
|
|
411
|
+
* on the run's result and is recorded on the assistant message as a `structured_output` tool call.
|
|
412
|
+
*
|
|
413
|
+
* Declared HERE rather than per request: a schema is a live object, and `AgentRunInput` crosses a
|
|
414
|
+
* JSON boundary on its way into a durable workflow.
|
|
415
|
+
*/
|
|
416
|
+
outputSchema?: StandardSchemaV1;
|
|
417
|
+
/**
|
|
418
|
+
* Extra model calls allowed to fix an answer that failed {@link outputSchema}, each shown the
|
|
419
|
+
* previous attempt's validation issues. Omit → 1; `0` → fail on the first invalid reply with a
|
|
420
|
+
* `StructuredOutputError`.
|
|
421
|
+
*/
|
|
422
|
+
outputRepairAttempts?: number;
|
|
423
|
+
/**
|
|
424
|
+
* Questions this agent puts to the user BEFORE it starts working — the scope it needs, collected
|
|
425
|
+
* up front rather than guessed. Authored here, so the turn spends no model call producing them
|
|
426
|
+
* and a client knows the total ("Question 1 of 3") the moment the form appears. Every question
|
|
427
|
+
* should pre-pick a default: the claim the surface makes is that confirming is enough.
|
|
428
|
+
*
|
|
429
|
+
* `when: 'thread-start'` (the default) asks once per thread; `'every-turn'` asks before each turn.
|
|
430
|
+
* Omit → no intake.
|
|
431
|
+
*/
|
|
432
|
+
intake?: AgentIntake;
|
|
433
|
+
/**
|
|
434
|
+
* Let the model ask its OWN clarifying question set, via the built-in `ask` tool, when it judges
|
|
435
|
+
* the scope is missing. Overrides `AgentModule.forRoot({ ask })` for this agent. Omit → the
|
|
436
|
+
* module-wide setting.
|
|
437
|
+
*/
|
|
438
|
+
ask?: boolean;
|
|
251
439
|
}
|
|
252
440
|
declare const AGENT_METADATA: unique symbol;
|
|
253
441
|
/** Marks a provider class as an agent. See {@link AgentOptions}. */
|
|
@@ -259,6 +447,49 @@ declare function Agent(options: AgentOptions): ClassDecorator;
|
|
|
259
447
|
*/
|
|
260
448
|
declare function readAgentMetadata(target: Type | Function): AgentOptions | undefined;
|
|
261
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Declares a provider class as a skill: an authored procedure any agent may pull in when a task
|
|
452
|
+
* calls for it. Discovered at boot and offered to every turn whose scopes include its `scope`.
|
|
453
|
+
*
|
|
454
|
+
* NOT AN AGENT. An `@Agent` is who is answering — its model, its tools, its history ceiling. A skill
|
|
455
|
+
* is how one task is done, and carries none of those: what it contributes is text, at a scope, under
|
|
456
|
+
* a name the model can ask for. An instruction that should apply to EVERY turn of an agent is that
|
|
457
|
+
* agent's `systemPrompt` (or a `@SystemPromptContributor()`), not a skill — a skill is for the ones
|
|
458
|
+
* that should apply only when the work calls for them, which is what stops them costing a prompt.
|
|
459
|
+
*
|
|
460
|
+
* The class is an ordinary provider, so it gets constructor DI — the reason a skill is a class and
|
|
461
|
+
* not a config entry. The body is either the flat `body` string here or a `body(ctx)` method on the
|
|
462
|
+
* class (which may read the turn's {@link SkillContext} and inject services); implement
|
|
463
|
+
* {@link SkillBody} to have the compiler hold you to its shape.
|
|
464
|
+
*/
|
|
465
|
+
interface SkillOptions {
|
|
466
|
+
/** Unique within its scope. What the model passes to the `skill` tool, and the `/` handle a UI offers. */
|
|
467
|
+
name: string;
|
|
468
|
+
/**
|
|
469
|
+
* One line: what task this covers. Read by the MODEL to decide whether to load it, and by a person
|
|
470
|
+
* choosing from an autocomplete — so write what it is FOR, not what it contains.
|
|
471
|
+
*/
|
|
472
|
+
description: string;
|
|
473
|
+
/**
|
|
474
|
+
* The scope token this skill is published at. Omit → `'global'`, the deployment-wide default. Any
|
|
475
|
+
* string a host's `ScopeResolver` can return works: `'tenant:base-7'`, `'sector:logistics'`.
|
|
476
|
+
*/
|
|
477
|
+
scope?: string;
|
|
478
|
+
/** A flat body. For one built per actor or per page, add a `body()` method instead. */
|
|
479
|
+
body?: string;
|
|
480
|
+
}
|
|
481
|
+
/** The shape a `@Skill` class's body method takes. Implement it to be held to it at compile time. */
|
|
482
|
+
interface SkillBody {
|
|
483
|
+
body(ctx: SkillContext): string | Promise<string>;
|
|
484
|
+
}
|
|
485
|
+
declare const SKILL_METADATA: unique symbol;
|
|
486
|
+
/** Marks a provider class as a skill. See {@link SkillOptions}. */
|
|
487
|
+
declare function Skill(options: SkillOptions): ClassDecorator;
|
|
488
|
+
/** Reads {@link SkillOptions} off a class, or `undefined` if it is not a `@Skill`. */
|
|
489
|
+
declare function readSkillMetadata(target: Type | Function): SkillOptions | undefined;
|
|
490
|
+
/** The scope a `@Skill` publishes at — its own, or the deployment-wide default. */
|
|
491
|
+
declare function skillScope(options: SkillOptions): string;
|
|
492
|
+
|
|
262
493
|
/**
|
|
263
494
|
* Marks the method on an `@Agent` class that builds the agent's base system prompt. The method
|
|
264
495
|
* receives the turn's `PromptContext` and returns a string (optionally async); because it lives on a
|
|
@@ -310,8 +541,12 @@ declare class AgentDiscoveryService implements OnModuleInit {
|
|
|
310
541
|
onModuleInit(): void;
|
|
311
542
|
/** Build the internal {@link AgentDefinition} from an `@Agent` instance and its options. */
|
|
312
543
|
private definitionFrom;
|
|
313
|
-
/**
|
|
314
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Resolve each handoff entry to an {@link AgentDelegation} — its target's registered name, plus
|
|
546
|
+
* whether that edge detaches (skipping non-agent classes). A bare name is kept for a plain edge
|
|
547
|
+
* rather than an always-object form, so a definition an older host built by hand still reads.
|
|
548
|
+
*/
|
|
549
|
+
private handoffEdges;
|
|
315
550
|
/** Push every `@SystemPromptContributor()` method on `instance` (bound) into the contributor list. */
|
|
316
551
|
private collectContributors;
|
|
317
552
|
/** The single method on `instance` carrying `metadataKey`, bound to the instance, or undefined. */
|
|
@@ -320,6 +555,114 @@ declare class AgentDiscoveryService implements OnModuleInit {
|
|
|
320
555
|
private boundMethod;
|
|
321
556
|
}
|
|
322
557
|
|
|
558
|
+
/**
|
|
559
|
+
* One `@Skill`-decorated provider, reduced to what a `SkillProvider` needs: its catalog line, and a
|
|
560
|
+
* bound function that produces its body for a turn.
|
|
561
|
+
*/
|
|
562
|
+
interface DeclaredSkill {
|
|
563
|
+
name: string;
|
|
564
|
+
description: string;
|
|
565
|
+
scope: string;
|
|
566
|
+
body(ctx: SkillContext): string | Promise<string>;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Collects every `@Skill`-decorated provider into the shared list the module's skill provider reads
|
|
570
|
+
* — the discovery counterpart to authoring skills as rows in the host's own table. Both sources feed
|
|
571
|
+
* one catalog; see `AgentModuleOptions.skills`.
|
|
572
|
+
*
|
|
573
|
+
* A class with neither a flat `body` nor a `body()` method is skipped with a warning rather than
|
|
574
|
+
* offered: a skill the model can see and load, that then returns nothing, reads to it as an
|
|
575
|
+
* instruction to do nothing in particular — which is worse than the skill not existing.
|
|
576
|
+
*/
|
|
577
|
+
declare class SkillDiscoveryService implements OnModuleInit {
|
|
578
|
+
private readonly discovery;
|
|
579
|
+
private readonly declared;
|
|
580
|
+
private readonly logger;
|
|
581
|
+
constructor(discovery: DiscoveryService, declared: DeclaredSkill[]);
|
|
582
|
+
onModuleInit(): void;
|
|
583
|
+
/** The instance's `body()` method (bound), else the flat string, else nothing to offer. */
|
|
584
|
+
private bodyOf;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Lists the skills available to THIS caller right now, scope-resolved — so a composer can offer them
|
|
589
|
+
* after a `/` instead of hardcoding a list, and a settings screen can show which scope each came
|
|
590
|
+
* from.
|
|
591
|
+
*
|
|
592
|
+
* THE SAME LIST THE MODEL IS OFFERED, built by the same call (`offerSkills`) against the same
|
|
593
|
+
* provider and the same resolver. A second implementation of "which skills apply" would drift the
|
|
594
|
+
* first time either was edited, and the drift would read to a user as a skill they can invoke that
|
|
595
|
+
* the agent then says it has never heard of.
|
|
596
|
+
*
|
|
597
|
+
* Ownership posture mirrors `GET /agent/agents`: a plain array, no envelope, and nothing the caller
|
|
598
|
+
* can pass to widen it — the actor comes from the resolver, never from a parameter.
|
|
599
|
+
*/
|
|
600
|
+
declare class SkillsController {
|
|
601
|
+
private readonly actorResolver;
|
|
602
|
+
private readonly skills;
|
|
603
|
+
constructor(actorResolver: ActorResolver, skills: SkillsConfig | undefined);
|
|
604
|
+
list(req: Request,
|
|
605
|
+
/**
|
|
606
|
+
* The thread this list is for. Only ever handed to the host's own resolver and provider, which
|
|
607
|
+
* may scope a skill to a conversation; nothing here reads the thread, so an unknown id widens
|
|
608
|
+
* nothing. Omitted → the empty string, the same value a turn on a brand-new thread carries.
|
|
609
|
+
*/
|
|
610
|
+
threadId?: string): Promise<SkillCatalogEntry[]>;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* What the assistant believes about THIS caller — and how they take one of those beliefs back.
|
|
615
|
+
*
|
|
616
|
+
* WHY THIS EXISTS WHERE `GET /agent/skills` HAS NO WRITE SIBLING. A skill is authored by a person
|
|
617
|
+
* who already knows it exists; a memory is written by the agent, about someone who does not. So the
|
|
618
|
+
* read-back is not a convenience on top of the feature, it IS half of the feature: a belief nobody
|
|
619
|
+
* can inspect is one nobody can correct, and a belief nobody can delete is one the deployment keeps
|
|
620
|
+
* whether or not it is true. That is also why `MemoryProvider.forget` is required rather than
|
|
621
|
+
* optional — see `memory.ts`.
|
|
622
|
+
*/
|
|
623
|
+
declare class MemoriesController {
|
|
624
|
+
private readonly actorResolver;
|
|
625
|
+
private readonly memory;
|
|
626
|
+
constructor(actorResolver: ActorResolver, memory: MemoryConfig | undefined);
|
|
627
|
+
/**
|
|
628
|
+
* Every memory this actor can reach, most specific first, each carrying its origin and whatever
|
|
629
|
+
* wider value it outranks.
|
|
630
|
+
*
|
|
631
|
+
* DELIBERATELY IGNORES `maxMemories`. That ceiling is a budget on what one TURN carries; applying
|
|
632
|
+
* it here would mean a person could not see — and so could not delete — a belief the assistant is
|
|
633
|
+
* one write away from acting on again. Showing someone more than the model sees is harmless;
|
|
634
|
+
* showing them less is the failure this endpoint exists to prevent.
|
|
635
|
+
*/
|
|
636
|
+
list(req: Request,
|
|
637
|
+
/** Only ever handed to the host's own resolver and provider; nothing here reads the thread. */
|
|
638
|
+
threadId?: string): Promise<MemoryDigestEntry[]>;
|
|
639
|
+
/**
|
|
640
|
+
* Forget one. Authorized against the actor's OWN resolved list, the same way a `skill` load is
|
|
641
|
+
* authorized against the turn's catalog: an id this actor cannot see is answered as missing rather
|
|
642
|
+
* than refused, so the endpoint cannot be used to find out which memories exist about other people.
|
|
643
|
+
*/
|
|
644
|
+
forget(req: Request, id: string): Promise<{
|
|
645
|
+
forgotten: boolean;
|
|
646
|
+
}>;
|
|
647
|
+
private unbounded;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* The `@Skill`-decorated classes as a `SkillProvider`. Reads `declared` on every call rather than
|
|
652
|
+
* capturing its contents: DI builds the provider before `SkillDiscoveryService` has filled the list.
|
|
653
|
+
*/
|
|
654
|
+
declare function declaredSkillProvider(declared: readonly DeclaredSkill[]): SkillProvider;
|
|
655
|
+
/**
|
|
656
|
+
* The turn's skills seam, or `undefined` where the host configured none — which is also what keeps
|
|
657
|
+
* a deployment that declares no skills from spending the `skills:catalog` checkpoint.
|
|
658
|
+
*
|
|
659
|
+
* DISCOVERED `@Skill` CLASSES ALONE DO NOT TURN SKILLS ON. Adding a decorated class would otherwise
|
|
660
|
+
* insert a checkpoint into every turn, and a run already in flight when that deployment rolled would
|
|
661
|
+
* replay against a sequence one position short. `skills: {}` is the opt-in; a class discovered
|
|
662
|
+
* without it is reported at boot rather than silently inert.
|
|
663
|
+
*/
|
|
664
|
+
declare function resolveSkillsConfig(options: AgentSkillsOptions | undefined, declared: readonly DeclaredSkill[]): SkillsConfig | undefined;
|
|
665
|
+
|
|
323
666
|
/**
|
|
324
667
|
* Runs the agent turn in-process. HITL approval resolves a pending promise keyed by
|
|
325
668
|
* `runId:toolCallId`. Sub-agent delegation runs a nested loop that streams into the top-level run's
|
|
@@ -332,13 +675,57 @@ declare class InlineAgentRunner implements AgentRunner {
|
|
|
332
675
|
private readonly store;
|
|
333
676
|
private readonly logger;
|
|
334
677
|
private readonly pending;
|
|
678
|
+
/**
|
|
679
|
+
* Runs someone has asked to stop. In-process, like `pending`, and for the same reason: this runner
|
|
680
|
+
* is single-replica by construction, so the request is readable by the only loop that could be
|
|
681
|
+
* running it. A cancelled id is dropped once the run settles, so the set tracks live runs only.
|
|
682
|
+
*/
|
|
683
|
+
private readonly cancelled;
|
|
335
684
|
constructor(factory: AgentDepsFactory, store: AgentStore);
|
|
336
685
|
start(input: AgentRunInput): Promise<{
|
|
337
686
|
runId: string;
|
|
338
687
|
}>;
|
|
339
|
-
signal(runId: string, toolCallId: string,
|
|
688
|
+
signal(runId: string, toolCallId: string, reply: HumanReply): Promise<void>;
|
|
689
|
+
/**
|
|
690
|
+
* Ask a run to stop, and settle whatever it was parked on.
|
|
691
|
+
*
|
|
692
|
+
* The loop observes the request at its next safe point (between steps, before the turn's tools) and
|
|
693
|
+
* unwinds from there — so a tool already executing is left to finish, and its result is recorded
|
|
694
|
+
* exactly as it would have been. What this cannot wait for is a turn parked on a human: that wait
|
|
695
|
+
* is a promise nobody is going to resolve, so it is REJECTED here with the same
|
|
696
|
+
* {@link RunCancelledError} the loop would have thrown, and the run unwinds through the identical
|
|
697
|
+
* path.
|
|
698
|
+
*
|
|
699
|
+
* Settlement — the run row, the thread's active stream, the stream's terminal — belongs to the
|
|
700
|
+
* `RunCancelledError` catch in `start`, so a cancel and an ordinary failure settle in one place
|
|
701
|
+
* each and can never both write.
|
|
702
|
+
*/
|
|
340
703
|
cancel(runId: string): Promise<void>;
|
|
704
|
+
/**
|
|
705
|
+
* The single place a cancelled run's state is settled: recorded as its own terminal (never
|
|
706
|
+
* `failed` — a user pressing Stop must not land in someone's error rate), the thread released, and
|
|
707
|
+
* the stream ENDED after a `cancelled` frame rather than failed, so a client that retries failed
|
|
708
|
+
* streams does not retry this one.
|
|
709
|
+
*/
|
|
710
|
+
private settleCancelled;
|
|
711
|
+
/**
|
|
712
|
+
* Park a run on a human, keyed by run + tool call. One map for approvals and for question-set
|
|
713
|
+
* answers alike: both wait on the same key, and `signal` cannot tell (or need to tell) which
|
|
714
|
+
* shape it is delivering — the loop asked for one and only ever gets what the caller sent. A
|
|
715
|
+
* cancel settles the wait too, by rejecting it.
|
|
716
|
+
*/
|
|
717
|
+
private park;
|
|
341
718
|
private topLevelHooks;
|
|
719
|
+
/**
|
|
720
|
+
* Delegate WITHOUT waiting: a nested loop nobody awaits, on its own thread and its own sink, which
|
|
721
|
+
* posts its answer back into the delegating thread when it lands (`deliverTo`, settled by the
|
|
722
|
+
* loop). The calling turn gets the run id straight back and finishes.
|
|
723
|
+
*
|
|
724
|
+
* No `sinkRunId`, unlike {@link runNested}: forwarding into the stream the human is watching would
|
|
725
|
+
* write into a turn that has already ended. Its own approvals resolve through the shared pending
|
|
726
|
+
* map keyed by its own runId — which is what the pending-approvals surface routes to.
|
|
727
|
+
*/
|
|
728
|
+
private startDetached;
|
|
342
729
|
/**
|
|
343
730
|
* Delegate to another agent as a nested in-process run. The sub-agent streams into `sinkRunId`
|
|
344
731
|
* (the top-level run the human is watching) so its output and any pending action tool are visible,
|
|
@@ -377,21 +764,23 @@ declare class LedgerQuotaStore implements QuotaStore {
|
|
|
377
764
|
private readonly limitTokens;
|
|
378
765
|
constructor(store: AgentStore, limitTokens: number);
|
|
379
766
|
check(actorRef: string, day: string): Promise<QuotaState>;
|
|
380
|
-
bump(): Promise<void>;
|
|
767
|
+
bump(_actorRef: string, _day: string, _tokens: number): Promise<void>;
|
|
381
768
|
}
|
|
382
769
|
|
|
383
770
|
/**
|
|
384
771
|
* A development / gateway {@link ActorResolver} that trusts request headers:
|
|
385
772
|
* `x-actor-id` (required), `x-actor-role` (comma-separated → `roles`), `x-tenant-ref`.
|
|
386
773
|
*
|
|
387
|
-
* **Security:** it
|
|
388
|
-
* and never grants a default role (an actor with no `x-actor-role` gets
|
|
389
|
-
* no tools). Trusting client headers is only safe behind a gateway that strips
|
|
390
|
-
* them from an authenticated principal. Real deployments should provide their own resolver
|
|
391
|
-
* that reads a verified session/JWT instead of installing this one.
|
|
774
|
+
* **Security:** it rejects with `UnauthorizedException` when `x-actor-id` is absent — it never
|
|
775
|
+
* fabricates an identity, and never grants a default role (an actor with no `x-actor-role` gets
|
|
776
|
+
* `roles: []`, i.e. no tools). Trusting client headers is only safe behind a gateway that strips
|
|
777
|
+
* and re-sets them from an authenticated principal. Real deployments should provide their own resolver
|
|
778
|
+
* that reads a verified session/JWT instead of installing this one. A custom resolver should
|
|
779
|
+
* reject the same way: a plain `Error` surfaces to the caller as a 500 with a logged stack, which
|
|
780
|
+
* both misreports an unauthenticated request and turns anonymous probes into error noise.
|
|
392
781
|
*/
|
|
393
782
|
declare class HeaderActorResolver implements ActorResolver {
|
|
394
783
|
resolve(req: unknown): Actor;
|
|
395
784
|
}
|
|
396
785
|
|
|
397
|
-
export { AGENT_METADATA, AI_TOOL_METADATA, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AiTool, AiToolDiscoveryService, type AiToolOptions, AttachmentsController, type ChatParams, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, LedgerQuotaStore, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, SystemPrompt, SystemPromptContributor, readAgentMetadata, readAiToolMetadata };
|
|
786
|
+
export { AGENT_METADATA, AI_TOOL_METADATA, ATTACHMENT_PAGE_SIZE, Agent, AgentApprovalPortAdapter, AgentDepsFactory, AgentDiscoveryService, AgentModule, AgentModuleAsyncOptions, AgentModuleOptions, type AgentOptions, AgentService, AgentSkillsOptions, AiTool, AiToolDiscoveryService, type AiToolOptions, AttachmentsController, type ChatParams, DEFAULT_ALLOWED_ATTACHMENT_CONTENT_TYPES, DEFAULT_MAX_ATTACHMENT_BYTES, type DeclaredSkill, HeaderActorResolver, InProcessTokenStreamSink, InlineAgentRunner, LedgerQuotaStore, MemoriesController, SKILL_METADATA, SYSTEM_PROMPT_CONTRIBUTOR_METADATA, SYSTEM_PROMPT_METADATA, Skill, type SkillBody, SkillDiscoveryService, type SkillOptions, SkillsController, SystemPrompt, SystemPromptContributor, type ThreadDefaultAgentReader, declaredSkillProvider, readAgentMetadata, readAiToolMetadata, readSkillMetadata, resolveSkillsConfig, skillScope };
|