@happyvertical/smrt-agents 0.38.21 → 0.38.22

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.
@@ -0,0 +1,276 @@
1
+ import { AITool } from '@happyvertical/ai';
2
+ import { Logger } from '@happyvertical/logger';
3
+ import { DispatchBus, SmrtClassOptions } from '@happyvertical/smrt-core';
4
+ import { DelegationEnvelope } from './delegation.js';
5
+ import { PrincipalAuditSink, PrincipalRun } from './execute-as-principal.js';
6
+ /** Catalog slug + permission id of the standard invoke-agent tool. */
7
+ export declare const INVOKE_AGENT_TOOL_SLUG = "agents.invoke";
8
+ /**
9
+ * Provider-safe function name the model receives for the invoke-agent tool.
10
+ * Catalog slugs contain a `.` which some providers (OpenAI) reject in function
11
+ * names; the loop resolves a call by either the slug or this name.
12
+ */
13
+ export declare const INVOKE_AGENT_FUNCTION_NAME = "agents-invoke";
14
+ /** DispatchBus signal prefix a worker is invoked through in the async transport. */
15
+ export declare const AGENT_INVOKE_SIGNAL = "agent.invoke";
16
+ /** DispatchBus signal a worker emits to report completion, correlated by id. */
17
+ export declare const AGENT_COMPLETED_SIGNAL = "agent.completed";
18
+ /**
19
+ * The **per-worker** signal type an async invocation is emitted on, so a
20
+ * processor only ever claims invocations for the worker class it serves.
21
+ *
22
+ * The async transport emits `agent.invoke.<agentClass>` (the class rendered as a
23
+ * single, provider-safe signal segment) rather than the bare `agent.invoke`.
24
+ * DispatchBus `process()` claims pending rows by *subscribed signal type* before
25
+ * a handler can inspect the payload, so a processor targeting worker A
26
+ * (subscribed to `agent.invoke.<A>`) can never claim a worker-B invocation
27
+ * (`agent.invoke.<B>`). A generic processor that handles every class subscribes
28
+ * to the single-segment wildcard `agent.invoke.*`.
29
+ */
30
+ export declare function agentInvokeSignalType(agentClass: string): string;
31
+ /**
32
+ * A tool executed under a {@link PrincipalRun} that is not a manifest CRUD
33
+ * operation — e.g. the orchestration invoke-agent tool. It carries its own AI
34
+ * definition and handler, and is offered through the conversational tool loop
35
+ * alongside manifest tools, gated by the same fail-closed `allowedTools`.
36
+ *
37
+ * Defined here (in `@happyvertical/smrt-agents`) rather than in the chat loop so
38
+ * the acyclic `chat → agents` dependency direction is preserved: the loop
39
+ * imports this contract, agents produces implementations of it.
40
+ */
41
+ export interface PrincipalTool {
42
+ /** Tool name + permission slug, gated by the persona's `allowedTools`. */
43
+ slug: string;
44
+ /** The provider tool definition offered to the model. */
45
+ aiTool: AITool;
46
+ /** Execute the tool under the principal run (should re-assert its own gate). */
47
+ execute(ctx: PrincipalToolContext): Promise<unknown>;
48
+ }
49
+ /** Context handed to a {@link PrincipalTool.execute}. */
50
+ export interface PrincipalToolContext {
51
+ /** The principal run whose context bounds this execution. */
52
+ run: PrincipalRun;
53
+ /** Parsed tool arguments. */
54
+ args: Record<string, unknown>;
55
+ /** The database handle for side-door operations. */
56
+ db?: SmrtClassOptions['db'];
57
+ }
58
+ /** A worker invocation handed to a {@link WorkerRunner}. */
59
+ export interface WorkerInvocation {
60
+ /** The principal run the worker executes within (the delegated principal). */
61
+ run: PrincipalRun;
62
+ /** The delegation envelope (principal, depth, correlation). */
63
+ envelope: DelegationEnvelope;
64
+ /** The target worker agent class. */
65
+ agentClass: string;
66
+ /** The task payload handed to the worker. */
67
+ task: Record<string, unknown>;
68
+ /** The database handle for the worker's operations. */
69
+ db?: SmrtClassOptions['db'];
70
+ }
71
+ /**
72
+ * Performs a worker's actual work under the delegated principal. Injected so
73
+ * orchestration stays decoupled from *what* a worker does (run an `Agent`, run a
74
+ * nested persona conversation, call a domain method); the runner receives a
75
+ * {@link PrincipalRun} already bound to the originating user's permissions.
76
+ */
77
+ export type WorkerRunner = (invocation: WorkerInvocation) => Promise<unknown>;
78
+ /**
79
+ * A worker's completion, correlated back to the invocation that produced it.
80
+ */
81
+ export interface AgentCompletion {
82
+ /** Correlates this completion to the invocation. */
83
+ correlationId: string;
84
+ /** The worker agent class that ran. */
85
+ agentClass: string;
86
+ /** The originating user the worker acted on behalf of. */
87
+ onBehalfOfUserId: string;
88
+ /** Whether the worker's work succeeded. */
89
+ ok: boolean;
90
+ /** The worker's result, when it succeeded. */
91
+ result?: unknown;
92
+ /** The error message, when it failed. */
93
+ error?: string;
94
+ }
95
+ /** The outcome the invoke-agent tool returns to the conversation. */
96
+ export interface InvokeAgentResult {
97
+ /**
98
+ * `completed` / `failed` for an in-process (inline) invocation whose result is
99
+ * surfaced in the same turn; `enqueued` for an async transport whose
100
+ * completion is surfaced later via {@link surfaceAgentCompletions}.
101
+ */
102
+ status: 'completed' | 'failed' | 'enqueued';
103
+ /** Correlates a later completion dispatch back to this invocation. */
104
+ correlationId: string;
105
+ /** The worker agent class invoked. */
106
+ agentClass: string;
107
+ /** The delegation depth of the invoked worker. */
108
+ depth: number;
109
+ /** The worker's result, when it completed in-process. */
110
+ result?: unknown;
111
+ /** The error message, when it failed in-process. */
112
+ error?: string;
113
+ }
114
+ /** A delivery handed to an {@link InvokeAgentTransport}. */
115
+ export interface InvokeAgentDelivery {
116
+ /** The child delegation envelope for the worker. */
117
+ envelope: DelegationEnvelope;
118
+ /** The target worker agent class. */
119
+ agentClass: string;
120
+ /** The task payload for the worker. */
121
+ task: Record<string, unknown>;
122
+ /** The worker runner (used by in-process transports; ignored by async ones). */
123
+ worker: WorkerRunner;
124
+ /** The database handle. */
125
+ db?: SmrtClassOptions['db'];
126
+ /** DispatchBus for the correlated invoke/completion signals. */
127
+ dispatchBus?: DispatchBus;
128
+ /** Audit sink forwarded to {@link executeAsPrincipal}. */
129
+ audit?: PrincipalAuditSink;
130
+ /** Opt into Postgres RLS transaction wrapping. */
131
+ postgresRls?: boolean;
132
+ /** Logger for the default audit sink. */
133
+ logger?: Logger;
134
+ }
135
+ /**
136
+ * How a worker invocation is delivered: run it in-process now (inline), emit a
137
+ * DispatchBus signal for a worker to process, or enqueue a job. Swapping the
138
+ * transport never changes the principal-delegation or completion semantics.
139
+ */
140
+ export interface InvokeAgentTransport {
141
+ deliver(delivery: InvokeAgentDelivery): Promise<InvokeAgentResult>;
142
+ }
143
+ /**
144
+ * Run a worker as the delegated principal and report its completion.
145
+ *
146
+ * The worker executes inside a single {@link executeAsPrincipal} context bound
147
+ * to the envelope's principal (`runAsUserId` + `tenantId`) and acting **on
148
+ * behalf of** the originating user — so its authority is the originating user's
149
+ * live RBAC and every action audits back to that user. On completion (success
150
+ * or failure) a correlated `agent.completed` dispatch is emitted **inside** the
151
+ * principal's tenant context, so it is stamped with the right tenant and the
152
+ * orchestrator can surface it back into the conversation.
153
+ */
154
+ export declare function executeDelegatedInvocation(options: {
155
+ envelope: DelegationEnvelope;
156
+ agentClass: string;
157
+ task: Record<string, unknown>;
158
+ worker: WorkerRunner;
159
+ db?: SmrtClassOptions['db'];
160
+ dispatchBus?: DispatchBus;
161
+ audit?: PrincipalAuditSink;
162
+ postgresRls?: boolean;
163
+ logger?: Logger;
164
+ }): Promise<AgentCompletion>;
165
+ /**
166
+ * Emit a correlated `agent.completed` dispatch for a worker's completion.
167
+ */
168
+ export declare function emitAgentCompletion(dispatchBus: DispatchBus, completion: AgentCompletion): Promise<void>;
169
+ /**
170
+ * Read the correlated completions for an invocation, so the orchestrator can
171
+ * surface a worker's result back into the conversation on a later turn (the
172
+ * async transport). Returns `[]` when nothing has completed yet.
173
+ */
174
+ export declare function surfaceAgentCompletions(dispatchBus: DispatchBus, correlationId: string): Promise<AgentCompletion[]>;
175
+ /**
176
+ * The default transport: run the worker in-process now and return its completion
177
+ * as the tool observation, so the result is surfaced back into the conversation
178
+ * in the same turn.
179
+ */
180
+ export declare const inlineInvokeAgentTransport: InvokeAgentTransport;
181
+ /**
182
+ * An async transport that emits a correlated, **per-worker** `agent.invoke.<class>`
183
+ * DispatchBus signal for a worker to process out of band
184
+ * ({@link processAgentInvocations}). The tool returns `enqueued`; the worker's
185
+ * completion is surfaced later via {@link surfaceAgentCompletions}. The worker
186
+ * runner is *not* used here — it is reconstructed on the processing side.
187
+ *
188
+ * Emitting on the per-worker signal type (see {@link agentInvokeSignalType})
189
+ * means a processor for worker A never claims an invocation targeted at worker
190
+ * B, even under compete delivery.
191
+ */
192
+ export declare function createDispatchInvokeTransport(dispatchBus: DispatchBus, options?: {
193
+ source?: string;
194
+ }): InvokeAgentTransport;
195
+ /**
196
+ * Process pending `agent.invoke` signals, running each worker as its delegated
197
+ * principal and emitting the correlated completion. This is the worker side of
198
+ * {@link createDispatchInvokeTransport}.
199
+ *
200
+ * Pass `agentClass` to target a single worker class — the processor subscribes
201
+ * to `agent.invoke.<class>` and can only ever claim that class's invocations, so
202
+ * running one processor per worker class never cross-claims. Omit it for a
203
+ * generic processor that handles every class (subscribes to the wildcard
204
+ * `agent.invoke.*` and dispatches on the payload's `agentClass`).
205
+ *
206
+ * The envelope arrives from a (persisted, thus untrusted) dispatch payload, so
207
+ * it is validated ({@link isValidDelegationEnvelope}) and its depth re-asserted
208
+ * before the worker runs — a malformed or tampered envelope cannot drive the
209
+ * chain past {@link MAX_DELEGATION_DEPTH}.
210
+ *
211
+ * @returns The number of invocations processed.
212
+ */
213
+ export declare function processAgentInvocations(options: {
214
+ dispatchBus: DispatchBus;
215
+ subscriber: string;
216
+ worker: WorkerRunner;
217
+ /** Target a single worker class; omit for a handle-every-class processor. */
218
+ agentClass?: string;
219
+ db?: SmrtClassOptions['db'];
220
+ audit?: PrincipalAuditSink;
221
+ postgresRls?: boolean;
222
+ logger?: Logger;
223
+ limit?: number;
224
+ }): Promise<number>;
225
+ /**
226
+ * Options for {@link createInvokeAgentTool}.
227
+ */
228
+ export interface CreateInvokeAgentToolOptions {
229
+ /**
230
+ * The **current run's** delegation envelope — the orchestrator's own (depth
231
+ * `0`) when building the tool for a conversation, or a worker's own envelope
232
+ * when building it for a nested/further delegation. Its principal is the
233
+ * ceiling every child inherits; the live run context is the source of truth
234
+ * for the principal and overrides this copy.
235
+ */
236
+ parentEnvelope: DelegationEnvelope;
237
+ /** The worker runner used by in-process transports. */
238
+ worker: WorkerRunner;
239
+ /** Database handle for the worker's operations. */
240
+ db?: SmrtClassOptions['db'];
241
+ /** DispatchBus for correlated invoke/completion signals. */
242
+ dispatchBus?: DispatchBus;
243
+ /** Delivery transport. Defaults to {@link inlineInvokeAgentTransport}. */
244
+ transport?: InvokeAgentTransport;
245
+ /** Audit sink forwarded to {@link executeAsPrincipal}. */
246
+ audit?: PrincipalAuditSink;
247
+ /** Opt into Postgres RLS transaction wrapping. */
248
+ postgresRls?: boolean;
249
+ /** Logger for the default audit sink. */
250
+ logger?: Logger;
251
+ /** Resolve a worker's tool ceiling from its class (e.g. its persona tools). */
252
+ resolveWorkerAllowedTools?: (agentClass: string) => string[] | undefined;
253
+ /** Depth ceiling override (mainly for tests). */
254
+ maxDepth?: number;
255
+ /** Override the tool description offered to the model. */
256
+ description?: string;
257
+ }
258
+ /**
259
+ * Build the standard **invoke-agent** tool.
260
+ *
261
+ * Offered through the conversational tool loop and gated by the persona's
262
+ * `allowedTools` (the model may only call it when `agents.invoke` is
263
+ * allow-listed). Its handler:
264
+ *
265
+ * 1. re-asserts the fail-closed allow-list ({@link PrincipalRun.assertToolAllowed});
266
+ * 2. derives the child {@link DelegationEnvelope} with the principal taken
267
+ * **from the live run context** — never from the tool arguments — so a worker
268
+ * cannot widen the principal, and increments the bounded depth;
269
+ * 3. delivers the invocation via the configured transport.
270
+ *
271
+ * The child inherits the orchestrator's principal verbatim and acts on behalf of
272
+ * the same originating user, so the worker runs under the originating user's
273
+ * permissions and audits back to them.
274
+ */
275
+ export declare function createInvokeAgentTool(options: CreateInvokeAgentToolOptions): PrincipalTool;
276
+ //# sourceMappingURL=invoke-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoke-agent.d.ts","sourceRoot":"","sources":["../src/invoke-agent.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1783580636247,
3
+ "timestamp": 1783586698251,
4
4
  "packageName": "@happyvertical/smrt-agents",
