@dudousxd/nestjs-agent-core 0.3.1 → 0.3.3

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.cts CHANGED
@@ -107,12 +107,30 @@ interface Decision {
107
107
  reason?: string;
108
108
  }
109
109
  type MessageRole = 'user' | 'assistant' | 'system';
110
+ /**
111
+ * A file a user attached to a message so a vision-capable model sees it natively (an image, a PDF).
112
+ * The lib stays provider-agnostic: it passes {@link MessageAttachment.url} straight through as the
113
+ * model's image/file part data — making that URL reachable by the provider (presigned S3, a proxy)
114
+ * is the consumer's job. The lib never fetches bytes or talks to a store.
115
+ */
116
+ interface MessageAttachment {
117
+ /** Stable id of the stored media object in the consumer's media store. Provenance + replay key. */
118
+ mediaId: string;
119
+ /** A URL the model provider can fetch the bytes from at turn time. */
120
+ url: string;
121
+ /** MIME type — routes the part: `image/*` → image part, otherwise → file part. */
122
+ contentType: string;
123
+ /** Original filename, for display and the file part's filename. */
124
+ name: string;
125
+ }
110
126
  /** A neutral chat message exchanged with the model. */
111
127
  interface ModelMessage {
112
128
  role: MessageRole;
113
129
  content: string;
114
130
  toolCalls?: ToolCallRequest[];
115
131
  toolResults?: ToolResult[];
132
+ /** User-message attachments (image/PDF), rendered as native model content parts by the adapter. */
133
+ attachments?: MessageAttachment[];
116
134
  }
