@happyvertical/smrt-agents 0.38.18 → 0.38.20

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/AGENTS.md CHANGED
@@ -85,11 +85,57 @@ Persisted `agent_config` snapshots env-derived values at sync time, so rotated e
85
85
 
86
86
  The TaskRunner calls `resolveLazyConfig()` immediately before constructing the agent, so live values always win over snapshotted ones. Re-exported from `@happyvertical/smrt-core` (`resolveLazyConfig`, `registerConfigResolver`, `getClassConfigResolvers`, …) for cases where agents isn't on the import path.
87
87
 
88
+ ## Learning Trait (issue #1886) — opt-in
89
+
90
+ Any agent can opt into a confidence-scored **recall-before / capture-after** loop backed by core's `LearningMemory` (over `_smrt_contexts` + `_smrt_embeddings`). **Off by default** — a non-opted agent behaves byte-for-byte as today; the lifecycle's learning branches are never entered.
91
+
92
+ ```typescript
93
+ @smrt()
94
+ class InvoiceAgent extends Agent {
95
+ static override learning = true; // or { minConfidence: 0.8, scope: 'invoices', ... }
96
+ protected config = {};
97
+
98
+ async run() {
99
+ // recall-before-run already populated `recalledMemories` (confidence >= floor)
100
+ const cached = this.recalledMemories.find((m) => m.key === this.docUrl);
101
+ const strategy = cached?.value ?? (await this.generateStrategy());
102
+
103
+ // stage the episode; the lifecycle reinforces it after run()
104
+ this.stageLearning({ scope: this.learningScope(), key: this.docUrl, value: strategy });
105
+
106
+ // a validated failure decays the memory without throwing
107
+ if (!ok) this.reportLearningOutcome({ success: false, error: 'no match' });
108
+ }
109
+ }
110
+ ```
111
+
112
+ - **`capture` semantics** (`LearningMemory`): success strengthens `confidence` toward 1.0 and increments `success_count`; failure decays toward `failureConfidence` (0.3) and increments `failure_count`. A single failure drops a confident memory below the reuse floor (0.7), so recall stops returning it. Refreshes `last_used_at`; honours `expires_at` and optional time-decay.
113
+ - **Memory isolation**: bound to `(agentType, agentInstanceId)` as `(owner_class, owner_id)`, so two tenants on the same agent class never share memory. `tenantId` is threaded into the optional semantic-search `where`.
114
+ - **Seams to override**: `learningScope()`, `recallForRun(memory)`, `captureForRun(memory, outcome)`, `getLearningSemanticSearch()`. Helpers for `run()`: `stageLearning(episode)`, `reportLearningOutcome(outcome)`, `getLearningMemory()`, and the `recalledMemories` field.
115
+ - **Config**: `static learning: boolean | AgentLearningConfig` — `{ enabled?, scope?, minConfidence?, successConfidence?, failureConfidence?, reinforcement?, decayHalfLifeMs? }`. `LearningMemory` and its types are re-exported from `@happyvertical/smrt-core`.
116
+
117
+ ## Multi-Instance Agents (issue #1890) — opt-in
118
+
119
+ `static multiInstance = false` by default: a class is a **singleton** (the N=1 case) and is byte-for-byte unchanged — one dispatch subscriber keyed by the agent type, one memory scope, class-wide interests. Set `static multiInstance = true` to run N durable instances (personas, from `@happyvertical/smrt-personas`) of one class per tenant, each independent.
120
+
121
+ The framework provides only the per-instance **identity**; a package scopes its own dispatch/interests to the instance's config by overriding the seams.
122
+
123
+ - **`AgentOptions.instanceKey`** — the durable per-instance key (typically the persona id). Honored **only** when `multiInstance` is true, so passing it to a non-opted agent is a no-op.
124
+ - **`getInstanceKey()`** → the key, or `null` for a singleton (opt-in off, or no key).
125
+ - **`getDispatchSubscriber()`** → `` `${agentType}#${key}` `` for a multi-instance agent, the bare `agentType` for a singleton. Used everywhere the agent subscribes/seeds/processes, so each instance has its own subscription rows and pending-dispatch queue — two instances never compete for or double-process each other's dispatches. Composed by the exported `instanceScopedSubscriber(agentType, key)`.
126
+ - **`learningScope()`** — suffixed with `#<key>` for a multi-instance agent, so instances learn independently (singleton scope unchanged).
127
+ - **Seams to override** (both default to singleton behavior):
128
+ - `resolveSignalSubscriptions()` — derive **instance-scoped** signal types from the instance config so an emit meant for one instance only matches its subscription.
129
+ - `instanceInterestFilter()` — an `ObjectFilter` AND-merged (as the base layer) into every `interesting()` query so instances partition the objects they process.
130
+
131
+ The **`default` persona reuses the singleton identity** (a `null` key), which is what makes the singleton→multi upgrade non-destructive — see `@happyvertical/smrt-personas` (`personaInstanceKey`, `upgradeSingletonToDefaultPersona`).
132
+
88
133
  ## Key Files