5
- "packageVersion": "0.38.21",
5
+ "packageVersion": "0.38.22",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-agents:Agent": {
8
8
  "name": "agent",
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-07-09T07:03:56.484Z",
3
+ "generatedAt": "2026-07-09T08:44:58.502Z",
4
4
  "packageName": "@happyvertical/smrt-agents",
5
- "packageVersion": "0.38.21",
5
+ "packageVersion": "0.38.22",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "3c56ab318e3669bec89b08af7060ae95444f3a304de38d9f8bd689d9772f4c84",
10
- "packageJson": "33d7d7df21c1e76ed0e4e47096feef1de05866eccfcd305a3403672989a4d04b",
11
- "agents": "6c364bc8d0d0867a99878ce3bedc6058fd9f30314755b04730e9eb8aa08e52ce"
9
+ "manifest": "702be59833db3b355d620b7d41cdafeaa5514d94ec2025d857fab4ccc392d992",
10
+ "packageJson": "8f0a1a6f52bbb78e9819dbdc401e3ddc4a09eb7fc355e6be2d822c2fdbdd7ccb",
11
+ "agents": "0abf501785ff63b6c01fe714286761181f8ce4de1c08b689e6f691423dc25a87"
12
12
  },
13
13
  "exports": [
14
14
  ".",
@@ -983,5 +983,5 @@
983
983
  "polymorphicAssociations": 0,
984
984
  "uuidColumns": 11
985
985
  },