117
135
  interface PageContext {
118
136
  kind?: string;
@@ -148,6 +166,8 @@ interface AgentRunInput {
148
166
  actor: Actor;
149
167
  /** The latest user message text. */
150
168
  userText: string;
169
+ /** Files attached to the latest user message (image/PDF). Persisted with it and sent to the model. */
170
+ attachments?: MessageAttachment[];
151
171
  pageContext?: PageContext;
152
172
  /** YYYY-MM-DD stamped by the runner so quota/day stays deterministic under durable replay. */
153
173
  day?: string;
@@ -181,6 +201,8 @@ interface AgentRunInput {
181
201
  */
182
202
  interface AgentDefinition {
183
203
  name: string;
204
+ /** Human-readable summary from `@Agent({ description })`. Surfaced by the `GET agents` catalog. */
205
+ description?: string;
184
206
  /** Base prompt for this agent. A flat string, or a {@link PromptBuilder} resolved per turn. */
185
207
  systemPrompt?: string | PromptBuilder;
186
208
  /** Allow-list of tool names this agent may use (subset of all registered tools). */
@@ -190,6 +212,16 @@ interface AgentDefinition {
190
212
  modelId?: string;
191
213
  maxSteps?: number;
192
214
  }
215
+ /**
216
+ * The read-model the `GET agents` endpoint returns to a client — the safe public subset of an
217
+ * {@link AgentDefinition} so a host can render a persona picker instead of hardcoding one.
218
+ */
219
+ interface AgentCatalogEntry {
220
+ name: string;
221
+ description: string;
222
+ /** Whether this is the agent a turn uses when the caller names none. Omitted when not the default. */
223
+ isDefault?: boolean;
224
+ }
193
225
  interface ThreadSummary {
194
226
  id: string;
195
227
  title: string;
@@ -206,6 +238,8 @@ interface StoredMessage {
206
238
  agentName?: string;
207
239
  toolCalls?: ToolCallRequest[];
208
240
  toolResults?: ToolResult[];
241
+ /** Files the user attached to this message (image/PDF). Persisted with the message, replayed as-is. */
242
+ attachments?: MessageAttachment[];
209
243
  followUps?: string[];
210
244
  usage?: MessageUsage;
211
245
  createdAt: string;
@@ -359,6 +393,54 @@ interface ModelProvider {
359
393
  runTurn(args: ModelTurnArgs): Promise<ModelTurnResult>;
360
394
  }
361
395
 
396
+ /**
397
+ * The structured live-stream vocabulary carried over the {@link SinkWriter} byte channel.
398
+ *
399
+ * The model turn (via the AI-SDK adapter) and the agent loop write these events as NDJSON — one
400
+ * `JSON.stringify(event)\n` per {@link SinkWriter.write}. The HTTP layer forwards each line as an
401
+ * SSE `data:` frame, and the client transport maps them back to the AI SDK UI-message chunk
402
+ * protocol so the browser renders text, reasoning, and tool cards (input + output) LIVE — the same
403
+ * rich rendering a raw `streamText().toUIMessageStream()` would give, but reconstructed on the
404
+ * client so the sink stays a format-agnostic byte buffer (durable buffering/replay is untouched).
405
+ *
406
+ * Keeping this vocabulary neutral (not AI-SDK `UIMessageChunk`) means core never depends on `ai`:
407
+ * the adapter owns model-parts → event, the transport owns event → UI-chunk.
408
+ */
409
+ type AgentStreamEvent = {
410
+ kind: 'step-start';
411
+ } | {
412
+ kind: 'step-finish';
413
+ } | {
414
+ kind: 'text';
415
+ text: string;
416
+ } | {
417
+ kind: 'reasoning';
418
+ text: string;
419
+ } | {
420
+ kind: 'tool-input-start';
421
+ id: string;
422
+ name: string;
423
+ } | {
424
+ kind: 'tool-input-delta';
425
+ id: string;
426
+ delta: string;
427
+ } | {
428
+ kind: 'tool-input-available';
429
+ id: string;
430
+ name: string;
431
+ input: unknown;
432
+ } | {
433
+ kind: 'tool-output';
434
+ id: string;
435
+ output: unknown;
436
+ } | {
437
+ kind: 'tool-output-error';
438
+ id: string;
439
+ error: string;
440
+ };
441
+ /** Encode one event as an NDJSON line (`{...}\n`) for {@link SinkWriter.write}. */
442
+ declare function encodeStreamEvent(event: AgentStreamEvent): Uint8Array;
443
+
362
444
  interface CreateThreadInput {
363
445
  actor: Actor;
364
446
  transient?: boolean;
@@ -372,6 +454,8 @@ interface AppendMessageInput {
372
454
  agentName?: string;
373
455
  toolCalls?: ToolCallRequest[];
374
456
  toolResults?: ToolResult[];
457
+ /** Files the user attached to this message (image/PDF). Persisted verbatim. */
458
+ attachments?: MessageAttachment[];
375
459
  followUps?: string[];
376
460
  usage?: MessageUsage;
377
461
  }
@@ -592,11 +676,12 @@ interface AgentRunner {
592
676
  * an identity is never invented from a default. See `HeaderActorResolver` for a header-based
593
677
  * resolver suitable for demos and gateways that strip/re-set the headers.
594
678
  *
595
- * `req` is the transport request object, typed `unknown` to keep core framework-agnostic;
596
- * a NestJS/Express app receives the express `Request`.
679
+ * `req` is the transport request object. Defaults to `unknown` to keep core framework-agnostic
680
+ * (a NestJS/Express app receives the express `Request`) — a host may narrow it via
681
+ * `ActorResolver<Request>` instead of writing its own `unknown`-narrowing type guard.
597
682
  */
598
- interface ActorResolver {
599
- resolve(req: unknown): Actor | Promise<Actor>;
683
+ interface ActorResolver<TReq = unknown> {
684
+ resolve(req: TReq): Actor | Promise<Actor>;
600
685
  }
601
686
 
602
687
  /**
@@ -682,6 +767,74 @@ interface AgentGovernanceQueries {
682
767
  recentThreads(limit: number): Promise<ThreadActivityRow[]>;
683
768
  }
684
769
 
770
+ /**
771
+ * The pure aggregation core shared by every {@link import('../spi/governance-queries.js').AgentGovernanceQueries}
772
+ * adapter (MikroORM, Drizzle, in-memory). An adapter's only job is the DB-specific row fetch; the
773
+ * cost formula, the group→sort bucketing, and the day-bounds math live here so all three surfaces
774
+ * report identical numbers.
775
+ */
776
+ /** The current per-1M token prices for one model; cache rates fall back to the input rate. */
777
+ interface ModelPrice {
778
+ inputPricePer1m: number;
779
+ outputPricePer1m: number;
780
+ cacheWritePricePer1m?: number | null;
781
+ cacheReadPricePer1m?: number | null;
782
+ }
783
+ /** The token split of one turn needed to estimate its cost. */
784
+ interface CostUsage {
785
+ inputTokens: number;
786
+ outputTokens: number;
787
+ cacheWriteTokens?: number | null | undefined;
788
+ cacheReadTokens?: number | null | undefined;
789
+ }
790
+ /**
791
+ * A normalized usage row the bucketers consume. Each adapter maps its raw DB row into this shape
792
+ * (MikroORM reads `thread.id` + `createdAt`, Drizzle/in-memory read the flat `threadId` + `day`).
793
+ */
794
+ interface GovernanceUsageInput extends CostUsage {
795
+ modelId: string;
796
+ actorRef: string;
797
+ threadId: string;
798
+ /** The `YYYY-MM-DD` UTC day the row belongs to (its trend bucket). */
799
+ day: string;
800
+ /** Provider-reported spend for the row; wins over the token estimate when present. */
801
+ costUsd?: number | null | undefined;
802
+ }
803
+ /** Thread metadata joined onto a spend bucket. */
804
+ interface ThreadMeta {
805
+ title: string;
806
+ actorRef: string;
807
+ }
808
+ /**
809
+ * Token-ledger estimate for one turn against a pricing row: the uncached input at the input rate,
810
+ * cache-write/cache-read tokens at their own rates (falling back to the input rate when unpriced),
811
+ * plus output at the output rate. An unpriced model (`price === undefined`) contributes 0 — its
812
+ * tokens still count. Cache token counts are subsets of `inputTokens`, so the uncached remainder is
813
+ * the difference.
814
+ */
815
+ declare function estimateCost(usage: CostUsage, price: ModelPrice | undefined): number;
816
+ /** Aggregate usage rows into per-model spend, highest cost first then modelId ascending. */
817
+ declare function bucketByModel(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): ModelSpendRow[];
818
+ /** Aggregate usage rows into per-actor spend (with distinct thread counts), highest cost first. */
819
+ declare function bucketByActor(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): ActorSpendRow[];
820
+ /**
821
+ * Aggregate usage rows into per-thread spend, highest cost first then threadId ascending, capped at
822
+ * `limit`. A thread absent from `threads` is dropped when `includeUnknownThreads` is false (the SQL
823
+ * adapters fetch only non-deleted threads, so a soft-deleted thread's rows vanish from the ranking);
824
+ * when true it is kept with blank title/actor (the in-memory adapter's semantics).
825
+ */
826
+ declare function bucketByThread(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>, threads: ReadonlyMap<string, ThreadMeta>, options: {
827
+ limit: number;
828
+ includeUnknownThreads: boolean;
829
+ }): ThreadSpendRow[];
830
+ /** Aggregate usage rows into a daily token/cost trend, ascending by day. */
831
+ declare function bucketUsageTrend(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): UsageTrendPoint[];
832
+ /** Turn an inclusive `YYYY-MM-DD` day range into the UTC datetime bounds used to filter usage rows. */
833
+ declare function dayBoundsUtc(range: GovernanceRange): {
834
+ start: Date;
835
+ end: Date;
836
+ };
837
+
685
838
  /** First filter layer: drop tools the actor's role may not invoke. `can` may be async (authz). */
686
839
  declare function filterToolsByRole(tools: ToolSpec[], actor: Actor, policy: RolesPolicy): Promise<ToolSpec[]>;
687
840
  /** Second filter layer: if the agent pins an allow-list, keep only those tool names. */
@@ -890,4 +1043,4 @@ declare function publishAgentRunFailed(payload: AgentRunFailed): void;
890
1043
  declare function publishAgentDelegated(payload: AgentDelegated): void;
891
1044
  declare function publishAgentRetrieved(payload: AgentRetrieved): void;
892
1045
 
893
- export { AGENT_ACTOR_RESOLVER, AGENT_DEPS_FACTORY, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_STORE, AGENT_TOOL_REGISTRY, type Actor, type ActorResolver, type ActorSpendRow, type AgentDefinition, type AgentDelegated, type AgentGovernanceQueries, type AgentLoopDeps, type AgentLoopHooks, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, type AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentStore, AgentStreamError, type AgentToolCallEvent, type AiToolCtx, type AppendMessageInput, type CreateThreadInput, type CurrentModelPrice, type Decision, DefaultRolesPolicy, type EmbeddingProvider, type GovernanceRange, type MessageRole, type MessageUsage, type ModelMessage, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type PageContext, type Passage, type PromptBuilder, type PromptContext, type PromptContributor, QuotaExceededError, type QuotaState, type QuotaStore, type QuotaView, type RecordToolCallInput, type RecordUsageInput, type RerankOptions, type Reranker, type RetrieveOptions, type Retriever, type RolesPolicy, type SinkWriter, type StoredMessage, type StreamError, type ThreadActivityRow, type ThreadDetail, type ThreadSpendRow, type ThreadSummary, type TokenStreamSink, type ToolCallActivityRow, type ToolCallRequest, type ToolCallStatus, type ToolDefinition, ToolForbiddenError, type ToolHandler, ToolInputInvalidError, type ToolKind, ToolNotFoundError, ToolRegistry, type ToolResult, type ToolSpec, type UpdateToolCallInput, type UsagePurpose, type UsageTrendPoint, filterToolsByAllowList, filterToolsByRole, publishAgentDelegated, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, runAgentLoop, seedModelPrices };
1046
+ export { AGENT_ACTOR_RESOLVER, AGENT_DEPS_FACTORY, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_STORE, AGENT_TOOL_REGISTRY, type Actor, type ActorResolver, type ActorSpendRow, type AgentCatalogEntry, type AgentDefinition, type AgentDelegated, type AgentGovernanceQueries, type AgentLoopDeps, type AgentLoopHooks, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, type AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentStore, AgentStreamError, type AgentStreamEvent, type AgentToolCallEvent, type AiToolCtx, type AppendMessageInput, type CostUsage, type CreateThreadInput, type CurrentModelPrice, type Decision, DefaultRolesPolicy, type EmbeddingProvider, type GovernanceRange, type GovernanceUsageInput, type MessageAttachment, type MessageRole, type MessageUsage, type ModelMessage, type ModelPrice, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type PageContext, type Passage, type PromptBuilder, type PromptContext, type PromptContributor, QuotaExceededError, type QuotaState, type QuotaStore, type QuotaView, type RecordToolCallInput, type RecordUsageInput, type RerankOptions, type Reranker, type RetrieveOptions, type Retriever, type RolesPolicy, type SinkWriter, type StoredMessage, type StreamError, type ThreadActivityRow, type ThreadDetail, type ThreadMeta, type ThreadSpendRow, type ThreadSummary, type TokenStreamSink, type ToolCallActivityRow, type ToolCallRequest, type ToolCallStatus, type ToolDefinition, ToolForbiddenError, type ToolHandler, ToolInputInvalidError, type ToolKind, ToolNotFoundError, ToolRegistry, type ToolResult, type ToolSpec, type UpdateToolCallInput, type UsagePurpose, type UsageTrendPoint, bucketByActor, bucketByModel, bucketByThread, bucketUsageTrend, dayBoundsUtc, encodeStreamEvent, estimateCost, filterToolsByAllowList, filterToolsByRole, publishAgentDelegated, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, runAgentLoop, seedModelPrices };
package/dist/index.d.ts CHANGED
@@ -107,12 +107,30 @@ interface Decision {
107
107
  reason?: string;
108
108
  }
109
109
  type MessageRole = 'user' | 'assistant' | 'system';
110
+ /**
111
+ * A file a user attached to a message so a vision-capable model sees it natively (an image, a PDF).
112
+ * The lib stays provider-agnostic: it passes {@link MessageAttachment.url} straight through as the
113
+ * model's image/file part data — making that URL reachable by the provider (presigned S3, a proxy)
114
+ * is the consumer's job. The lib never fetches bytes or talks to a store.
115
+ */
116
+ interface MessageAttachment {
117
+ /** Stable id of the stored media object in the consumer's media store. Provenance + replay key. */
118
+ mediaId: string;
119
+ /** A URL the model provider can fetch the bytes from at turn time. */
120
+ url: string;
121
+ /** MIME type — routes the part: `image/*` → image part, otherwise → file part. */
122
+ contentType: string;
123
+ /** Original filename, for display and the file part's filename. */
124
+ name: string;
125
+ }
110
126
  /** A neutral chat message exchanged with the model. */
111
127
  interface ModelMessage {
112
128
  role: MessageRole;
113
129
  content: string;
114
130
  toolCalls?: ToolCallRequest[];
115
131
  toolResults?: ToolResult[];
132
+ /** User-message attachments (image/PDF), rendered as native model content parts by the adapter. */
133
+ attachments?: MessageAttachment[];
116
134
  }
117
135
  interface PageContext {
118
136
  kind?: string;
@@ -148,6 +166,8 @@ interface AgentRunInput {
148
166
  actor: Actor;
149
167
  /** The latest user message text. */
150
168
  userText: string;
169
+ /** Files attached to the latest user message (image/PDF). Persisted with it and sent to the model. */
170
+ attachments?: MessageAttachment[];
151
171
  pageContext?: PageContext;
152
172
  /** YYYY-MM-DD stamped by the runner so quota/day stays deterministic under durable replay. */
153
173
  day?: string;
@@ -181,6 +201,8 @@ interface AgentRunInput {
181
201
  */
182
202
  interface AgentDefinition {
183
203
  name: string;
204
+ /** Human-readable summary from `@Agent({ description })`. Surfaced by the `GET agents` catalog. */
205
+ description?: string;
184
206
  /** Base prompt for this agent. A flat string, or a {@link PromptBuilder} resolved per turn. */
185
207
  systemPrompt?: string | PromptBuilder;
186
208
  /** Allow-list of tool names this agent may use (subset of all registered tools). */
@@ -190,6 +212,16 @@ interface AgentDefinition {
190
212
  modelId?: string;
191
213
  maxSteps?: number;
192
214
  }
215
+ /**
216
+ * The read-model the `GET agents` endpoint returns to a client — the safe public subset of an
217
+ * {@link AgentDefinition} so a host can render a persona picker instead of hardcoding one.
218
+ */
219
+ interface AgentCatalogEntry {
220
+ name: string;
221
+ description: string;
222
+ /** Whether this is the agent a turn uses when the caller names none. Omitted when not the default. */
223
+ isDefault?: boolean;
224
+ }
193
225
  interface ThreadSummary {
194
226
  id: string;
195
227
  title: string;
@@ -206,6 +238,8 @@ interface StoredMessage {
206
238
  agentName?: string;
207
239
  toolCalls?: ToolCallRequest[];
208
240
  toolResults?: ToolResult[];
241
+ /** Files the user attached to this message (image/PDF). Persisted with the message, replayed as-is. */
242
+ attachments?: MessageAttachment[];
209
243
  followUps?: string[];
210
244
  usage?: MessageUsage;
211
245
  createdAt: string;
@@ -359,6 +393,54 @@ interface ModelProvider {
359
393
  runTurn(args: ModelTurnArgs): Promise<ModelTurnResult>;
360
394
  }
361
395
 
396
+ /**
397
+ * The structured live-stream vocabulary carried over the {@link SinkWriter} byte channel.
398
+ *
399
+ * The model turn (via the AI-SDK adapter) and the agent loop write these events as NDJSON — one
400
+ * `JSON.stringify(event)\n` per {@link SinkWriter.write}. The HTTP layer forwards each line as an
401
+ * SSE `data:` frame, and the client transport maps them back to the AI SDK UI-message chunk
402
+ * protocol so the browser renders text, reasoning, and tool cards (input + output) LIVE — the same
403
+ * rich rendering a raw `streamText().toUIMessageStream()` would give, but reconstructed on the
404
+ * client so the sink stays a format-agnostic byte buffer (durable buffering/replay is untouched).
405
+ *
406
+ * Keeping this vocabulary neutral (not AI-SDK `UIMessageChunk`) means core never depends on `ai`:
407
+ * the adapter owns model-parts → event, the transport owns event → UI-chunk.
408
+ */
409
+ type AgentStreamEvent = {
410
+ kind: 'step-start';
411
+ } | {
412
+ kind: 'step-finish';
413
+ } | {
414
+ kind: 'text';
415
+ text: string;
416
+ } | {
417
+ kind: 'reasoning';
418
+ text: string;
419
+ } | {
420
+ kind: 'tool-input-start';
421
+ id: string;
422
+ name: string;
423
+ } | {
424
+ kind: 'tool-input-delta';
425
+ id: string;
426
+ delta: string;
427
+ } | {
428
+ kind: 'tool-input-available';
429
+ id: string;
430
+ name: string;
431
+ input: unknown;
432
+ } | {
433
+ kind: 'tool-output';
434
+ id: string;
435
+ output: unknown;
436
+ } | {
437
+ kind: 'tool-output-error';
438
+ id: string;
439
+ error: string;
440
+ };
441
+ /** Encode one event as an NDJSON line (`{...}\n`) for {@link SinkWriter.write}. */
442
+ declare function encodeStreamEvent(event: AgentStreamEvent): Uint8Array;
443
+
362
444
  interface CreateThreadInput {
363
445
  actor: Actor;
364
446
  transient?: boolean;
@@ -372,6 +454,8 @@ interface AppendMessageInput {
372
454
  agentName?: string;
373
455
  toolCalls?: ToolCallRequest[];
374
456
  toolResults?: ToolResult[];
457
+ /** Files the user attached to this message (image/PDF). Persisted verbatim. */
458
+ attachments?: MessageAttachment[];
375
459
  followUps?: string[];
376
460
  usage?: MessageUsage;
377
461
  }
@@ -592,11 +676,12 @@ interface AgentRunner {
592
676
  * an identity is never invented from a default. See `HeaderActorResolver` for a header-based
593
677
  * resolver suitable for demos and gateways that strip/re-set the headers.
594
678
  *
595
- * `req` is the transport request object, typed `unknown` to keep core framework-agnostic;
596
- * a NestJS/Express app receives the express `Request`.
679
+ * `req` is the transport request object. Defaults to `unknown` to keep core framework-agnostic
680
+ * (a NestJS/Express app receives the express `Request`) — a host may narrow it via
681
+ * `ActorResolver<Request>` instead of writing its own `unknown`-narrowing type guard.
597
682
  */
598
- interface ActorResolver {
599
- resolve(req: unknown): Actor | Promise<Actor>;
683
+ interface ActorResolver<TReq = unknown> {
684
+ resolve(req: TReq): Actor | Promise<Actor>;
600
685
  }
601
686
 
602
687
  /**
@@ -682,6 +767,74 @@ interface AgentGovernanceQueries {
682
767
  recentThreads(limit: number): Promise<ThreadActivityRow[]>;
683
768
  }
684
769
 
770
+ /**
771
+ * The pure aggregation core shared by every {@link import('../spi/governance-queries.js').AgentGovernanceQueries}
772
+ * adapter (MikroORM, Drizzle, in-memory). An adapter's only job is the DB-specific row fetch; the
773
+ * cost formula, the group→sort bucketing, and the day-bounds math live here so all three surfaces
774
+ * report identical numbers.
775
+ */
776
+ /** The current per-1M token prices for one model; cache rates fall back to the input rate. */
777
+ interface ModelPrice {
778
+ inputPricePer1m: number;
779
+ outputPricePer1m: number;
780
+ cacheWritePricePer1m?: number | null;
781
+ cacheReadPricePer1m?: number | null;
782
+ }
783
+ /** The token split of one turn needed to estimate its cost. */
784
+ interface CostUsage {
785
+ inputTokens: number;
786
+ outputTokens: number;
787
+ cacheWriteTokens?: number | null | undefined;
788
+ cacheReadTokens?: number | null | undefined;
789
+ }
790
+ /**
791
+ * A normalized usage row the bucketers consume. Each adapter maps its raw DB row into this shape
792
+ * (MikroORM reads `thread.id` + `createdAt`, Drizzle/in-memory read the flat `threadId` + `day`).
793
+ */
794
+ interface GovernanceUsageInput extends CostUsage {
795
+ modelId: string;
796
+ actorRef: string;
797
+ threadId: string;
798
+ /** The `YYYY-MM-DD` UTC day the row belongs to (its trend bucket). */
799
+ day: string;
800
+ /** Provider-reported spend for the row; wins over the token estimate when present. */
801
+ costUsd?: number | null | undefined;
802
+ }
803
+ /** Thread metadata joined onto a spend bucket. */
804
+ interface ThreadMeta {
805
+ title: string;
806
+ actorRef: string;
807
+ }
808
+ /**
809
+ * Token-ledger estimate for one turn against a pricing row: the uncached input at the input rate,
810
+ * cache-write/cache-read tokens at their own rates (falling back to the input rate when unpriced),
811
+ * plus output at the output rate. An unpriced model (`price === undefined`) contributes 0 — its
812
+ * tokens still count. Cache token counts are subsets of `inputTokens`, so the uncached remainder is
813
+ * the difference.
814
+ */
815
+ declare function estimateCost(usage: CostUsage, price: ModelPrice | undefined): number;
816
+ /** Aggregate usage rows into per-model spend, highest cost first then modelId ascending. */
817
+ declare function bucketByModel(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): ModelSpendRow[];
818
+ /** Aggregate usage rows into per-actor spend (with distinct thread counts), highest cost first. */
819
+ declare function bucketByActor(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): ActorSpendRow[];
820
+ /**
821
+ * Aggregate usage rows into per-thread spend, highest cost first then threadId ascending, capped at
822
+ * `limit`. A thread absent from `threads` is dropped when `includeUnknownThreads` is false (the SQL
823
+ * adapters fetch only non-deleted threads, so a soft-deleted thread's rows vanish from the ranking);
824
+ * when true it is kept with blank title/actor (the in-memory adapter's semantics).
825
+ */
826
+ declare function bucketByThread(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>, threads: ReadonlyMap<string, ThreadMeta>, options: {
827
+ limit: number;
828
+ includeUnknownThreads: boolean;
829
+ }): ThreadSpendRow[];
830
+ /** Aggregate usage rows into a daily token/cost trend, ascending by day. */
831
+ declare function bucketUsageTrend(rows: GovernanceUsageInput[], prices: ReadonlyMap<string, ModelPrice>): UsageTrendPoint[];
832
+ /** Turn an inclusive `YYYY-MM-DD` day range into the UTC datetime bounds used to filter usage rows. */
833
+ declare function dayBoundsUtc(range: GovernanceRange): {
834
+ start: Date;
835
+ end: Date;
836
+ };
837
+
685
838
  /** First filter layer: drop tools the actor's role may not invoke. `can` may be async (authz). */
686
839
  declare function filterToolsByRole(tools: ToolSpec[], actor: Actor, policy: RolesPolicy): Promise<ToolSpec[]>;
687
840
  /** Second filter layer: if the agent pins an allow-list, keep only those tool names. */
@@ -890,4 +1043,4 @@ declare function publishAgentRunFailed(payload: AgentRunFailed): void;
890
1043
  declare function publishAgentDelegated(payload: AgentDelegated): void;
891
1044
  declare function publishAgentRetrieved(payload: AgentRetrieved): void;
892
1045
 
893
- export { AGENT_ACTOR_RESOLVER, AGENT_DEPS_FACTORY, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_STORE, AGENT_TOOL_REGISTRY, type Actor, type ActorResolver, type ActorSpendRow, type AgentDefinition, type AgentDelegated, type AgentGovernanceQueries, type AgentLoopDeps, type AgentLoopHooks, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, type AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentStore, AgentStreamError, type AgentToolCallEvent, type AiToolCtx, type AppendMessageInput, type CreateThreadInput, type CurrentModelPrice, type Decision, DefaultRolesPolicy, type EmbeddingProvider, type GovernanceRange, type MessageRole, type MessageUsage, type ModelMessage, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type PageContext, type Passage, type PromptBuilder, type PromptContext, type PromptContributor, QuotaExceededError, type QuotaState, type QuotaStore, type QuotaView, type RecordToolCallInput, type RecordUsageInput, type RerankOptions, type Reranker, type RetrieveOptions, type Retriever, type RolesPolicy, type SinkWriter, type StoredMessage, type StreamError, type ThreadActivityRow, type ThreadDetail, type ThreadSpendRow, type ThreadSummary, type TokenStreamSink, type ToolCallActivityRow, type ToolCallRequest, type ToolCallStatus, type ToolDefinition, ToolForbiddenError, type ToolHandler, ToolInputInvalidError, type ToolKind, ToolNotFoundError, ToolRegistry, type ToolResult, type ToolSpec, type UpdateToolCallInput, type UsagePurpose, type UsageTrendPoint, filterToolsByAllowList, filterToolsByRole, publishAgentDelegated, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, runAgentLoop, seedModelPrices };
1046
+ export { AGENT_ACTOR_RESOLVER, AGENT_DEPS_FACTORY, AGENT_DURABLE_RUNNER, AGENT_EMBEDDING_PROVIDER, AGENT_GOVERNANCE_QUERIES, AGENT_MODEL, AGENT_OPTIONS, AGENT_PRICING_STORE, AGENT_PROMPT_CONTRIBUTORS, AGENT_QUOTA_STORE, AGENT_REGISTRY, AGENT_RETRIEVER, AGENT_ROLES_POLICY, AGENT_RUNNER, AGENT_SINK, AGENT_STORE, AGENT_TOOL_REGISTRY, type Actor, type ActorResolver, type ActorSpendRow, type AgentCatalogEntry, type AgentDefinition, type AgentDelegated, type AgentGovernanceQueries, type AgentLoopDeps, type AgentLoopHooks, type AgentMessageEvent, type AgentPricingStore, type AgentQuotaExceeded, AgentRegistry, type AgentRetrieved, type AgentRunFailed, type AgentRunFinished, type AgentRunInput, type AgentRunStarted, type AgentRunner, type AgentStore, AgentStreamError, type AgentStreamEvent, type AgentToolCallEvent, type AiToolCtx, type AppendMessageInput, type CostUsage, type CreateThreadInput, type CurrentModelPrice, type Decision, DefaultRolesPolicy, type EmbeddingProvider, type GovernanceRange, type GovernanceUsageInput, type MessageAttachment, type MessageRole, type MessageUsage, type ModelMessage, type ModelPrice, type ModelPriceInput, type ModelProvider, type ModelSpendRow, type ModelTurnArgs, type ModelTurnResult, type PageContext, type Passage, type PromptBuilder, type PromptContext, type PromptContributor, QuotaExceededError, type QuotaState, type QuotaStore, type QuotaView, type RecordToolCallInput, type RecordUsageInput, type RerankOptions, type Reranker, type RetrieveOptions, type Retriever, type RolesPolicy, type SinkWriter, type StoredMessage, type StreamError, type ThreadActivityRow, type ThreadDetail, type ThreadMeta, type ThreadSpendRow, type ThreadSummary, type TokenStreamSink, type ToolCallActivityRow, type ToolCallRequest, type ToolCallStatus, type ToolDefinition, ToolForbiddenError, type ToolHandler, ToolInputInvalidError, type ToolKind, ToolNotFoundError, ToolRegistry, type ToolResult, type ToolSpec, type UpdateToolCallInput, type UsagePurpose, type UsageTrendPoint, bucketByActor, bucketByModel, bucketByThread, bucketUsageTrend, dayBoundsUtc, encodeStreamEvent, estimateCost, filterToolsByAllowList, filterToolsByRole, publishAgentDelegated, publishAgentMessage, publishAgentQuotaExceeded, publishAgentRetrieved, publishAgentRunFailed, publishAgentRunFinished, publishAgentRunStarted, publishAgentToolCall, runAgentLoop, seedModelPrices };