89
134
 
90
135
  | File | Purpose |
91
136
  |------|---------|
92
- | `src/agent.ts` | Base Agent class — lifecycle, dispatch, interests, config |
137
+ | `src/agent.ts` | Base Agent class — lifecycle, dispatch, interests, config, opt-in learning trait, multi-instance identity |
138
+ | `src/learning.ts` | `AgentLearningConfig` + `resolveAgentLearning()` declaration normalisation |
93
139
  | `src/schedule.ts` | AgentSchedule model — cron, execution tracking |
94
140
  | `src/tenant-agent.ts` | TenantAgent — junction table, hierarchical resolution |
95
141
  | `src/interests.ts` | Interest filter types and configuration |
package/dist/agent.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Logger } from '@happyvertical/logger';
2
- import { ConfigResolver, DispatchBus, DispatchMetadata, SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';
2
+ import { ConfigResolver, DispatchBus, DispatchMetadata, LearningEpisode, LearningMemory, LearningMemoryRecord, LearningOutcome, LearningSemanticSearch, SmrtObject, SmrtObjectOptions } from '@happyvertical/smrt-core';
3
3
  import { AgentAIOptions } from './ai-config.js';
4
- import { AgentWithInterestsOptions, InterestOptions, InterestResult } from './interests.js';
4
+ import { AgentWithInterestsOptions, InterestOptions, InterestResult, ObjectFilter } from './interests.js';
5
+ import { AgentLearningDeclaration } from './learning.js';
5
6
  import { AgentStatusType } from './types.js';
6
7
  import { AgentAdminRoute, AgentUISlots } from './ui.js';
7
8
  /**
@@ -29,6 +30,18 @@ export interface AgentOptions extends SmrtObjectOptions, AgentWithInterestsOptio
29
30
  * shutdown itself; the first handler to finish exits the process.
30
31
  */
31
32
  manageProcessSignals?: boolean;
33
+ /**
34
+ * Durable per-instance key for multi-instance agents (#1890).
35
+ *
36
+ * Only honored when the agent class opts into multi-instance
37
+ * (`static multiInstance = true`); a singleton agent (the default) ignores it,
38
+ * so passing a key can never change a non-opted agent's behavior. When honored
39
+ * it becomes the per-instance dispatch subscriber suffix and memory partition
40
+ * (see {@link Agent.getDispatchSubscriber} / {@link Agent.learningScope}) so N
41
+ * instances of one class run independently. Typically the persona id from
42
+ * `@happyvertical/smrt-personas` (a persona is a durable instance).
43
+ */
44
+ instanceKey?: string | null;
32
45
  }
33
46
  /**
34
47
  * Base Agent class for building autonomous actors in the SMRT ecosystem
@@ -187,6 +200,59 @@ export declare abstract class Agent extends SmrtObject {
187
200
  * ```
188
201
  */
189
202
  static configResolvers: Record<string, ConfigResolver>;
203
+ /**
204
+ * Opt-in learning trait declaration (#1886).
205
+ *
206
+ * **Off by default.** Set to `true` (or a config object) on a subclass to
207
+ * wire a confidence-scored recall-before / capture-after loop into the agent
208
+ * lifecycle, backed by {@link LearningMemory}. A non-opted agent behaves
209
+ * byte-for-byte as it does today — the learning branches are never entered.
210
+ *
211
+ * When enabled, the loop wraps `run()` itself (in {@link initialize}), so it
212
+ * fires whether the agent runs via {@link execute} or the background/scheduled
213
+ * path (which calls `run()` directly). Each run:
214
+ * 1. recalls confident memories for {@link learningScope} before `run()`,
215
+ * exposing them via {@link recalledMemories};
216
+ * 2. captures the run outcome after `run()` — a clean completion reinforces
217
+ * the staged memory (see {@link stageLearning}); a thrown error or an
218
+ * explicit {@link reportLearningOutcome} failure decays it.
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * @smrt()
223
+ * class InvoiceAgent extends Agent {
224
+ * static override learning = true; // reuse floor 0.7, success 0.9, fail 0.3
225
+ * // or: static override learning = { minConfidence: 0.8, scope: 'invoices' };
226
+ * protected config = {};
227
+ * async run() {
228
+ * const [cached] = this.recalledMemories;
229
+ * const strategy = cached?.value ?? (await this.generateStrategy());
230
+ * this.stageLearning({ scope: this.learningScope(), key: 'default', value: strategy });
231
+ * }
232
+ * }
233
+ * ```
234
+ */
235
+ static learning: AgentLearningDeclaration;
236
+ /**
237
+ * Opt into multiple durable instances of this agent class per tenant (#1890).
238
+ *
239
+ * **Off by default** — a non-opted class is a **singleton** (the N=1 case) and
240
+ * behaves byte-for-byte as it does today: one dispatch subscriber keyed by the
241
+ * agent type, one memory scope, class-wide interests. Setting this to `true`
242
+ * lets N configured instances (personas, from `@happyvertical/smrt-personas`)
243
+ * run independently: each is constructed with its own {@link AgentOptions.instanceKey},
244
+ * which the framework folds into a per-instance dispatch subscriber
245
+ * ({@link getDispatchSubscriber}), memory partition ({@link learningScope}),
246
+ * and interest/subscription scoping seams ({@link instanceInterestFilter} /
247
+ * {@link resolveSignalSubscriptions}) so two instances never double-process
248
+ * each other's dispatches or interests.
249
+ *
250
+ * The framework provides the per-instance *identity*; a package scopes its own
251
+ * dispatch/interests to the instance's config by overriding the seams. The
252
+ * `default` persona reuses the singleton identity (null key), which makes the
253
+ * singleton→multi upgrade non-destructive.
254
+ */
255
+ static multiInstance: boolean;
190
256
  /**
191
257
  * Current agent status
192
258
  */
