@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/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-D7GdhqdK.js';
3
- export { d as AGENT_TOOL_BRAND, e as AgentAttachmentsOptions, f as AgentDeps, a as AgentSurface, F as FunctionalTool, g as delegateToolName, p as provideAgentTool, u as utcDay } from './agent-deps.factory-D7GdhqdK.js';
4
- import { AgentRunner, AgentStore, QuotaStore, Actor, MessageAttachment, PageContext, Decision, ThreadSummary, ThreadDetail, UpdateThreadInput, QuotaView, AgentApprovalPort, ActorResolver, AttachmentStagingStore, ToolRegistry, AgentRegistry, PromptContributor, AgentRunInput, TokenStreamSink, SinkWriter, QuotaState } from '@dudousxd/nestjs-agent-core';
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
- /** Files attached to this message (image/PDF) for a vision-capable model. */
21
- attachments?: MessageAttachment[];
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
- constructor(runner: AgentRunner, store: AgentStore, deps: AgentDepsFactory, quota: QuotaStore | undefined);
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, decision: Decision): Promise<void>;
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 thread lookup entirely when the caller already named an agent or there's no thread yet.
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 currently awaiting `toolCallId` (its thread's active stream). That run is the
103
- * sub-agent's own child run when the pending call belongs to a delegated agent — the client never
104
- * knows or supplies a runId; it is derived here from the tool call alone.
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 size cap + content-type allowlist, then handed
147
- * to the bound {@link AttachmentStagingStore} to persist and turn into a model-fetchable URL.
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
- /** Other `@Agent` classes this agent may hand off to (auto-exposed as handoff tools). */
250
- handoff?: Type[];
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
- /** Resolve each handoff `@Agent` class to its registered name (skipping non-agent classes). */
314
- private handoffNames;
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, decision: Decision): Promise<void>;
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 throws when `x-actor-id` is absent — it never fabricates an identity
388
- * and never grants a default role (an actor with no `x-actor-role` gets `roles: []`, i.e.
389
- * no tools). Trusting client headers is only safe behind a gateway that strips and re-sets
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 };