986
- "agentDoc": "# @happyvertical/smrt-agents\n\nAgent framework for autonomous actors with inter-agent messaging, interest-based object discovery, scheduling, and multi-tenant bindings.\n\n## Agent Lifecycle\n\n`initialize()` → `validate()` → `run()` → `shutdown()`\n\n- Extend `Agent` (which extends `SmrtObject`) and implement `run()`\n- Status tracking: `idle → initializing → running → error/shutdown`\n- `execute()` runs the full lifecycle automatically\n- Process signal handling is opt-in via `manageProcessSignals: true` and is intended for single-agent processes\n\n## DispatchBus — Inter-Agent Communication\n\nAgents communicate via persistent async messaging through core's DispatchBus:\n\n```typescript\n// Emitting (in any agent)\nconst bus = await this.getDispatch();\nawait bus.emit('campaign.completed', { campaignId: '123' }, { source: 'Suasor' });\n\n// Subscribing (in receiving agent)\nasync handleDispatch(payload: unknown, metadata: DispatchMetadata): Promise<void> {\n if (metadata.type === 'campaign.completed') await this.recordRevenue(payload);\n}\nasync run() { await this.processDispatches(); } // processes via handleDispatch()\n```\n\nCLI: `smrt dispatch:list`, `dispatch:process --subscriber Fiscus`, `dispatch:retry`, `dispatch:cleanup`\n\n## Interests — Object Discovery\n\nAgents query objects they care about via declarative filters:\n\n```typescript\nconstructor(options) {\n super({ ...options, interests: {\n objects: { Meeting: { filter: { status: 'upcoming' }, handler: async (m) => ({ action: 'recap' }) } },\n qualify: async (items) => items.filter(/* AI-based post-filter */),\n }});\n}\nasync run() { for (const { type, data } of await this.interesting()) { ... } }\n```\n\n## Configuration\n\n- **File-based**: `getModuleConfig('agent-name', defaults)` from `smrt.config.ts`\n- **DB-persisted**: `saveSlotConfig(slotId, data)` for UI overrides\n- **Merged**: `getMergedConfig('slotId')` — DB overrides file config\n- **UI slots**: `static uiSlots` declares admin panels (id, label, icon, order)\n\n## TenantAgent — Multi-Tenant Bindings\n\nJunction table (`tenant_agents`) binding agents to tenants with permission overrides and hierarchy resolution:\n- Explicit binding: row exists for tenant (source: 'explicit')\n- Inherited: walks up tenant hierarchy (source: 'inherited')\n- Permissions: manifest defaults merged with per-tenant overrides\n\n## AgentSchedule\n\nCron-based scheduling stored in `_smrt_agent_schedules`. Fields: `agentType`, `cron`, `method` (default: 'run'), `maxConcurrent`, `timeout`. Executed by ScheduleRunner from smrt-jobs.\n\n## Lazy agent_config Resolution (issue #1161)\n\nPersisted `agent_config` snapshots env-derived values at sync time, so rotated env vars don't reach already-stored schedule rows. Two complementary mechanisms unfreeze them:\n\n1. **`$env` sentinels in persisted config** — register a global resolver and reference it from the JSON:\n\n ```ts\n import { registerConfigResolver } from '@happyvertical/smrt-agents';\n registerConfigResolver('sharedAssetStorage', () => resolveSharedAssetStorage());\n // persisted: { \"assetStorage\": { \"$env\": \"sharedAssetStorage\" } }\n ```\n\n2. **`static configResolvers` on the agent class** — declarative, discoverable via the class itself:\n\n ```ts\n class Praeco extends Agent {\n static override configResolvers = {\n assetStorage: () => resolveSharedAssetStorage(),\n };\n }\n ```\n\nThe 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.\n\n## Learning Trait (issue #1886) — opt-in\n\nAny 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.\n\n```typescript\n@smrt()\nclass InvoiceAgent extends Agent {\n static override learning = true; // or { minConfidence: 0.8, scope: 'invoices', ... }\n protected config = {};\n\n async run() {\n // recall-before-run already populated `recalledMemories` (confidence >= floor)\n const cached = this.recalledMemories.find((m) => m.key === this.docUrl);\n const strategy = cached?.value ?? (await this.generateStrategy());\n\n // stage the episode; the lifecycle reinforces it after run()\n this.stageLearning({ scope: this.learningScope(), key: this.docUrl, value: strategy });\n\n // a validated failure decays the memory without throwing\n if (!ok) this.reportLearningOutcome({ success: false, error: 'no match' });\n }\n}\n```\n\n- **`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.\n- **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`.\n- **Seams to override**: `learningScope()`, `recallForRun(memory)`, `captureForRun(memory, outcome)`, `getLearningSemanticSearch()`. Helpers for `run()`: `stageLearning(episode)`, `reportLearningOutcome(outcome)`, `getLearningMemory()`, and the `recalledMemories` field.\n- **Config**: `static learning: boolean | AgentLearningConfig` — `{ enabled?, scope?, minConfidence?, successConfidence?, failureConfidence?, reinforcement?, decayHalfLifeMs? }`. `LearningMemory` and its types are re-exported from `@happyvertical/smrt-core`.\n\n## Multi-Instance Agents (issue #1890) — opt-in\n\n`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.\n\nThe framework provides only the per-instance **identity**; a package scopes its own dispatch/interests to the instance's config by overriding the seams.\n\n- **`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.\n- **`getInstanceKey()`** → the key, or `null` for a singleton (opt-in off, or no key).\n- **`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)`.\n- **`learningScope()`** — suffixed with `#<key>` for a multi-instance agent, so instances learn independently (singleton scope unchanged).\n- **Seams to override** (both default to singleton behavior):\n - `resolveSignalSubscriptions()` — derive **instance-scoped** signal types from the instance config so an emit meant for one instance only matches its subscription.\n - `instanceInterestFilter()` — an `ObjectFilter` AND-merged (as the base layer) into every `interesting()` query so instances partition the objects they process.\n\nThe **`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`).\n\n## Key Files\n\n| File | Purpose |\n|------|---------|\n| `src/agent.ts` | Base Agent class — lifecycle, dispatch, interests, config, opt-in learning trait, multi-instance identity |\n| `src/learning.ts` | `AgentLearningConfig` + `resolveAgentLearning()` declaration normalisation |\n| `src/schedule.ts` | AgentSchedule model — cron, execution tracking |\n| `src/tenant-agent.ts` | TenantAgent — junction table, hierarchical resolution |\n| `src/interests.ts` | Interest filter types and configuration |\n| `src/config.ts` | File + DB config management, UI slots |\n"
986
+ "agentDoc": "# @happyvertical/smrt-agents\n\nAgent framework for autonomous actors with inter-agent messaging, interest-based object discovery, scheduling, and multi-tenant bindings.\n\n## Agent Lifecycle\n\n`initialize()` → `validate()` → `run()` → `shutdown()`\n\n- Extend `Agent` (which extends `SmrtObject`) and implement `run()`\n- Status tracking: `idle → initializing → running → error/shutdown`\n- `execute()` runs the full lifecycle automatically\n- Process signal handling is opt-in via `manageProcessSignals: true` and is intended for single-agent processes\n\n## DispatchBus — Inter-Agent Communication\n\nAgents communicate via persistent async messaging through core's DispatchBus:\n\n```typescript\n// Emitting (in any agent)\nconst bus = await this.getDispatch();\nawait bus.emit('campaign.completed', { campaignId: '123' }, { source: 'Suasor' });\n\n// Subscribing (in receiving agent)\nasync handleDispatch(payload: unknown, metadata: DispatchMetadata): Promise<void> {\n if (metadata.type === 'campaign.completed') await this.recordRevenue(payload);\n}\nasync run() { await this.processDispatches(); } // processes via handleDispatch()\n```\n\nCLI: `smrt dispatch:list`, `dispatch:process --subscriber Fiscus`, `dispatch:retry`, `dispatch:cleanup`\n\n## Interests — Object Discovery\n\nAgents query objects they care about via declarative filters:\n\n```typescript\nconstructor(options) {\n super({ ...options, interests: {\n objects: { Meeting: { filter: { status: 'upcoming' }, handler: async (m) => ({ action: 'recap' }) } },\n qualify: async (items) => items.filter(/* AI-based post-filter */),\n }});\n}\nasync run() { for (const { type, data } of await this.interesting()) { ... } }\n```\n\n## Configuration\n\n- **File-based**: `getModuleConfig('agent-name', defaults)` from `smrt.config.ts`\n- **DB-persisted**: `saveSlotConfig(slotId, data)` for UI overrides\n- **Merged**: `getMergedConfig('slotId')` — DB overrides file config\n- **UI slots**: `static uiSlots` declares admin panels (id, label, icon, order)\n\n## TenantAgent — Multi-Tenant Bindings\n\nJunction table (`tenant_agents`) binding agents to tenants with permission overrides and hierarchy resolution:\n- Explicit binding: row exists for tenant (source: 'explicit')\n- Inherited: walks up tenant hierarchy (source: 'inherited')\n- Permissions: manifest defaults merged with per-tenant overrides\n\n## AgentSchedule\n\nCron-based scheduling stored in `_smrt_agent_schedules`. Fields: `agentType`, `cron`, `method` (default: 'run'), `maxConcurrent`, `timeout`. Executed by ScheduleRunner from smrt-jobs.\n\n## Lazy agent_config Resolution (issue #1161)\n\nPersisted `agent_config` snapshots env-derived values at sync time, so rotated env vars don't reach already-stored schedule rows. Two complementary mechanisms unfreeze them:\n\n1. **`$env` sentinels in persisted config** — register a global resolver and reference it from the JSON:\n\n ```ts\n import { registerConfigResolver } from '@happyvertical/smrt-agents';\n registerConfigResolver('sharedAssetStorage', () => resolveSharedAssetStorage());\n // persisted: { \"assetStorage\": { \"$env\": \"sharedAssetStorage\" } }\n ```\n\n2. **`static configResolvers` on the agent class** — declarative, discoverable via the class itself:\n\n ```ts\n class Praeco extends Agent {\n static override configResolvers = {\n assetStorage: () => resolveSharedAssetStorage(),\n };\n }\n ```\n\nThe 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.\n\n## Learning Trait (issue #1886) — opt-in\n\nAny 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.\n\n```typescript\n@smrt()\nclass InvoiceAgent extends Agent {\n static override learning = true; // or { minConfidence: 0.8, scope: 'invoices', ... }\n protected config = {};\n\n async run() {\n // recall-before-run already populated `recalledMemories` (confidence >= floor)\n const cached = this.recalledMemories.find((m) => m.key === this.docUrl);\n const strategy = cached?.value ?? (await this.generateStrategy());\n\n // stage the episode; the lifecycle reinforces it after run()\n this.stageLearning({ scope: this.learningScope(), key: this.docUrl, value: strategy });\n\n // a validated failure decays the memory without throwing\n if (!ok) this.reportLearningOutcome({ success: false, error: 'no match' });\n }\n}\n```\n\n- **`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.\n- **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`.\n- **Seams to override**: `learningScope()`, `recallForRun(memory)`, `captureForRun(memory, outcome)`, `getLearningSemanticSearch()`. Helpers for `run()`: `stageLearning(episode)`, `reportLearningOutcome(outcome)`, `getLearningMemory()`, and the `recalledMemories` field.\n- **Config**: `static learning: boolean | AgentLearningConfig` — `{ enabled?, scope?, minConfidence?, successConfidence?, failureConfidence?, reinforcement?, decayHalfLifeMs? }`. `LearningMemory` and its types are re-exported from `@happyvertical/smrt-core`.\n\n## Multi-Instance Agents (issue #1890) — opt-in\n\n`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.\n\nThe framework provides only the per-instance **identity**; a package scopes its own dispatch/interests to the instance's config by overriding the seams.\n\n- **`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.\n- **`getInstanceKey()`** → the key, or `null` for a singleton (opt-in off, or no key).\n- **`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)`.\n- **`learningScope()`** — suffixed with `#<key>` for a multi-instance agent, so instances learn independently (singleton scope unchanged).\n- **Seams to override** (both default to singleton behavior):\n - `resolveSignalSubscriptions()` — derive **instance-scoped** signal types from the instance config so an emit meant for one instance only matches its subscription.\n - `instanceInterestFilter()` — an `ObjectFilter` AND-merged (as the base layer) into every `interesting()` query so instances partition the objects they process.\n\nThe **`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`).\n\n## Principal Execution (issue #1888)\n\n`executeAsPrincipal(options, fn)` runs agent work **AS a persona's bound user**, reusing the existing RBAC cascade with no snapshotting. It publishes `(user_id, tenant_id, permissions[])` onto the DB session (Postgres RLS then bounds every query per-`(table, action)` and per-tenant) and hands `fn` a `PrincipalRun` whose `assertToolAllowed()` / `assertOperation()` enforce the persona tool ceiling and the RLS-off catalog gate. Effective authority = **bound-user RBAC ∩ agent-class ceiling ∩ persona `allowedTools`**. Actions audit as on-behalf-of the originating user via a `PrincipalAuditSink`.\n\n## Agent Orchestration (issue #1892) — invoke-agent + principal delegation\n\nA conversational (orchestrator) agent can invoke worker agents with **principal delegation**. This is *not* a new engine — it is a standard `invoke-agent` tool plus a completion-dispatch convention on top of `executeAsPrincipal` + the DispatchBus.\n\n```typescript\nimport { createInvokeAgentTool, rootDelegationEnvelope } from '@happyvertical/smrt-agents';\n\nconst tool = createInvokeAgentTool({\n db,\n parentEnvelope: rootDelegationEnvelope({ runAsUserId, tenantId, onBehalfOfUserId }),\n worker: async ({ run, agentClass, task }) => runWorker(run, agentClass, task),\n});\n// Offered through the chat tool loop as an `extraTools` entry, gated by the\n// persona's allowedTools like any other tool (slug: 'agents.invoke').\n```\n\n- **`DelegationEnvelope`** carries the **immutable principal** (`runAsUserId` + `tenantId` + originating `onBehalfOfUserId`) and a bounded `depth`. `deriveDelegationEnvelope()` copies the principal verbatim and asserts `depth <= MAX_DELEGATION_DEPTH` (3) — a worker cannot invoke a further worker under a broader principal (`PrincipalWideningError` / `DelegationDepthExceededError`).\n- **`createInvokeAgentTool()`** → a `PrincipalTool` whose handler derives the child envelope with the principal taken **from the live run context, never the tool args**, so the invoke-agent tool is structurally immune to principal widening.\n- **`executeDelegatedInvocation()`** runs the worker via `executeAsPrincipal` under that same principal and emits a correlated `agent.completed` dispatch; **`surfaceAgentCompletions(bus, correlationId)`** reads it back into the conversation.\n- **Transports** (pluggable): the default `inlineInvokeAgentTransport` runs the worker in-process (completion surfaces in the same turn); `createDispatchInvokeTransport(bus)` emits an `agent.invoke` signal a worker processes via `processAgentInvocations()` (async). A job-queue transport (enqueue on the `agents` queue) is a consumer-supplied `InvokeAgentTransport` — orchestration never hard-depends on `@happyvertical/smrt-jobs`, which sits *below* agents in the dependency graph.\n\n## Key Files\n\n| File | Purpose |\n|------|---------|\n| `src/agent.ts` | Base Agent class — lifecycle, dispatch, interests, config, opt-in learning trait, multi-instance identity |\n| `src/execute-as-principal.ts` | `executeAsPrincipal` / `PrincipalRun` — run agent work as a persona's bound user (#1888) |\n| `src/delegation.ts` | `DelegationEnvelope` — immutable principal + bounded delegation depth (#1892) |\n| `src/invoke-agent.ts` | `invoke-agent` tool, worker executor, completion-dispatch convention, transports (#1892) |\n| `src/learning.ts` | `AgentLearningConfig` + `resolveAgentLearning()` declaration normalisation |\n| `src/schedule.ts` | AgentSchedule model — cron, execution tracking |\n| `src/tenant-agent.ts` | TenantAgent — junction table, hierarchical resolution |\n| `src/interests.ts` | Interest filter types and configuration |\n| `src/config.ts` | File + DB config management, UI slots |\n"
987
987
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-agents",
3
- "version": "0.38.21",
3
+ "version": "0.38.22",
4
4
  "type": "module",