@@ -217,6 +283,34 @@ export declare abstract class Agent extends SmrtObject {
217
283
  * Cached DispatchBus instance for inter-agent communication
218
284
  */
219
285
  private _dispatch;
286
+ /**
287
+ * Cached LearningMemory binding, once successfully built. Not cached when
288
+ * learning is disabled or the DB isn't ready yet, so an early call can't
289
+ * permanently stick the agent in a learning-disabled state.
290
+ */
291
+ private _learningMemory?;
292
+ /**
293
+ * Whether `run()` has been wrapped with the learning loop (idempotency guard).
294
+ */
295
+ private _runWrappedForLearning;
296
+ /**
297
+ * The episode the current run acted on, staged via {@link stageLearning} so
298
+ * the lifecycle can reinforce it after `run()`.
299
+ */
300
+ private _learningEpisode;
301
+ /**
302
+ * Explicit outcome for the current run, set via
303
+ * {@link reportLearningOutcome}. When unset, a clean `run()` is treated as a
304
+ * success and a thrown error as a failure.
305
+ */
306
+ private _learningOutcome;
307
+ /**
308
+ * Memories recalled before `run()` when the learning trait is enabled.
309
+ *
310
+ * Empty for non-opted agents. Populated by the lifecycle (see
311
+ * {@link recallForRun}); read from `run()` to reuse prior knowledge.
312
+ */
313
+ protected recalledMemories: LearningMemoryRecord[];
220
314
  /**
221
315
  * Creates a new Agent instance
222
316
  *
@@ -236,6 +330,55 @@ export declare abstract class Agent extends SmrtObject {
236
330
  * Human-readable class name for logs and UI.
237
331
  */
238
332
  protected getAgentClassName(): string;
333
+ /**
334
+ * Whether this agent class opted into multiple durable instances per tenant.
335
+ */
336
+ protected isMultiInstance(): boolean;
337
+ /**
338
+ * The durable per-instance key for this agent, or `null` for a singleton.
339
+ *
340
+ * Returns `null` unless the class opts in (`static multiInstance = true`) AND a
341
+ * non-empty {@link AgentOptions.instanceKey} was supplied — so a non-opted
342
+ * agent is always singleton-identified even if a key is passed. This is the
343
+ * anchor the framework folds into the dispatch subscriber, memory scope, and
344
+ * scoping seams below.
345
+ */
346
+ getInstanceKey(): string | null;
347
+ /**
348
+ * Canonical dispatch subscriber identity for this agent.
349
+ *
350
+ * A singleton (no instance key) is the bare agent type — **unchanged** from the
351
+ * class-keyed behavior. A multi-instance agent is `` `${agentType}#${key}` ``,
352
+ * giving each instance its own subscription rows and its own pending-dispatch
353
+ * queue so instances don't compete for or double-process each other's
354
+ * dispatches. Used everywhere the agent subscribes, seeds, and processes.
355
+ */
356
+ getDispatchSubscriber(): string;
357
+ /**
358
+ * The signal types this instance should seed as dispatch subscriptions.
359
+ *
360
+ * Defaults to the class's static {@link Agent.signalSubscriptions} unchanged.
361
+ * A multi-instance package overrides this to derive **instance-scoped** signal
362
+ * types from the persona/instance config (e.g. append the instance key or a
363
+ * routing dimension), so an emit meant for one instance only matches that
364
+ * instance's subscription and the other never processes it.
365
+ */
366
+ protected resolveSignalSubscriptions(): string[];
367
+ /**
368
+ * An optional filter AND-merged (as the base layer) into every
369
+ * {@link interesting} query for this instance.
370
+ *
371
+ * `undefined` by default (no scoping — singleton behavior unchanged). A
372
+ * multi-instance package overrides it to return an instance-discriminating
373
+ * filter derived from the persona/instance config, so two instances of one
374
+ * class partition the objects they process and never double-handle the same
375
+ * row. Global and per-object interest filters layer on top (and win on key
376
+ * collision), so choose a dedicated discriminator key here.
377
+ *
378
+ * Applies to the standard filter path; custom `query` interest filters own
379
+ * their SQL and should incorporate {@link getInstanceKey} themselves.
380
+ */
381
+ protected instanceInterestFilter(): ObjectFilter | undefined;
239
382
  /**
240
383
  * Get UI slot definitions for this agent instance
241
384
  *
@@ -383,6 +526,68 @@ export declare abstract class Agent extends SmrtObject {
383
526
  * ```
384
527
  */
385
528
  processDispatches(): Promise<number>;
529
+ /**
530
+ * Base memory scope for this agent's learning.
531
+ *
532
+ * Defaults to the configured `scope` (if any) or `agent/<agentType>`.
533
+ * Override to shape how memories are filed (e.g. per task type). Recall and
534
+ * capture are additionally isolated by the agent instance id (owner), so
535
+ * memory never bleeds across tenants running the same agent class.
536
+ */
537
+ protected learningScope(): string;
538
+ /**
539
+ * Optional semantic-search arm for {@link LearningMemory}.
540
+ *
541
+ * Returns `undefined` by default (keyed-context recall only). Override to
542
+ * wire embedding search — e.g. return a bound `collection.semanticSearch`.
543
+ */
544
+ protected getLearningSemanticSearch(): LearningSemanticSearch | undefined;
545
+ /**
546
+ * Resolve the tenant id used for the learning scope and semantic filtering.
547
+ */
548
+ private resolveLearningTenantId;
549
+ /**
550
+ * Get this agent's {@link LearningMemory} binding, or `null` when learning is
551
+ * disabled or no database is configured.
552
+ *
553
+ * Cheap and side-effect-free when the trait is off (returns `null` after a
554
+ * single static-flag check), which keeps non-opted agents unchanged.
555
+ */
556
+ getLearningMemory(): LearningMemory | null;
557
+ /**
558
+ * Wrap `run()` with the recall-before / capture-after learning loop when the
559
+ * trait is enabled, so it fires **however run() is invoked** — via
560
+ * {@link execute} OR directly by the background/scheduled path
561
+ * (`ScheduleRunner` → `TaskRunner` calls the agent's configured method, which
562
+ * defaults to `run` and never goes through `execute()`). Both paths call
563
+ * {@link initialize}, so wrapping here covers them. Idempotent, and a no-op
564
+ * for non-opted agents (their `run()` is left untouched).
565
+ */
566
+ private wrapRunForLearning;
567
+ /**
568
+ * Recall relevant memories before `run()`.
569
+ *
570
+ * Default: a scope-wide, confidence-filtered recall of {@link learningScope}.
571
+ * Override to shape the recall (e.g. a keyed lookup or a semantic query).
572
+ */
573
+ protected recallForRun(memory: LearningMemory): Promise<LearningMemoryRecord[]>;
574
+ /**
575
+ * Capture the run outcome after `run()`.
576
+ *
577
+ * Default: reinforce the memory staged via {@link stageLearning}. A no-op
578
+ * when nothing was staged. Override for bespoke capture logic.
579
+ */
580
+ protected captureForRun(memory: LearningMemory, outcome: LearningOutcome): Promise<void>;
581
+ /**
582
+ * Stage the memory episode the current run acted on, so the lifecycle
583
+ * reinforces it after `run()` completes. Call from `run()`.
584
+ */
585
+ protected stageLearning(episode: LearningEpisode): void;
586
+ /**
587
+ * Report an explicit outcome for the current run (e.g. a validated failure
588
+ * that did not throw). Overrides the default success/throw inference.
589
+ */
590
+ protected reportLearningOutcome(outcome: LearningOutcome): void;
386
591
  /**
387
592
  * Initialize the agent
388
593
  * Sets status to 'initializing' and sets up signal handlers
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,9 @@ function getAgentClassName(name) {
11
11
  function getAgentTypeAliases(name) {
12
12
  return Array.from(new Set([getAgentTypeName(name), getAgentClassName(name)].filter(Boolean)));
13
13
  }
14
+ function instanceScopedSubscriber(agentType, instanceKey) {
15
+ return instanceKey ? `${agentType}#${instanceKey}` : agentType;
16
+ }
14
17
  //#endregion
15
18
  //#region src/config.ts
16
19
  var __defProp = Object.defineProperty;
@@ -164,6 +167,6 @@ var AgentConfigCollection = class extends SmrtCollection {
164
167
  }
165
168
  };
166
169
  //#endregion
167
- export { getAgentTypeName as a, getAgentTypeAliases as i, AgentConfigCollection as n, getAgentClassName as r, AgentConfig as t };
170
+ export { getAgentTypeName as a, getAgentTypeAliases as i, AgentConfigCollection as n, instanceScopedSubscriber as o, getAgentClassName as r, AgentConfig as t };
168
171
 
169
- //# sourceMappingURL=config-C73buv9Y.js.map
172
+ //# sourceMappingURL=config-BRQLhsFp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-BRQLhsFp.js","names":["tenantId"],"sources":["../../src/identity.ts","../../src/config.ts"],"sourcesContent":["import { getClassName, ObjectRegistry } from '@happyvertical/smrt-core';\n\n/**\n * Return the canonical agent type identifier for storage and dispatch routing.\n *\n * Uses the registry's qualified name when available and falls back to the input\n * name for dynamically defined or unregistered classes.\n */\nexport function getAgentTypeName(name: string): string {\n const registered = ObjectRegistry.getClass(name);\n return registered?.qualifiedName || registered?.name || name;\n}\n\n/**\n * Return the human-readable class name for UI and logs.\n */\nexport function getAgentClassName(name: string): string {\n const registered = ObjectRegistry.getClass(name);\n return registered?.name || getClassName(name);\n}\n\n/**\n * Return all meaningful aliases for an agent type.\n *\n * The qualified name is first so persistence lookups prefer canonical rows,\n * while the simple class name keeps legacy rows discoverable during migration.\n */\nexport function getAgentTypeAliases(name: string): string[] {\n return Array.from(\n new Set([getAgentTypeName(name), getAgentClassName(name)].filter(Boolean)),\n );\n}\n\n/**\n * Compose a per-instance dispatch subscriber identity from an agent type and an\n * optional instance key (#1890).\n *\n * Multiple durable instances of one agent class each need their own subscriber\n * name so their dispatch subscriptions and pending dispatches never collide —\n * that is what keeps two instances from double-processing each other's work.\n *\n * Returns the bare `agentType` when `instanceKey` is nullish/empty, so a\n * **singleton** agent's subscriber is byte-for-byte unchanged (the N=1 default).\n * When a key is present the identity is `` `${agentType}#${instanceKey}` `` — a\n * stable, reversible composition (the type never contains `#`).\n */\nexport function instanceScopedSubscriber(\n agentType: string,\n instanceKey?: string | null,\n): string {\n return instanceKey ? `${agentType}#${instanceKey}` : agentType;\n}\n","/**\n * AgentConfig - Persistent configuration storage for agents\n *\n * This module provides database-backed configuration for agents,\n * enabling consuming apps to persist agent settings.\n *\n * @module\n */\n\nimport {\n field,\n type SmrtClassOptions,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n queryGlobal,\n queryWithGlobals,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport { getAgentTypeName } from './identity.js';\n\n/**\n * AgentConfig stores agent configuration in the database\n *\n * Each config record maps to a UI slot for an agent instance:\n * - agentId: The agent instance's ID\n * - agentClass: The canonical agent type (qualified name when available)\n * - slotId: The configuration slot (e.g., 'sources', 'settings')\n * - configData: JSON object containing the configuration\n *\n * @example\n * ```typescript\n * // Save config for an agent slot\n * const config = new AgentConfig({\n * agentId: agent.id,\n * agentClass: 'Praeco',\n * slotId: 'sources',\n * configData: { scrapers: ['civicweb', 'govstack'] },\n * db: options.db\n * });\n * await config.initialize();\n * await config.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'agent_configs',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class AgentConfig extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global agent configs\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * ID of the agent instance this config belongs to\n */\n @field({ type: 'text' })\n agentId: string = '';\n\n /**\n * Canonical agent type for this config (qualified name when available)\n */\n @field({ type: 'text' })\n agentClass: string = '';\n\n /**\n * UI slot ID (e.g., 'sources', 'settings', 'reports')\n */\n @field({ type: 'text' })\n slotId: string = '';\n\n /**\n * Configuration data stored as JSON\n *\n * Sensitive (#1540): agent config blobs routinely carry API keys/credentials,\n * so this is excluded from generated API/MCP responses and rejected as a\n * `where` filter key.\n */\n @field({ type: 'json', sensitive: true })\n configData: Record<string, unknown> = {};\n\n /**\n * Schema version for future migrations\n */\n @field({ type: 'integer' })\n schemaVersion: number = 1;\n\n /**\n * Load all configs for a specific agent\n *\n * @param agentId - Agent instance ID\n * @param options - Database options\n * @returns Map of slotId → configData\n */\n static async forAgent(\n agentId: string,\n options: SmrtClassOptions,\n ): Promise<Map<string, Record<string, unknown>>> {\n const configsByAgent = await AgentConfig.forAgents([agentId], options);\n return configsByAgent.get(agentId) ?? new Map();\n }\n\n /**\n * Load configs for multiple agents in a single query.\n *\n * @param agentIds - Agent instance IDs\n * @param options - Database options\n * @returns Map of agentId -> (slotId -> configData)\n */\n static async forAgents(\n agentIds: string[],\n options: SmrtClassOptions,\n ): Promise<Map<string, Map<string, Record<string, unknown>>>> {\n const configsByAgent = new Map<\n string,\n Map<string, Record<string, unknown>>\n >();\n if (agentIds.length === 0) {\n return configsByAgent;\n }\n\n const collection = await AgentConfigCollection.create(options);\n const configs = await collection.list({\n where: { 'agentId in': agentIds },\n });\n\n for (const config of configs) {\n if (!configsByAgent.has(config.agentId)) {\n configsByAgent.set(config.agentId, new Map());\n }\n configsByAgent.get(config.agentId)?.set(config.slotId, config.configData);\n }\n\n return configsByAgent;\n }\n\n /**\n * Load config for a specific agent and slot\n *\n * @param agentId - Agent instance ID\n * @param slotId - UI slot ID\n * @param options - Database options\n * @returns Config data or undefined if not found\n */\n static async forSlot(\n agentId: string,\n slotId: string,\n options: SmrtClassOptions,\n ): Promise<Record<string, unknown> | undefined> {\n const collection = await AgentConfigCollection.create(options);\n const configs = await collection.list({\n where: { agentId, slotId },\n limit: 1,\n });\n return configs[0]?.configData;\n }\n\n /**\n * Save or update config for an agent slot\n *\n * @param data - Config data including agentId, agentClass, slotId, configData\n * @param options - Database options\n * @returns Saved AgentConfig instance\n */\n static async saveSlot(\n data: {\n agentId: string;\n agentClass: string;\n slotId: string;\n configData: Record<string, unknown>;\n },\n options: SmrtClassOptions,\n ): Promise<AgentConfig> {\n const normalizedAgentClass = getAgentTypeName(data.agentClass);\n const collection = await AgentConfigCollection.create(options);\n\n // Check for existing config using list with where clause\n const existingConfigs = await collection.list({\n where: { agentId: data.agentId, slotId: data.slotId },\n limit: 1,\n });\n\n if (existingConfigs.length > 0) {\n // Update existing\n const existing = existingConfigs[0];\n existing.configData = data.configData;\n existing.agentClass = normalizedAgentClass;\n await existing.save();\n return existing;\n }\n\n // Create new\n const config = await collection.create({\n agentId: data.agentId,\n agentClass: normalizedAgentClass,\n slotId: data.slotId,\n configData: data.configData,\n slug: `${data.agentId}-${data.slotId}`,\n });\n await config.save();\n return config;\n }\n}\n\n/**\n * Collection for AgentConfig objects\n */\nexport class AgentConfigCollection extends SmrtCollection<AgentConfig> {\n static readonly _itemClass = AgentConfig;\n\n /**\n * Find all configs for a specific tenant\n * @param tenantId - Tenant ID to filter by\n * @returns Array of AgentConfig objects for the tenant\n */\n async findByTenant(tenantId: string): Promise<AgentConfig[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global configs (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global AgentConfig objects\n */\n async findGlobal(): Promise<AgentConfig[]> {\n return queryGlobal<AgentConfig>(this);\n }\n\n /**\n * Find configs for a tenant including global configs.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of AgentConfig objects for the tenant and global configs\n */\n async findWithGlobals(tenantId: string): Promise<AgentConfig[]> {\n return queryWithGlobals<AgentConfig>(\n this,\n tenantId,\n 'AgentConfig.findWithGlobals',\n );\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ import { Logger } from '@happyvertical/logger';
2
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
3
+ import { OperationPermissionCollectionInput, OperationPermissionDecision, PermissionResolver, SessionPermissionRuntimeContext } from '@happyvertical/smrt-users';
4
+ /**
5
+ * The bound principal an agent runs as. A resolved persona structurally
6
+ * satisfies this once its optional `runAsUserId` has been narrowed to a
7
+ * concrete id — `allowedTools` on a `ResolvedPersona` is already the persona's
8
+ * tools intersected with the `TenantAgent` capability ceiling.
9
+ */
10
+ export interface PrincipalBinding {
11
+ /** The user whose live permissions bound this execution. Required. */
12
+ runAsUserId: string;
13
+ /** Tenant the principal acts within. */
14
+ tenantId: string | null;
15
+ /**
16
+ * The persona's tool allow-list (already capped by the agent-class ceiling).
17
+ * This is a **fail-closed** whitelist, mirroring
18
+ * `@happyvertical/smrt-chat`'s `AgentSession.isToolAllowed()` (S5 #1392): an
19
+ * absent (`undefined`) or empty allow-list permits **NO** tools, never all of
20
+ * them, so forgetting to pass it can only tighten authority. Resolved personas
21
+ * always provide a concrete `string[]`.
22
+ */
23
+ allowedTools?: string[];
24
+ /** Optional acting `Bot` profile id, recorded in the audit entry. */
25
+ actsAsProfileId?: string | null;
26
+ }
27
+ /**
28
+ * A single audit record describing an agent action performed by the bound
29
+ * principal (`actorUserId`) on behalf of the originating user
30
+ * (`onBehalfOfUserId`).
31
+ */
32
+ export interface PrincipalAuditEntry {
33
+ /** Action label, e.g. `'agent.run'`. */
34
+ action: string;
35
+ /** The persona's bound user the work ran as. */
36
+ actorUserId: string;
37
+ /** The user who triggered the agent, if known. */
38
+ onBehalfOfUserId: string | null;
39
+ /** Tenant the action ran within. */
40
+ tenantId: string | null;
41
+ /** Canonical agent class, when the caller supplies it. */
42
+ agentClass?: string;
43
+ /** Acting profile id, when the persona sets one. */
44
+ actsAsProfileId?: string | null;
45
+ /** Free-form additional context. */
46
+ metadata?: Record<string, unknown>;
47
+ }
48
+ /**
49
+ * Sink that records a {@link PrincipalAuditEntry}. Provide one to persist audit
50
+ * rows (e.g. via `AuditLog.record`); when omitted, the entry is emitted as a
51
+ * structured log line.
52
+ */
53
+ export type PrincipalAuditSink = (entry: PrincipalAuditEntry) => void | Promise<void>;
54
+ /**
55
+ * Options for {@link executeAsPrincipal}.
56
+ */
57
+ export interface ExecuteAsPrincipalOptions extends SmrtClassOptions {
58
+ /** The bound principal to run as. */
59
+ principal: PrincipalBinding;
60
+ /** The originating user the action is performed on behalf of (for audit). */
61
+ onBehalfOfUserId?: string | null;
62
+ /** Canonical agent class, recorded in the audit entry. */
63
+ agentClass?: string;
64
+ /** Audit action label. Defaults to `'agent.run'`. */
65
+ action?: string;
66
+ /** Extra audit metadata merged into the emitted entry. */
67
+ auditMetadata?: Record<string, unknown>;
68
+ /**
69
+ * Pre-resolved permission slugs. When omitted, the principal's permissions
70
+ * are resolved live so role changes reflect on the next execution.
71
+ */
72
+ permissions?: string[];
73
+ /** Reuse an initialized resolver across executions. */
74
+ resolver?: PermissionResolver;
75
+ /** Opt into Postgres RLS transaction wrapping (defaults to package config). */
76
+ postgresRls?: boolean;
77
+ /**
78
+ * Enter tenant context so tenant auto-filtering applies on every adapter.
79
+ * Defaults to `true` whenever the principal has a tenant.
80
+ */
81
+ enterTenantContext?: boolean;
82
+ /** Audit sink. Defaults to a structured log line. */
83
+ audit?: PrincipalAuditSink;
84
+ /** Logger used for the default audit sink. */
85
+ logger?: Logger;
86
+ }
87
+ /**
88
+ * Thrown when the persona attempts a tool outside its `allowedTools`.
89
+ */
90
+ export declare class PrincipalToolNotAllowedError extends Error {
91
+ readonly tool: string;
92
+ readonly status = 403;
93
+ constructor(tool: string);
94
+ }
95
+ /**
96
+ * The handle passed to the {@link executeAsPrincipal} body. Its
97
+ * session-permission {@link context} is already published for the principal, so
98
+ * data operations are bounded by RLS on Postgres. The assertions enforce the
99
+ * remaining two authority dimensions.
100
+ */
101
+ export interface PrincipalRun {
102
+ /** The published session-permission runtime context for the principal. */
103
+ context: SessionPermissionRuntimeContext;
104
+ /** The principal's published (snapshot) permission slugs. */
105
+ permissions: string[];
106
+ /**
107
+ * The effective, fail-closed tool allow-list — always a concrete array (an
108
+ * absent binding allow-list normalizes to `[]`, i.e. no tools).
109
+ */
110
+ allowedTools: string[];
111
+ /**
112
+ * Whether `tool` is within the fail-closed allow-list. An empty allow-list,
113
+ * or an empty/non-string tool name, permits nothing.
114
+ */
115
+ isToolAllowed(tool: string): boolean;
116
+ /** Throw {@link PrincipalToolNotAllowedError} unless `tool` is allowed. */
117
+ assertToolAllowed(tool: string): void;
118
+ /**
119
+ * Assert the principal holds the catalog permission for `(collection,
120
+ * action)`, authorizing against the **published** principal set
121
+ * (`context.permissionSet`) — the same snapshot the RLS session enforces — so
122
+ * the bound is adapter-independent. This is the door-agnostic teeth for the
123
+ * RLS-off adapters; under Postgres RLS it is a redundant (but harmless)
124
+ * second gate. Throws `OperationPermissionError` on denial.
125
+ */
126
+ assertOperation(collection: OperationPermissionCollectionInput, action: string, extraOptions?: SmrtClassOptions): Promise<OperationPermissionDecision>;
127
+ }
128
+ /**
129
+ * Run `fn` AS the persona's bound principal.
130
+ *
131
+ * Resolves the bound user's live permissions, publishes them onto the DB
132
+ * session (so Postgres RLS bounds every query per-`(table, action)`), emits an
133
+ * on-behalf-of audit entry, and hands `fn` a {@link PrincipalRun} whose
134
+ * assertions enforce the persona tool ceiling and the RLS-off catalog gate.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * await executeAsPrincipal(
139
+ * {
140
+ * db,
141
+ * principal: {
142
+ * runAsUserId: persona.runAsUserId,
143
+ * tenantId: persona.tenantId,
144
+ * allowedTools: persona.allowedTools,
145
+ * },
146
+ * onBehalfOfUserId: triggeringUserId,
147
+ * agentClass: persona.agentClass,
148
+ * },
149
+ * async (run) => {
150
+ * run.assertToolAllowed('articles.publish');
151
+ * await run.assertOperation('articles', 'update');
152
+ * await agent.run();
153
+ * },
154
+ * );
155
+ * ```
156
+ */
157
+ export declare function executeAsPrincipal<T>(options: ExecuteAsPrincipalOptions, fn: (run: PrincipalRun) => Promise<T>): Promise<T>;
158
+ //# sourceMappingURL=execute-as-principal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute-as-principal.d.ts","sourceRoot":"","sources":["../src/execute-as-principal.ts"],"names":[],"mappings":"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"}
@@ -16,4 +16,18 @@ export declare function getAgentClassName(name: string): string;
16
16
  * while the simple class name keeps legacy rows discoverable during migration.
17
17
  */
18
18
  export declare function getAgentTypeAliases(name: string): string[];
19
+ /**
20
+ * Compose a per-instance dispatch subscriber identity from an agent type and an
21
+ * optional instance key (#1890).
22
+ *
23
+ * Multiple durable instances of one agent class each need their own subscriber
24
+ * name so their dispatch subscriptions and pending dispatches never collide —
25
+ * that is what keeps two instances from double-processing each other's work.
26
+ *
27
+ * Returns the bare `agentType` when `instanceKey` is nullish/empty, so a
28
+ * **singleton** agent's subscriber is byte-for-byte unchanged (the N=1 default).
29
+ * When a key is present the identity is `` `${agentType}#${instanceKey}` `` — a
30
+ * stable, reversible composition (the type never contains `#`).
31
+ */
32
+ export declare function instanceScopedSubscriber(agentType: string, instanceKey?: string | null): string;
19
33
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI1D"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,MAAM,CAER"}
package/dist/index.d.ts CHANGED
@@ -3,8 +3,11 @@ export { getClassConfigResolvers, getConfigResolver, isLazyConfigSentinel, listC
3
3
  export { Agent, type AgentOptions } from './agent.js';
4
4
  export { type AgentAIOptions, type AgentAISecretFallback, resolveAgentAIOptions, } from './ai-config.js';
5
5
  export { AgentConfig, AgentConfigCollection } from './config.js';
6
+ export { type ExecuteAsPrincipalOptions, executeAsPrincipal, type PrincipalAuditEntry, type PrincipalAuditSink, type PrincipalBinding, type PrincipalRun, PrincipalToolNotAllowedError, } from './execute-as-principal.js';
7
+ export { instanceScopedSubscriber } from './identity.js';
6
8
  export type { AgentWithInterestsOptions, AsyncQualifierFn, InterestFilter, InterestHandlerFn, InterestOptions, InterestResult, ObjectFilter, ObjectInterestConfig, QueryFn, } from './interests.js';
7
9
  export { mergeFilters, normalizeSort } from './interests.js';
10
+ export { type AgentLearningConfig, type AgentLearningDeclaration, type ResolvedAgentLearning, resolveAgentLearning, } from './learning.js';
8
11
  export { AgentSchedule, AgentScheduleCollection, type ScheduleStatus, } from './schedule.js';
9
12
  export type { SummaryArticleImage, SummaryArticleOptions, SummaryArticleResult, } from './summary-article.js';
10
13
  export { type ResolvedAgentAvailability, TenantAgent, TenantAgentCollection, type TenantAgentStatus, } from './tenant-agent.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAKH,OAAO,wBAAwB,CAAC;AAIhC,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EACV,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,OAAO,GACR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,yBAAyB,EAC9B,WAAW,EACX,qBAAqB,EACrB,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAKH,OAAO,wBAAwB,CAAC;AAIhC,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,KAAK,yBAAyB,EAC9B,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,OAAO,GACR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAG7D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,yBAAyB,EAC9B,WAAW,EACX,qBAAqB,EACrB,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}