5
5
  "smrtRawPrimitives": "strict",
6
6
  "description": "Agent framework for building autonomous actors in the SMRT ecosystem",
@@ -59,13 +59,13 @@
59
59
  "@happyvertical/ai": "^0.77.0",
60
60
  "@happyvertical/files": "^0.77.0",
61
61
  "@happyvertical/utils": "^0.77.0",
62
- "@happyvertical/smrt-config": "0.38.21",
63
- "@happyvertical/smrt-secrets": "0.38.21",
64
- "@happyvertical/smrt-tenancy": "0.38.21",
65
- "@happyvertical/smrt-types": "0.38.21",
66
- "@happyvertical/smrt-core": "0.38.21",
67
- "@happyvertical/smrt-ui": "0.38.21",
68
- "@happyvertical/smrt-users": "0.38.21"
62
+ "@happyvertical/smrt-config": "0.38.22",
63
+ "@happyvertical/smrt-core": "0.38.22",
64
+ "@happyvertical/smrt-secrets": "0.38.22",
65
+ "@happyvertical/smrt-tenancy": "0.38.22",
66
+ "@happyvertical/smrt-types": "0.38.22",
67
+ "@happyvertical/smrt-ui": "0.38.22",
68
+ "@happyvertical/smrt-users": "0.38.22"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@happyvertical/logger": "^0.77.0",
@@ -82,7 +82,7 @@
82
82
  "typescript": "^5.9.3",
83
83
  "vite": "^8.1.3",
84
84
  "vitest": "^4.1.9",
85
- "@happyvertical/smrt-vitest": "0.38.21"
85
+ "@happyvertical/smrt-vitest": "0.38.22"
86
86
  },
87
87
  "keywords": [
88
88
  "agent",