@happyvertical/smrt-agents 0.51.39 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +22 -0
- package/dist/agent.d.ts +16 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/chunks/{execute-as-principal-uuJq7EFs.js → execute-as-principal-DtwCUq2t.js} +11 -2
- package/dist/chunks/execute-as-principal-DtwCUq2t.js.map +1 -0
- package/dist/data-surface.d.ts +21 -2
- package/dist/data-surface.d.ts.map +1 -1
- package/dist/identity.d.ts +19 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +510 -34
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -1
- package/dist/migrations/tenant-agent-config-keys.d.ts +143 -0
- package/dist/migrations/tenant-agent-config-keys.d.ts.map +1 -0
- package/dist/server/jobs-data-surface-action-queue.d.ts +12 -1
- package/dist/server/jobs-data-surface-action-queue.d.ts.map +1 -1
- package/dist/server/serialization.d.ts +7 -2
- package/dist/server/serialization.d.ts.map +1 -1
- package/dist/server.js +19 -9
- package/dist/server.js.map +1 -1
- package/dist/smrt-collection-data-surface.d.ts +32 -1
- package/dist/smrt-collection-data-surface.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +6 -6
- package/dist/ui.d.ts +94 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +33 -1
- package/dist/ui.js.map +1 -1
- package/package.json +19 -19
- package/dist/chunks/execute-as-principal-uuJq7EFs.js.map +0 -1
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* reimplemented as a thin wrapper over this adapter in this change — doing so
|
|
34
34
|
* safely needs that engine ported first. Tracked as a follow-up in #2912.
|
|
35
35
|
*/
|
|
36
|
-
import type { DataQueryResult, DataQuerySchema, DataQuerySort } from '@happyvertical/smrt-types';
|
|
36
|
+
import type { DataQueryFilter, DataQueryFilterOperator, DataQueryResult, DataQuerySchema, DataQuerySort } from '@happyvertical/smrt-types';
|
|
37
37
|
import type { DataSurfaceDefinition, DataSurfaceExecutionContext, DataSurfaceField, DataSurfaceSchema } from './data-surface.js';
|
|
38
38
|
/** One AND-ed group of SMRT `where` conditions. */
|
|
39
39
|
type WhereCondition = Record<string, unknown>;
|
|
@@ -125,6 +125,37 @@ export interface CreateSmrtCollectionDataSurfaceOptions {
|
|
|
125
125
|
/** Trusted policy override; defaults to the registry-derived schema. */
|
|
126
126
|
schema?: DataSurfaceSchema;
|
|
127
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* The operators an id (native `UUID` column) field accepts. Ordered and
|
|
130
|
+
* pattern comparisons have no meaning on an id — and PostgreSQL has no
|
|
131
|
+
* `uuid ~~ text` operator at all, so `like` reached the driver as a 500.
|
|
132
|
+
*/
|
|
133
|
+
export declare const ID_FIELD_FILTER_OPERATORS: DataQueryFilterOperator[];
|
|
134
|
+
/**
|
|
135
|
+
* The declared field ids of `qualifiedName` that live in a native `UUID`
|
|
136
|
+
* column, read from the class's manifest schema — the same column metadata
|
|
137
|
+
* `SmrtCollection` consults for its own UUID handling.
|
|
138
|
+
*/
|
|
139
|
+
export declare function uuidColumnFieldIds(qualifiedName: string, fieldIds: Iterable<string>): Set<string>;
|
|
140
|
+
/**
|
|
141
|
+
* Refuse a condition on an id field that could only fail in the database: an
|
|
142
|
+
* operator other than {@link ID_FIELD_FILTER_OPERATORS} (a host `schema`
|
|
143
|
+
* override may still declare one), or a value that is not a UUID, which
|
|
144
|
+
* PostgreSQL rejects with `invalid input syntax for type uuid` and which the
|
|
145
|
+
* caller could never have matched anyway. Both are the caller's error, so
|
|
146
|
+
* they fail as a `DataQueryValidationError` with an actionable message.
|
|
147
|
+
*/
|
|
148
|
+
export declare function assertIdFieldConditions(filter: DataQueryFilter | undefined, idFields: ReadonlySet<string>): void;
|
|
149
|
+
/**
|
|
150
|
+
* `SmrtCollection.list()` order terms for a normalized sort.
|
|
151
|
+
*
|
|
152
|
+
* The data-query contract orders an empty (NULL) value before every other
|
|
153
|
+
* value ascending and after it descending — the order the query tools verify
|
|
154
|
+
* a returned page against. Database defaults disagree with that and with each
|
|
155
|
+
* other (PostgreSQL puts NULLs first when descending), so a page containing a
|
|
156
|
+
* NULL sort value failed the order check as a 502. The placement is explicit.
|
|
157
|
+
*/
|
|
158
|
+
export declare function dataQueryOrderByTerms(sort: readonly DataQuerySort[] | undefined): string[] | undefined;
|
|
128
159
|
/**
|
|
129
160
|
* Memoized query schema for one registered class. The schema is derived from
|
|
130
161
|
* immutable registration metadata, so it is built once per process rather
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smrt-collection-data-surface.d.ts","sourceRoot":"","sources":["../src/smrt-collection-data-surface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;
|
|
1
|
+
{"version":3,"file":"smrt-collection-data-surface.d.ts","sourceRoot":"","sources":["../src/smrt-collection-data-surface.ts"],"names":[],"mappings":"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"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-agents",
|
|
6
|
-
"packageVersion": "0.
|
|
6
|
+
"packageVersion": "0.52.0",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "18c2d2edc3fb712ed42faccbcdcde20e2bc4fb807b4ac95e6397473eda93020f",
|
|
11
|
+
"packageJson": "ff51fe90b98bcffca002647c7afe4d911d464a2fee3483be324b60d0b9fcdaad",
|
|
12
|
+
"agents": "178635896412caad1ff05c2c9d28a4b70dac91f4a531a1d4fb9fb5c084000b2a"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@types/node": "24.13.6",
|
|
48
48
|
"fast-glob": "3.3.3",
|
|
49
49
|
"svelte-check": "^4.7.6",
|
|
50
|
-
"svelte": "^5.57.
|
|
50
|
+
"svelte": "^5.57.0",
|
|
51
51
|
"typescript": "6.0.3",
|
|
52
52
|
"vite": "8.1.4",
|
|
53
53
|
"vitest": "4.1.10"
|
|
@@ -2247,5 +2247,5 @@
|
|
|
2247
2247
|
"polymorphicAssociations": 0,
|
|
2248
2248
|
"uuidColumns": 18
|
|
2249
2249
|
},
|
|
2250
|
-
"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. Persona-backed\n agents use `AgentOptions.personaId` as the durable config owner; legacy agents\n use the saved Agent row id.\n- **Merged**: `getMergedConfig('slotId')` — DB overrides file config\n- **UI slots**: `static uiSlots` declares admin panels (id, label, icon, order,\n `scope`, optional versioned `settingsSchema`). Without a registered custom\n component, `AgentSettingsForm` renders that schema.\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.personaId`** — durable persona/settings identity, including\n the default persona. Independent from execution instance identity.\n- **`AgentOptions.instanceKey`** — the durable execution-instance key (typically\n the persona id, but null for the default persona). Honored **only** when\n `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: permissions resolve live unless the caller passes an explicit `permissions` snapshot, which the run then carries as `run.permissionSnapshot`. `agents.invoke` propagates that snapshot as the `DelegationEnvelope.permissions` ceiling, and `executeDelegatedInvocation` runs the worker with ceiling ∩ live grants, so a narrowed run cannot widen through delegation and a tampered persisted ceiling cannot exceed live RBAC (#2978). 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## Data Surface Read Tools (issue #2447)\n\n`createDataSurfaceTools()` produces the `data.discover`, `data.inspect`, and\n`data.query` `PrincipalTool`s consumed through chat's `extraTools` seam. The\ncaller supplies a server-owned surface catalog and executor; the tools copy\n`userId`, `tenantId`, database, and permissions only from the live\n`PrincipalRun`. Discovery and inspection first assert the collection's read\ncatalog permission and omit denied surfaces/fields. Query requests and results\nare normalized with the core bounded data-query protocol, including projection,\ncursor/page, row/byte, fingerprint, freshness, total, and truncation rules.\nSensitive/read-permission fields are removed from descriptors, and\n`DataSurfaceField` policy metadata is stripped before the core schema validator.\nExecutor-provided paginated rows retain their order and are validated with a\nstable identity tie-breaker (including type-aware numeric/date comparisons),\nwhile internal sort keys are stripped when they were not requested in the\nprojection. Execution has a bounded deadline; public executor/result failures\nuse stable generic errors while optional `onFailure` telemetry receives the\nauthenticated/delegated principal and detailed server-side error. Hidden field\nrequest failures also use a stable public error. Tool arguments never contain\nprincipal or tenant authority.\n\n## Playbook Preflight (issue #2590)\n\n`createPlaybookPreflightTool()` produces the `playbooks.preflight`\n`PrincipalTool`. It resolves a playbook through the caller's own layer chain on\nthe `server` plane and reports a per-step verdict from the two predicates\n`PrincipalRun` already exposes — `isToolAllowed(tool)` and `assertOperation`\n(which evaluates the catalog permission without performing the operation). It\nexecutes nothing.\n\n**Advisory only: preflight predicts, it never grants.** Name gates entry;\ndecomposition gates every step. Preflight computes that intersection ahead of\ntime and replaces neither gate, so a permission revoked between the prediction\nand the execution still denies at execution — proven in\n`playbook-preflight.test.ts`, which lets a cached `allow` stand and shows\n`assertOperation` refusing anyway.\n\n- Step → gating tool slug and permission collection are seams (`toolSlug`,\n `collection`), defaulting to the registered collection slug for the step's\n qualified model (`playbookStepToolSlug` / `playbookStepCollection`).\n- The cache partition key comes from the **live run context**, never the tool\n arguments, so one principal's prediction can never be served to another.\n- An unknown key and an unauthorized key return the same uniform unavailable\n report — the tool is not an enumeration oracle.\n- `filterPlaybooksByPreflight()` hides playbooks a caller could not run from a\n listing. It is a listing convenience and **never** load-bearing for\n authorization; a key it lets through is still authorized step by step.\n\n## Report Data-Surface Tools (issue #2462)\n\n`createReportDataSurfaceTools()` maps server-owned report definitions into the\ngeneric `data.discover`, `data.inspect`, and `data.query` catalog, and adds\nprincipal-bound `reports.query`, `reports.refresh`, `reports.drilldown`, and\n`reports.export` tools. It reuses the reports package's materialized-query,\nlifecycle, drilldown, and snapshot contracts rather than reimplementing them.\nReport ids, collections, query seams, browser delivery, background queues, and\naction hosts come only from the configured server catalog; tool arguments never\ncarry principal, tenant, or report-constructor authority.\n\n- Generic discovery excludes sensitive and permission-gated report columns.\n- Discovered report fields retain safe `kind`, `filterScope`, and capability\n metadata, while the surface reports its available actions and lifecycle\n freshness support. Actions are filtered by the current tool allow-list and\n effective permissions. This lets agents distinguish dimensions/buckets\n (`where`) from measures (`having`) without exposing hidden columns or\n authority.\n- Silent reads do not change browser state; visible reads require a host-bound\n acknowledgement whose post-command revision is not older than the requested\n compare-and-swap revision; background requests contain no principal or tenant\n payload.\n- Read results request the tenant-safe report lifecycle whenever an\n authenticated database is available, so `freshness` and the redacted\n lifecycle state cover stale and lock-skipped materializations.\n- Refresh and export use app-owned action hosts for live authorization and\n auditing. Export captures an opaque immutable snapshot and preserves the\n reports package's preview/confirmation protocol.\n- Drilldown re-reads a single accessible materialized row before it builds the\n inherited source query, so callers cannot inject row values from another\n tenant.\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## Server Entry Points Stay Svelte-Free (issue #2924)\n\n`SmrtDataSurfaceActionTask`, `DataSurfaceActionTokenState`, and\n`DataSurfaceActionIdempotencyState` stamp `@happyvertical/smrt-agents/server`\nas their manifest `importPath`, so core's consumer plugin emits that specifier\nverbatim into a consumer's generated `.smrt/register.js` — which the `smrt` CLI\nloads with a bare `import()` under plain Node for `db:migrate`. **No module\nreachable from `./server` (or from the package root, or `./vite`) may import a\nSvelte component barrel.** A Vite build of the same graph succeeds, so this\nregresses silently and then breaks `db:migrate` for every object in the\nconsuming app, not just this package's.\n\n- Import shared data-surface protocol limits from the Svelte-free\n `@happyvertical/smrt-ui/data-surface`, never `@happyvertical/smrt-ui/data`\n (a component barrel that re-exports `.svelte` files).\n- `src/server/__tests__/node-entry-loadability.integration.test.ts` imports\n every server-side subpath in a fresh plain-Node process; it needs a built\n `dist/` (turbo's `test` task supplies it).\n- `scripts/verify-manifest-exports.mjs` fails a publish when any **non-root**\n manifest `importPath` is unloadable under plain Node. A bundler-only *root*\n barrel is still only a warning.\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/report-data-surface.ts` | Principal-bound report discovery, query, lifecycle, drilldown, and export tools (#2462) |\n| `src/smrt-collection-data-surface.ts` | `createSmrtCollectionDataSurfaceDefinition()` — generic registry-driven `SmrtObject` collection to `DataSurfaceDefinition` adapter, with field-policy redaction (schema overrides included), class-aware tenant scope, DNF filter/scope lowering, deny-all short-circuit, query-bound opaque cursors, and offset/cursor paging; hydrated row values are serialized to JSON scalars at the adapter boundary — a `Date`/date-only/zoneless-UTC `datetime` becomes an ISO-8601 UTC instant, a `bigint` a safe number, and a non-scalar value on a scalar descriptor its JSON string — so a populated datetime column no longer fails the whole result; callers must keep `maxPageLimit` <= the collection's own row-limit cap (#2905, follow-up #2912, #2933) |\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/playbook-preflight.ts` | `playbooks.preflight` PrincipalTool — advisory per-step verdicts, never a grant (#2590) |\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"
|
|
2250
|
+
"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. Persona-backed\n agents use `AgentOptions.personaId` as the durable config owner; legacy agents\n use the saved Agent row id.\n- **Merged**: `getMergedConfig('slotId')` — DB overrides file config\n- **UI slots**: `static uiSlots` declares admin panels (id, label, icon, order,\n `scope`, optional versioned `settingsSchema`). Without a registered custom\n component, `AgentSettingsForm` renders that schema.\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### Config owner id and the #1092 key repair\n\nA binding with no Agent row owns slot configs under\n`tenantAgentConfigOwnerId(tenantId, type)` = `<tenant>:<canonical type>`\n(`serializeResolvedAgent()` uses it). Seeders and schedule sync must compose it\nwith the helper. Rows keyed `<tenant>:<Class>` (pre-#1092) or bindings under a\nbare/renamed type are moved by `repairTenantAgentConfigKeys(db, { aliases,\napply })` (`src/migrations/tenant-agent-config-keys.ts`): dry run by default,\ncounted totals, blocked rows (two spellings of one binding/slot) refuse the\napply, one transaction with a ledger and a post-check; idempotent. Register the\nhost's agent classes (or pass `aliases`) before running it.\n\n## Create entries\n\n`static createEntries: AgentCreateEntry[]` (literal array; the scanner captures\nit into `agent.createEntries`) lets an agent contribute \"create\" options: `id`,\n`type`, `format`, `label`, `description`, `icon`, `route` (relative to the\nhost base path), `order`, `availability` (`permission`, `accessLevels`,\n`requires`). `SerializedAgent.createEntries` carries them;\n`resolveAgentCreateEntries(agents, { basePath, accessLevel, capabilities })`\nfrom `./ui` filters, builds `href`, and sorts. Non-relative routes are dropped.\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.personaId`** — durable persona/settings identity, including\n the default persona. Independent from execution instance identity.\n- **`AgentOptions.instanceKey`** — the durable execution-instance key (typically\n the persona id, but null for the default persona). Honored **only** when\n `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: permissions resolve live unless the caller passes an explicit `permissions` snapshot, which the run then carries as `run.permissionSnapshot`. `agents.invoke` propagates that snapshot as the `DelegationEnvelope.permissions` ceiling, and `executeDelegatedInvocation` runs the worker with ceiling ∩ live grants, so a narrowed run cannot widen through delegation and a tampered persisted ceiling cannot exceed live RBAC (#2978). 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## Data Surface Read Tools (issue #2447)\n\n`createDataSurfaceTools()` produces the `data.discover`, `data.inspect`, and\n`data.query` `PrincipalTool`s consumed through chat's `extraTools` seam. The\ncaller supplies a server-owned surface catalog and executor; the tools copy\n`userId`, `tenantId`, database, and permissions only from the live\n`PrincipalRun`. Discovery and inspection first assert the collection's read\ncatalog permission and omit denied surfaces/fields. Query requests and results\nare normalized with the core bounded data-query protocol, including projection,\ncursor/page, row/byte, fingerprint, freshness, total, and truncation rules.\nSensitive/read-permission fields are removed from descriptors, and\n`DataSurfaceField` policy metadata is stripped before the core schema validator.\nExecutor-provided paginated rows retain their order and are validated with a\nstable identity tie-breaker (including type-aware numeric/date comparisons),\nwhile internal sort keys are stripped when they were not requested in the\nprojection. Execution has a bounded deadline; public executor/result failures\nuse stable generic errors while optional `onFailure` telemetry receives the\nauthenticated/delegated principal and detailed server-side error. Hidden field\nrequest failures also use a stable public error. Tool arguments never contain\nprincipal or tenant authority.\n\n## Playbook Preflight (issue #2590)\n\n`createPlaybookPreflightTool()` produces the `playbooks.preflight`\n`PrincipalTool`. It resolves a playbook through the caller's own layer chain on\nthe `server` plane and reports a per-step verdict from the two predicates\n`PrincipalRun` already exposes — `isToolAllowed(tool)` and `assertOperation`\n(which evaluates the catalog permission without performing the operation). It\nexecutes nothing.\n\n**Advisory only: preflight predicts, it never grants.** Name gates entry;\ndecomposition gates every step. Preflight computes that intersection ahead of\ntime and replaces neither gate, so a permission revoked between the prediction\nand the execution still denies at execution — proven in\n`playbook-preflight.test.ts`, which lets a cached `allow` stand and shows\n`assertOperation` refusing anyway.\n\n- Step → gating tool slug and permission collection are seams (`toolSlug`,\n `collection`), defaulting to the registered collection slug for the step's\n qualified model (`playbookStepToolSlug` / `playbookStepCollection`).\n- The cache partition key comes from the **live run context**, never the tool\n arguments, so one principal's prediction can never be served to another.\n- An unknown key and an unauthorized key return the same uniform unavailable\n report — the tool is not an enumeration oracle.\n- `filterPlaybooksByPreflight()` hides playbooks a caller could not run from a\n listing. It is a listing convenience and **never** load-bearing for\n authorization; a key it lets through is still authorized step by step.\n\n## Report Data-Surface Tools (issue #2462)\n\n`createReportDataSurfaceTools()` maps server-owned report definitions into the\ngeneric `data.discover`, `data.inspect`, and `data.query` catalog, and adds\nprincipal-bound `reports.query`, `reports.refresh`, `reports.drilldown`, and\n`reports.export` tools. It reuses the reports package's materialized-query,\nlifecycle, drilldown, and snapshot contracts rather than reimplementing them.\nReport ids, collections, query seams, browser delivery, background queues, and\naction hosts come only from the configured server catalog; tool arguments never\ncarry principal, tenant, or report-constructor authority.\n\n- Generic discovery excludes sensitive and permission-gated report columns.\n- Discovered report fields retain safe `kind`, `filterScope`, and capability\n metadata, while the surface reports its available actions and lifecycle\n freshness support. Actions are filtered by the current tool allow-list and\n effective permissions. This lets agents distinguish dimensions/buckets\n (`where`) from measures (`having`) without exposing hidden columns or\n authority.\n- Silent reads do not change browser state; visible reads require a host-bound\n acknowledgement whose post-command revision is not older than the requested\n compare-and-swap revision; background requests contain no principal or tenant\n payload.\n- Read results request the tenant-safe report lifecycle whenever an\n authenticated database is available, so `freshness` and the redacted\n lifecycle state cover stale and lock-skipped materializations.\n- Refresh and export use app-owned action hosts for live authorization and\n auditing. Export captures an opaque immutable snapshot and preserves the\n reports package's preview/confirmation protocol.\n- Drilldown re-reads a single accessible materialized row before it builds the\n inherited source query, so callers cannot inject row values from another\n tenant.\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## Server Entry Points Stay Svelte-Free (issue #2924)\n\n`SmrtDataSurfaceActionTask`, `DataSurfaceActionTokenState`, and\n`DataSurfaceActionIdempotencyState` stamp `@happyvertical/smrt-agents/server`\nas their manifest `importPath`, so core's consumer plugin emits that specifier\nverbatim into a consumer's generated `.smrt/register.js` — which the `smrt` CLI\nloads with a bare `import()` under plain Node for `db:migrate`. **No module\nreachable from `./server` (or from the package root, or `./vite`) may import a\nSvelte component barrel.** A Vite build of the same graph succeeds, so this\nregresses silently and then breaks `db:migrate` for every object in the\nconsuming app, not just this package's.\n\n- Import shared data-surface protocol limits from the Svelte-free\n `@happyvertical/smrt-ui/data-surface`, never `@happyvertical/smrt-ui/data`\n (a component barrel that re-exports `.svelte` files).\n- `src/server/__tests__/node-entry-loadability.integration.test.ts` imports\n every server-side subpath in a fresh plain-Node process; it needs a built\n `dist/` (turbo's `test` task supplies it).\n- `scripts/verify-manifest-exports.mjs` fails a publish when any **non-root**\n manifest `importPath` is unloadable under plain Node. A bundler-only *root*\n barrel is still only a warning.\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/report-data-surface.ts` | Principal-bound report discovery, query, lifecycle, drilldown, and export tools (#2462) |\n| `src/smrt-collection-data-surface.ts` | `createSmrtCollectionDataSurfaceDefinition()` — generic registry-driven `SmrtObject` collection to `DataSurfaceDefinition` adapter, with field-policy redaction (schema overrides included), class-aware tenant scope, DNF filter/scope lowering, deny-all short-circuit, query-bound opaque cursors, and offset/cursor paging; hydrated row values are serialized to JSON scalars at the adapter boundary — a `Date`/date-only/zoneless-UTC `datetime` becomes an ISO-8601 UTC instant, a `bigint` a safe number, and a non-scalar value on a scalar descriptor its JSON string — so a populated datetime column no longer fails the whole result; callers must keep `maxPageLimit` <= the collection's own row-limit cap (#2905, follow-up #2912, #2933) |\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/playbook-preflight.ts` | `playbooks.preflight` PrincipalTool — advisory per-step verdicts, never a grant (#2590) |\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"
|
|
2251
2251
|
}
|
package/dist/ui.d.ts
CHANGED
|
@@ -123,6 +123,98 @@ export interface AgentAdminRoute {
|
|
|
123
123
|
/** Optional: export name for server load function */
|
|
124
124
|
load?: string;
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* When an {@link AgentCreateEntry} is offered. Every condition given must
|
|
128
|
+
* hold; the host supplies the facts through {@link AgentCreateEntryContext}.
|
|
129
|
+
*/
|
|
130
|
+
export interface AgentCreateEntryAvailability {
|
|
131
|
+
/**
|
|
132
|
+
* An agent manifest permission id (e.g. `manage:sources`) that must be
|
|
133
|
+
* granted for the tenant (`SerializedAgent.permissions`).
|
|
134
|
+
*/
|
|
135
|
+
permission?: string;
|
|
136
|
+
/** Host access levels allowed to use it (e.g. `['admin', 'editor']`). */
|
|
137
|
+
accessLevels?: string[];
|
|
138
|
+
/** Host capability flags that must all be on (e.g. `['video']`). */
|
|
139
|
+
requires?: string[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A "create" option an agent contributes to the host's create menu, declared
|
|
143
|
+
* with `static createEntries` on the agent class and carried in its manifest.
|
|
144
|
+
* It is plain data: the host decides where the menu lives, groups entries by
|
|
145
|
+
* `type`/`format`, and builds the link from its own base path.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* static override createEntries: AgentCreateEntry[] = [
|
|
150
|
+
* {
|
|
151
|
+
* id: 'meeting-article',
|
|
152
|
+
* type: 'news/politics',
|
|
153
|
+
* format: 'text',
|
|
154
|
+
* label: 'Write about a meeting',
|
|
155
|
+
* description: 'Pick a council meeting; its papers are linked for you.',
|
|
156
|
+
* icon: 'calendar',
|
|
157
|
+
* route: 'articles/new/source?for=meeting',
|
|
158
|
+
* availability: { accessLevels: ['admin', 'editor'] },
|
|
159
|
+
* },
|
|
160
|
+
* ];
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export interface AgentCreateEntry {
|
|
164
|
+
/** Stable id, unique within the agent. */
|
|
165
|
+
id: string;
|
|
166
|
+
/** Host content type id the entry creates (e.g. `news/politics`). */
|
|
167
|
+
type: string;
|
|
168
|
+
/** Host-defined grouping within a type (e.g. `text`, `video`). */
|
|
169
|
+
format?: string;
|
|
170
|
+
/** Plain action name ("Write about a meeting"). */
|
|
171
|
+
label: string;
|
|
172
|
+
/** One line saying what happens. */
|
|
173
|
+
description?: string;
|
|
174
|
+
/** Icon name the host maps. */
|
|
175
|
+
icon?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Where creation starts, relative to the host's base path (may carry a
|
|
178
|
+
* query string). Absolute and protocol-relative URLs are rejected.
|
|
179
|
+
*/
|
|
180
|
+
route: string;
|
|
181
|
+
/** Sort order within its type (lower first). */
|
|
182
|
+
order?: number;
|
|
183
|
+
availability?: AgentCreateEntryAvailability;
|
|
184
|
+
}
|
|
185
|
+
/** Facts the host supplies to {@link resolveAgentCreateEntries}. */
|
|
186
|
+
export interface AgentCreateEntryContext {
|
|
187
|
+
/** Prefix for every entry's `route` (e.g. `/sites/bentley`). */
|
|
188
|
+
basePath: string;
|
|
189
|
+
/** The viewer's access level in the host's terms. */
|
|
190
|
+
accessLevel?: string;
|
|
191
|
+
/** Capability flags that are on (e.g. `video`). */
|
|
192
|
+
capabilities?: Iterable<string>;
|
|
193
|
+
}
|
|
194
|
+
/** The agent facts `resolveAgentCreateEntries` reads (a `SerializedAgent` fits). */
|
|
195
|
+
export interface AgentCreateEntrySource {
|
|
196
|
+
agentType: string;
|
|
197
|
+
agentClass: string;
|
|
198
|
+
createEntries?: AgentCreateEntry[];
|
|
199
|
+
permissions?: Record<string, boolean>;
|
|
200
|
+
}
|
|
201
|
+
/** An available entry with its link built. */
|
|
202
|
+
export interface ResolvedAgentCreateEntry extends Omit<AgentCreateEntry, 'route' | 'availability'> {
|
|
203
|
+
/** `<agentType>:<id>`, unique across agents. */
|
|
204
|
+
key: string;
|
|
205
|
+
agentType: string;
|
|
206
|
+
agentClass: string;
|
|
207
|
+
href: string;
|
|
208
|
+
}
|
|
209
|
+
/** Whether an entry is available for these agent permissions and host facts. */
|
|
210
|
+
export declare function isAgentCreateEntryAvailable(entry: AgentCreateEntry, permissions: Record<string, boolean> | undefined, context: Omit<AgentCreateEntryContext, 'basePath'>): boolean;
|
|
211
|
+
/**
|
|
212
|
+
* The create entries a host should offer: every available entry of the given
|
|
213
|
+
* (already tenant-resolved, enabled) agents, with `href` built from
|
|
214
|
+
* `basePath`, sorted by type, then `order`, then label. Entries whose route is
|
|
215
|
+
* not relative are dropped.
|
|
216
|
+
*/
|
|
217
|
+
export declare function resolveAgentCreateEntries(agents: readonly AgentCreateEntrySource[], context: AgentCreateEntryContext): ResolvedAgentCreateEntry[];
|
|
126
218
|
/**
|
|
127
219
|
* Context passed to agent route load functions
|
|
128
220
|
*
|
|
@@ -153,6 +245,8 @@ export interface AgentManifestInfo {
|
|
|
153
245
|
description?: string;
|
|
154
246
|
uiSlots: Record<string, AgentUISlot>;
|
|
155
247
|
adminRoutes?: AgentAdminRoute[];
|
|
248
|
+
/** Create options this agent contributes (`static createEntries`) */
|
|
249
|
+
createEntries?: AgentCreateEntry[];
|
|
156
250
|
/** Default signal subscriptions declared by this agent */
|
|
157
251
|
signalSubscriptions?: string[];
|
|
158
252
|
permissions: Array<{
|
package/dist/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACf,MAAM,2BAA2B,CAAC;AAEnC;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,OAAO,IAAI,mBAAmB,CAAC,KAAK,CAAC,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,OAAO,GAAG,OAAO;IACpD,8DAA8D;IAC9D,MAAM,EAAE,OAAO,CAAC;IAChB,6CAA6C;IAC7C,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,MAAM,CAAC;AAEX,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,qBAAqB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,GAAG,EAAE,GAAG,CAAC;CACV;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,qBAAqB,KAC3B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,0DAA0D;IAC1D,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,WAAW,EAAE,KAAK,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC,CAAC;IACH,QAAQ,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,SAAS,EAAE,KAAK,CAAC;QACf,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC,CAAC;IACH,UAAU,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,SAAS,mBAAmB,EACzC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,GAC/B,IAAI,CAAC;IAER,0CAA0C;IAC1C,GAAG,CAAC,MAAM,SAAS,mBAAmB,EACpC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAErC,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAEvC,yCAAyC;IACzC,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAEjD,2CAA2C;IAC3C,SAAS,IAAI,MAAM,EAAE,CAAC;IAEtB,kDAAkD;IAClD,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAExD,mDAAmD;IACnD,KAAK,IAAI,IAAI,CAAC;IAEd,qEAAqE;IACrE,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAE3D,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAEjD,oDAAoD;IACpD,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAExE,sCAAsC;IACtC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAE/D,mCAAmC;IACnC,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAElD,8CAA8C;IAC9C,sBAAsB,CACpB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,aAAa,GACvB,IAAI,CAAC;IAER,yCAAyC;IACzC,iBAAiB,CACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,aAAa,GAAG,SAAS,CAAC;IAE7B,kDAAkD;IAClD,iBAAiB,CACf,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,GACvB,IAAI,CAAC;IAER,6CAA6C;IAC7C,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAC;CAC9E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,IAAI,wBAAwB,CAwF3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,qBAAqB,EAAE,wBAAwB,GAAG,SAAS,CAAC;CACjE;AAKD,eAAO,MAAM,eAAe,EAAE,wBACI,CAAC;AAEnC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CA8CxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAWhC,CAAC"}
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
|
package/dist/ui.js
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
//#region src/ui.ts
|
|
2
|
+
function isRelativeRoute(route) {
|
|
3
|
+
const trimmed = route.trim();
|
|
4
|
+
return trimmed.length > 0 && !trimmed.startsWith("//") && !/^[a-z][a-z0-9+.-]*:/i.test(trimmed);
|
|
5
|
+
}
|
|
6
|
+
function isAgentCreateEntryAvailable(entry, permissions, context) {
|
|
7
|
+
const availability = entry.availability;
|
|
8
|
+
if (!availability) return true;
|
|
9
|
+
if (availability.permission && permissions?.[availability.permission] !== true) return false;
|
|
10
|
+
if (availability.accessLevels && (!context.accessLevel || !availability.accessLevels.includes(context.accessLevel))) return false;
|
|
11
|
+
if (availability.requires?.length) {
|
|
12
|
+
const on = new Set(context.capabilities ?? []);
|
|
13
|
+
if (!availability.requires.every((flag) => on.has(flag))) return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
function resolveAgentCreateEntries(agents, context) {
|
|
18
|
+
const base = context.basePath.replace(/\/+$/, "");
|
|
19
|
+
const resolved = [];
|
|
20
|
+
for (const agent of agents) for (const entry of agent.createEntries ?? []) {
|
|
21
|
+
if (!isRelativeRoute(entry.route)) continue;
|
|
22
|
+
if (!isAgentCreateEntryAvailable(entry, agent.permissions, context)) continue;
|
|
23
|
+
const { route, availability: _availability, ...rest } = entry;
|
|
24
|
+
resolved.push({
|
|
25
|
+
...rest,
|
|
26
|
+
key: `${agent.agentType}:${entry.id}`,
|
|
27
|
+
agentType: agent.agentType,
|
|
28
|
+
agentClass: agent.agentClass,
|
|
29
|
+
href: `${base}/${route.trim().replace(/^\/+/, "")}`
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return resolved.sort((a, b) => a.type.localeCompare(b.type) || (a.order ?? 100) - (b.order ?? 100) || a.label.localeCompare(b.label));
|
|
33
|
+
}
|
|
2
34
|
function createUIRegistry() {
|
|
3
35
|
const components = /* @__PURE__ */ new Map();
|
|
4
36
|
const manifests = /* @__PURE__ */ new Map();
|
|
@@ -133,6 +165,6 @@ var AGENTS_MODULE_META = {
|
|
|
133
165
|
]
|
|
134
166
|
};
|
|
135
167
|
//#endregion
|
|
136
|
-
export { AGENTS_MODULE_META, AGENTS_UI_SLOTS, AgentUIRegistry, createUIRegistry };
|
|
168
|
+
export { AGENTS_MODULE_META, AGENTS_UI_SLOTS, AgentUIRegistry, createUIRegistry, isAgentCreateEntryAvailable, resolveAgentCreateEntries };
|
|
137
169
|
|
|
138
170
|
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","names":[],"sources":["../src/ui.ts"],"sourcesContent":["import type {\n ModuleComponentType,\n ModuleUISlot,\n SmrtModuleMeta,\n} from '@happyvertical/smrt-types';\n\n/**\n * UI type definitions for SMRT Agents\n *\n * These types allow agents to declare admin panel UI slots\n * that can be implemented as Svelte components in agent packages.\n *\n * @example\n * ```typescript\n * import { AgentUIRegistry, type AdminPanelBaseProps } from '@happyvertical/smrt-agents/ui';\n *\n * // In agent package: register components at import time\n * AgentUIRegistry.register('MyAgent', 'settings', SettingsPanel);\n *\n * // In host app: use registered components\n * const Component = AgentUIRegistry.get('MyAgent', 'settings');\n * ```\n */\n\n/**\n * Svelte component type for agent admin-panel slots.\n *\n * Re-exported from the canonical {@link ModuleComponentType} in\n * `@happyvertical/smrt-types` — the single shared definition that owns the\n * irreducible `any` (see its inline note: the placeholder must be assignable\n * both FROM arbitrary concrete components and TO Svelte's render union, which\n * no non-`any` type satisfies under `strictFunctionTypes`). Aliased here so\n * this package's public UI surface keeps the local `ComponentType` name.\n */\nexport type ComponentType<Props = unknown> = ModuleComponentType<Props>;\n\n/**\n * Base props that all admin panel components receive\n */\nexport interface AdminPanelBaseProps<TConfig = unknown> {\n /** Current configuration from the agent (merged file + db) */\n config: TConfig;\n /** Callback to save configuration changes */\n onSave: (config: TConfig) => Promise<void>;\n /** Whether the panel is in read-only mode */\n readonly?: boolean;\n /** CSS class for styling integration */\n class?: string;\n /**\n * Read-only file-based configuration defaults (from smrt.config.js)\n * Use this to display which values come from the config file\n */\n fileConfig?: TConfig;\n /**\n * Editable database-persisted configuration overrides\n * Use this to display which values have been customized in the DB\n */\n dbConfig?: TConfig;\n}\n\nexport type AgentSettingFieldType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'textarea'\n | 'json';\n\nexport interface AgentSettingOption {\n value: string;\n label: string;\n}\n\n/** A non-secret setting that a host app can render without custom UI code. */\nexport interface AgentSettingField {\n id: string;\n label: string;\n type: AgentSettingFieldType;\n description?: string;\n required?: boolean;\n default?: unknown;\n placeholder?: string;\n options?: AgentSettingOption[];\n min?: number;\n max?: number;\n}\n\n/** Versioned contract for a schema-rendered agent settings panel. */\nexport interface AgentSettingsSchema {\n version: number;\n fields: AgentSettingField[];\n}\n\n/**\n * Definition of a UI slot that an agent declares\n *\n * Agents define slots they support; UI packages implement them.\n */\nexport interface AgentUISlot {\n /** Unique identifier for this slot (e.g., 'sources', 'reports', 'settings') */\n id: string;\n /** Human-readable label for the slot */\n label: string;\n /** Description of what this panel configures */\n description?: string;\n /** Icon identifier (e.g., 'settings', 'database', 'users') */\n icon?: string;\n /** Display order (lower numbers first) */\n order?: number;\n /** Whether the slot is currently unavailable in the admin UI */\n disabled?: boolean;\n /** Durable owner for settings written through this slot. */\n scope?: 'agent' | 'persona';\n /** Optional fallback form when the agent does not register a custom panel. */\n settingsSchema?: AgentSettingsSchema;\n}\n\n/**\n * Map of slot IDs to their definitions\n * Used as static property on Agent subclasses\n */\nexport type AgentUISlots = Record<string, AgentUISlot>;\n\n/**\n * A route an agent provides for its admin UI\n *\n * Agents declare these so that host applications or tooling\n * (for example, a Vite plugin) can wire them into a SvelteKit app.\n *\n * @example\n * ```typescript\n * static adminRoutes: AgentAdminRoute[] = [\n * { path: 'sources', component: 'SourcesPanel', load: 'loadSources' },\n * { path: 'sources/[sourceId]', component: 'SourceDetail', load: 'loadSourceDetail' },\n * ];\n * ```\n */\nexport interface AgentAdminRoute {\n /** Route path relative to agent root (e.g., 'sources/[sourceId]') */\n path: string;\n /** Component export name from the agent's admin entry point */\n component: string;\n /** Optional: export name for server load function */\n load?: string;\n}\n\n/**\n * Context passed to agent route load functions\n *\n * A normalized subset of SvelteKit's ServerLoadEvent,\n * so agent load functions don't need a direct SvelteKit dependency.\n */\nexport interface AgentRouteLoadContext {\n params: Record<string, string>;\n parent: () => Promise<Record<string, unknown>>;\n fetch: typeof fetch;\n url: URL;\n}\n\n/**\n * Agent route load function signature\n *\n * Returned data is spread into the page's `data` prop.\n */\nexport type AgentRouteLoadFn = (\n context: AgentRouteLoadContext,\n) => Promise<Record<string, unknown>> | Record<string, unknown>;\n\n/**\n * Agent manifest type (re-exported from smrt-core scanner types)\n * Duplicated here to avoid hard dependency on scanner internals\n */\nexport interface AgentManifestInfo {\n name: string;\n slug: string;\n icon?: string;\n tier: 'free' | 'standard' | 'premium';\n description?: string;\n uiSlots: Record<string, AgentUISlot>;\n adminRoutes?: AgentAdminRoute[];\n /** Default signal subscriptions declared by this agent */\n signalSubscriptions?: string[];\n permissions: Array<{\n id: string;\n label: string;\n category: string;\n defaultGranted?: boolean;\n }>;\n features: Array<{\n id: string;\n label: string;\n description?: string;\n type: string;\n }>;\n menuItems: Array<{\n id: string;\n label: string;\n icon?: string;\n order: number;\n path: string;\n requiredPermission?: string;\n }>;\n components: Array<{ exportPath: string; type: string }>;\n}\n\n/**\n * Registry of UI component implementations\n * Maps agent class name + slot ID to Svelte component\n */\nexport interface AgentUIComponentRegistry {\n /** Register a component for an agent's slot */\n register<TProps extends AdminPanelBaseProps>(\n agentClass: string,\n slotId: string,\n component: ComponentType<TProps>,\n ): void;\n\n /** Get a component for an agent's slot */\n get<TProps extends AdminPanelBaseProps>(\n agentClass: string,\n slotId: string,\n ): ComponentType<TProps> | undefined;\n\n /** Get all registered slot IDs for an agent */\n getSlots(agentClass: string): string[];\n\n /** Check if a component is registered */\n has(agentClass: string, slotId: string): boolean;\n\n /** Get all registered agent class names */\n getAgents(): string[];\n\n /** Unregister a component (useful for testing) */\n unregister(agentClass: string, slotId: string): boolean;\n\n /** Clear all registrations (useful for testing) */\n clear(): void;\n\n /** Register a component by composite key (e.g., 'praeco:sources') */\n registerByKey(key: string, component: ComponentType): void;\n\n /** Get a component by composite key */\n getByKey(key: string): ComponentType | undefined;\n\n /** Register an agent manifest for runtime access */\n registerManifest(agentClass: string, manifest: AgentManifestInfo): void;\n\n /** Get a registered agent manifest */\n getManifest(agentClass: string): AgentManifestInfo | undefined;\n\n /** Get all registered manifests */\n getAllManifests(): Map<string, AgentManifestInfo>;\n\n /** Register a route component for an agent */\n registerRouteComponent(\n agentClass: string,\n path: string,\n component: ComponentType,\n ): void;\n\n /** Get a route component for an agent */\n getRouteComponent(\n agentClass: string,\n path: string,\n ): ComponentType | undefined;\n\n /** Register a route load function for an agent */\n registerRouteLoad(\n agentClass: string,\n path: string,\n loadFn: AgentRouteLoadFn,\n ): void;\n\n /** Get a route load function for an agent */\n getRouteLoad(agentClass: string, path: string): AgentRouteLoadFn | undefined;\n}\n\n/**\n * Create a new UI component registry\n *\n * @example\n * ```typescript\n * const registry = createUIRegistry();\n * registry.register('MyAgent', 'settings', SettingsPanel);\n *\n * const Component = registry.get('MyAgent', 'settings');\n * if (Component) {\n * // Render component\n * }\n * ```\n */\nexport function createUIRegistry(): AgentUIComponentRegistry {\n const components = new Map<string, ComponentType>();\n const manifests = new Map<string, AgentManifestInfo>();\n const routeComponents = new Map<string, ComponentType>();\n const routeLoads = new Map<string, AgentRouteLoadFn>();\n\n const makeKey = (agentClass: string, slotId: string) =>\n `${agentClass}:${slotId}`;\n\n const makeRouteKey = (agentClass: string, path: string) =>\n `${agentClass}:route:${path}`;\n\n return {\n register(agentClass, slotId, component) {\n components.set(makeKey(agentClass, slotId), component);\n },\n\n get(agentClass, slotId) {\n return components.get(makeKey(agentClass, slotId));\n },\n\n getSlots(agentClass) {\n const prefix = `${agentClass}:`;\n return Array.from(components.keys())\n .filter((k) => k.startsWith(prefix))\n .map((k) => k.slice(prefix.length));\n },\n\n has(agentClass, slotId) {\n return components.has(makeKey(agentClass, slotId));\n },\n\n getAgents() {\n const agents = new Set<string>();\n for (const key of components.keys()) {\n const agentClass = key.split(':')[0];\n agents.add(agentClass);\n }\n return Array.from(agents);\n },\n\n unregister(agentClass, slotId) {\n return components.delete(makeKey(agentClass, slotId));\n },\n\n clear() {\n components.clear();\n manifests.clear();\n routeComponents.clear();\n routeLoads.clear();\n },\n\n registerByKey(key, component) {\n components.set(key, component);\n },\n\n getByKey(key) {\n return components.get(key);\n },\n\n registerManifest(agentClass, manifest) {\n manifests.set(agentClass, manifest);\n },\n\n getManifest(agentClass) {\n return manifests.get(agentClass);\n },\n\n getAllManifests() {\n return new Map(manifests);\n },\n\n registerRouteComponent(agentClass, path, component) {\n routeComponents.set(makeRouteKey(agentClass, path), component);\n },\n\n getRouteComponent(agentClass, path) {\n return routeComponents.get(makeRouteKey(agentClass, path));\n },\n\n registerRouteLoad(agentClass, path, loadFn) {\n routeLoads.set(makeRouteKey(agentClass, path), loadFn);\n },\n\n getRouteLoad(agentClass, path) {\n return routeLoads.get(makeRouteKey(agentClass, path));\n },\n };\n}\n\n/**\n * Global UI registry singleton\n *\n * Agent UI packages register their components here at import time,\n * enabling discovery by host applications.\n *\n * Uses a `globalThis.__smrtAgentUIRegistry` property to guarantee a\n * single registry instance per JavaScript runtime, even when bundlers\n * (Vite, webpack) duplicate this module across optimized dependency\n * chunks or package versions.\n *\n * @example\n * ```typescript\n * // In agent package (e.g., @happyvertical/praeco/admin)\n * import { AgentUIRegistry } from '@happyvertical/smrt-agents/ui';\n * import SourcesPanel from './SourcesPanel.svelte';\n *\n * AgentUIRegistry.register('Praeco', 'sources', SourcesPanel);\n *\n * // In host SvelteKit app\n * import { AgentUIRegistry } from '@happyvertical/smrt-agents/ui';\n * import '@happyvertical/praeco/admin'; // Registers components\n *\n * const Component = AgentUIRegistry.get('Praeco', 'sources');\n * ```\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtAgentUIRegistry: AgentUIComponentRegistry | undefined;\n}\n\nif (!globalThis.__smrtAgentUIRegistry) {\n globalThis.__smrtAgentUIRegistry = createUIRegistry();\n}\nexport const AgentUIRegistry: AgentUIComponentRegistry =\n globalThis.__smrtAgentUIRegistry;\n\n/**\n * What an agent's `./admin` entry point must export.\n *\n * This is the contract between agent packages and host apps.\n * Instead of registering individual slot components, agents export\n * a single root component that handles its own sub-navigation.\n *\n * @example\n * ```typescript\n * // In agent package: histrio/src/ui/admin/index.ts\n * export { default } from './AdminRoot.svelte';\n * export { createAPIClient } from '../types.js';\n * export const navItems: AgentAdminNavItem[] = [\n * { id: 'characters', label: 'Characters', icon: 'users', order: 1 },\n * { id: 'performers', label: 'Performers', icon: 'mic', order: 2 },\n * ];\n * ```\n */\nexport interface AgentAdminExport {\n /** Root admin component — renders all panels, handles its own sub-navigation */\n default?: ComponentType;\n /** Create a typed API client for this agent */\n createAPIClient?: (baseUrl: string) => unknown;\n /** Navigation items for tabs/sidebar within the agent admin */\n navItems?: AgentAdminNavItem[];\n}\n\n/**\n * Props passed to the root admin component\n */\nexport interface AgentAdminRootProps {\n /** Typed API client created by the agent's own factory */\n apiClient: unknown;\n /** Which panel to show (from URL hash, e.g., 'sources') */\n activePanel?: string;\n /** Called when user navigates within the agent */\n onNavigate?: (panelId: string) => void;\n /** Whether admin is in read-only mode */\n readonly?: boolean;\n}\n\n/**\n * Navigation item within an agent's admin UI\n */\nexport interface AgentAdminNavItem {\n /** Matches hash fragment and panel ID */\n id: string;\n /** Display label */\n label: string;\n /** Icon identifier */\n icon?: string;\n /** Display order (lower numbers first) */\n order?: number;\n}\n\n/**\n * Agents module UI slots (for ModuleUIRegistry)\n */\nexport const AGENTS_UI_SLOTS: Record<string, ModuleUISlot> = {\n 'agent-dashboard': {\n id: 'agent-dashboard',\n label: 'Agent Dashboard',\n description: 'Combined overview panel for agent schedules',\n icon: 'activity',\n category: 'admin',\n order: 1,\n propsInterface: 'AgentDashboardProps',\n },\n 'agent-schedule-list': {\n id: 'agent-schedule-list',\n label: 'Agent Schedule List',\n description: 'List of scheduled agents',\n icon: 'calendar',\n category: 'list',\n order: 2,\n propsInterface: 'AgentScheduleListProps',\n },\n 'agent-schedule-form': {\n id: 'agent-schedule-form',\n label: 'Agent Schedule Form',\n description: 'Form for creating or editing agent schedules',\n icon: 'edit',\n category: 'form',\n order: 3,\n propsInterface: 'AgentScheduleFormProps',\n },\n 'agent-run-history': {\n id: 'agent-run-history',\n label: 'Agent Run History',\n description: 'History of agent runs',\n icon: 'clock',\n category: 'list',\n order: 4,\n propsInterface: 'AgentRunHistoryProps',\n },\n 'schedule-status-badge': {\n id: 'schedule-status-badge',\n label: 'Schedule Status Badge',\n description: 'Status indicator for schedule states',\n icon: 'tag',\n category: 'display',\n order: 5,\n propsInterface: 'ScheduleStatusBadgeProps',\n },\n};\n\n/**\n * Agents module metadata\n */\nexport const AGENTS_MODULE_META: SmrtModuleMeta = {\n name: '@happyvertical/smrt-agents',\n displayName: 'Agents',\n description: 'Agent framework for building autonomous actors',\n uiSlots: AGENTS_UI_SLOTS,\n models: ['Agent', 'AgentConfig', 'AgentSchedule', 'TenantAgent'],\n collections: [\n 'AgentConfigCollection',\n 'AgentScheduleCollection',\n 'TenantAgentCollection',\n ],\n};\n"],"mappings":";AAmSO,SAAS,mBAA6C;CAC3D,MAAM,6BAAa,IAAI,IAA2B;CAClD,MAAM,4BAAY,IAAI,IAA+B;CACrD,MAAM,kCAAkB,IAAI,IAA2B;CACvD,MAAM,6BAAa,IAAI,IAA8B;CAErD,MAAM,WAAW,YAAoB,WACnC,GAAG,WAAU,GAAI;CAEnB,MAAM,gBAAgB,YAAoB,SACxC,GAAG,WAAU,SAAU;CAEzB,OAAO;EACL,SAAS,YAAY,QAAQ,WAAW;GACtC,WAAW,IAAI,QAAQ,YAAY,MAAM,GAAG,SAAS;EACvD;EAEA,IAAI,YAAY,QAAQ;GACtB,OAAO,WAAW,IAAI,QAAQ,YAAY,MAAM,CAAC;EACnD;EAEA,SAAS,YAAY;GACnB,MAAM,SAAS,GAAG,WAAU;GAC5B,OAAO,MAAM,KAAK,WAAW,KAAK,CAAC,CAAA,CAChC,QAAQ,MAAM,EAAE,WAAW,MAAM,CAAC,CAAA,CAClC,KAAK,MAAM,EAAE,MAAM,OAAO,MAAM,CAAC;EACtC;EAEA,IAAI,YAAY,QAAQ;GACtB,OAAO,WAAW,IAAI,QAAQ,YAAY,MAAM,CAAC;EACnD;EAEA,YAAY;GACV,MAAM,yBAAS,IAAI,IAAY;GAC/B,KAAA,MAAW,OAAO,WAAW,KAAK,GAAG;IACnC,MAAM,aAAa,IAAI,MAAM,GAAG,CAAA,CAAE;IAClC,OAAO,IAAI,UAAU;GACvB;GACA,OAAO,MAAM,KAAK,MAAM;EAC1B;EAEA,WAAW,YAAY,QAAQ;GAC7B,OAAO,WAAW,OAAO,QAAQ,YAAY,MAAM,CAAC;EACtD;EAEA,QAAQ;GACN,WAAW,MAAM;GACjB,UAAU,MAAM;GAChB,gBAAgB,MAAM;GACtB,WAAW,MAAM;EACnB;EAEA,cAAc,KAAK,WAAW;GAC5B,WAAW,IAAI,KAAK,SAAS;EAC/B;EAEA,SAAS,KAAK;GACZ,OAAO,WAAW,IAAI,GAAG;EAC3B;EAEA,iBAAiB,YAAY,UAAU;GACrC,UAAU,IAAI,YAAY,QAAQ;EACpC;EAEA,YAAY,YAAY;GACtB,OAAO,UAAU,IAAI,UAAU;EACjC;EAEA,kBAAkB;GAChB,OAAO,IAAI,IAAI,SAAS;EAC1B;EAEA,uBAAuB,YAAY,MAAM,WAAW;GAClD,gBAAgB,IAAI,aAAa,YAAY,IAAI,GAAG,SAAS;EAC/D;EAEA,kBAAkB,YAAY,MAAM;GAClC,OAAO,gBAAgB,IAAI,aAAa,YAAY,IAAI,CAAC;EAC3D;EAEA,kBAAkB,YAAY,MAAM,QAAQ;GAC1C,WAAW,IAAI,aAAa,YAAY,IAAI,GAAG,MAAM;EACvD;EAEA,aAAa,YAAY,MAAM;GAC7B,OAAO,WAAW,IAAI,aAAa,YAAY,IAAI,CAAC;EACtD;CACF;AACF;AAiCA,IAAI,CAAC,WAAW,uBACd,WAAW,wBAAwB,iBAAiB;AAE/C,IAAM,kBACX,WAAW;AA4DN,IAAM,kBAAgD;CAC3D,mBAAmB;EACjB,IAAI;EACJ,OAAO;EACP,aAAa;EACb,MAAM;EACN,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CACA,uBAAuB;EACrB,IAAI;EACJ,OAAO;EACP,aAAa;EACb,MAAM;EACN,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CACA,uBAAuB;EACrB,IAAI;EACJ,OAAO;EACP,aAAa;EACb,MAAM;EACN,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CACA,qBAAqB;EACnB,IAAI;EACJ,OAAO;EACP,aAAa;EACb,MAAM;EACN,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;CACA,yBAAyB;EACvB,IAAI;EACJ,OAAO;EACP,aAAa;EACb,MAAM;EACN,UAAU;EACV,OAAO;EACP,gBAAgB;CAClB;AACF;AAKO,IAAM,qBAAqC;CAChD,MAAM;CACN,aAAa;CACb,aAAa;CACb,SAAS;CACT,QAAQ;EAAC;EAAS;EAAe;EAAiB;CAAa;CAC/D,aAAa;EACX;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"ui.js","names":[],"sources":["../src/ui.ts"],"sourcesContent":["import type {\n ModuleComponentType,\n ModuleUISlot,\n SmrtModuleMeta,\n} from '@happyvertical/smrt-types';\n\n/**\n * UI type definitions for SMRT Agents\n *\n * These types allow agents to declare admin panel UI slots\n * that can be implemented as Svelte components in agent packages.\n *\n * @example\n * ```typescript\n * import { AgentUIRegistry, type AdminPanelBaseProps } from '@happyvertical/smrt-agents/ui';\n *\n * // In agent package: register components at import time\n * AgentUIRegistry.register('MyAgent', 'settings', SettingsPanel);\n *\n * // In host app: use registered components\n * const Component = AgentUIRegistry.get('MyAgent', 'settings');\n * ```\n */\n\n/**\n * Svelte component type for agent admin-panel slots.\n *\n * Re-exported from the canonical {@link ModuleComponentType} in\n * `@happyvertical/smrt-types` — the single shared definition that owns the\n * irreducible `any` (see its inline note: the placeholder must be assignable\n * both FROM arbitrary concrete components and TO Svelte's render union, which\n * no non-`any` type satisfies under `strictFunctionTypes`). Aliased here so\n * this package's public UI surface keeps the local `ComponentType` name.\n */\nexport type ComponentType<Props = unknown> = ModuleComponentType<Props>;\n\n/**\n * Base props that all admin panel components receive\n */\nexport interface AdminPanelBaseProps<TConfig = unknown> {\n /** Current configuration from the agent (merged file + db) */\n config: TConfig;\n /** Callback to save configuration changes */\n onSave: (config: TConfig) => Promise<void>;\n /** Whether the panel is in read-only mode */\n readonly?: boolean;\n /** CSS class for styling integration */\n class?: string;\n /**\n * Read-only file-based configuration defaults (from smrt.config.js)\n * Use this to display which values come from the config file\n */\n fileConfig?: TConfig;\n /**\n * Editable database-persisted configuration overrides\n * Use this to display which values have been customized in the DB\n */\n dbConfig?: TConfig;\n}\n\nexport type AgentSettingFieldType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'textarea'\n | 'json';\n\nexport interface AgentSettingOption {\n value: string;\n label: string;\n}\n\n/** A non-secret setting that a host app can render without custom UI code. */\nexport interface AgentSettingField {\n id: string;\n label: string;\n type: AgentSettingFieldType;\n description?: string;\n required?: boolean;\n default?: unknown;\n placeholder?: string;\n options?: AgentSettingOption[];\n min?: number;\n max?: number;\n}\n\n/** Versioned contract for a schema-rendered agent settings panel. */\nexport interface AgentSettingsSchema {\n version: number;\n fields: AgentSettingField[];\n}\n\n/**\n * Definition of a UI slot that an agent declares\n *\n * Agents define slots they support; UI packages implement them.\n */\nexport interface AgentUISlot {\n /** Unique identifier for this slot (e.g., 'sources', 'reports', 'settings') */\n id: string;\n /** Human-readable label for the slot */\n label: string;\n /** Description of what this panel configures */\n description?: string;\n /** Icon identifier (e.g., 'settings', 'database', 'users') */\n icon?: string;\n /** Display order (lower numbers first) */\n order?: number;\n /** Whether the slot is currently unavailable in the admin UI */\n disabled?: boolean;\n /** Durable owner for settings written through this slot. */\n scope?: 'agent' | 'persona';\n /** Optional fallback form when the agent does not register a custom panel. */\n settingsSchema?: AgentSettingsSchema;\n}\n\n/**\n * Map of slot IDs to their definitions\n * Used as static property on Agent subclasses\n */\nexport type AgentUISlots = Record<string, AgentUISlot>;\n\n/**\n * A route an agent provides for its admin UI\n *\n * Agents declare these so that host applications or tooling\n * (for example, a Vite plugin) can wire them into a SvelteKit app.\n *\n * @example\n * ```typescript\n * static adminRoutes: AgentAdminRoute[] = [\n * { path: 'sources', component: 'SourcesPanel', load: 'loadSources' },\n * { path: 'sources/[sourceId]', component: 'SourceDetail', load: 'loadSourceDetail' },\n * ];\n * ```\n */\nexport interface AgentAdminRoute {\n /** Route path relative to agent root (e.g., 'sources/[sourceId]') */\n path: string;\n /** Component export name from the agent's admin entry point */\n component: string;\n /** Optional: export name for server load function */\n load?: string;\n}\n\n/**\n * When an {@link AgentCreateEntry} is offered. Every condition given must\n * hold; the host supplies the facts through {@link AgentCreateEntryContext}.\n */\nexport interface AgentCreateEntryAvailability {\n /**\n * An agent manifest permission id (e.g. `manage:sources`) that must be\n * granted for the tenant (`SerializedAgent.permissions`).\n */\n permission?: string;\n /** Host access levels allowed to use it (e.g. `['admin', 'editor']`). */\n accessLevels?: string[];\n /** Host capability flags that must all be on (e.g. `['video']`). */\n requires?: string[];\n}\n\n/**\n * A \"create\" option an agent contributes to the host's create menu, declared\n * with `static createEntries` on the agent class and carried in its manifest.\n * It is plain data: the host decides where the menu lives, groups entries by\n * `type`/`format`, and builds the link from its own base path.\n *\n * @example\n * ```typescript\n * static override createEntries: AgentCreateEntry[] = [\n * {\n * id: 'meeting-article',\n * type: 'news/politics',\n * format: 'text',\n * label: 'Write about a meeting',\n * description: 'Pick a council meeting; its papers are linked for you.',\n * icon: 'calendar',\n * route: 'articles/new/source?for=meeting',\n * availability: { accessLevels: ['admin', 'editor'] },\n * },\n * ];\n * ```\n */\nexport interface AgentCreateEntry {\n /** Stable id, unique within the agent. */\n id: string;\n /** Host content type id the entry creates (e.g. `news/politics`). */\n type: string;\n /** Host-defined grouping within a type (e.g. `text`, `video`). */\n format?: string;\n /** Plain action name (\"Write about a meeting\"). */\n label: string;\n /** One line saying what happens. */\n description?: string;\n /** Icon name the host maps. */\n icon?: string;\n /**\n * Where creation starts, relative to the host's base path (may carry a\n * query string). Absolute and protocol-relative URLs are rejected.\n */\n route: string;\n /** Sort order within its type (lower first). */\n order?: number;\n availability?: AgentCreateEntryAvailability;\n}\n\n/** Facts the host supplies to {@link resolveAgentCreateEntries}. */\nexport interface AgentCreateEntryContext {\n /** Prefix for every entry's `route` (e.g. `/sites/bentley`). */\n basePath: string;\n /** The viewer's access level in the host's terms. */\n accessLevel?: string;\n /** Capability flags that are on (e.g. `video`). */\n capabilities?: Iterable<string>;\n}\n\n/** The agent facts `resolveAgentCreateEntries` reads (a `SerializedAgent` fits). */\nexport interface AgentCreateEntrySource {\n agentType: string;\n agentClass: string;\n createEntries?: AgentCreateEntry[];\n permissions?: Record<string, boolean>;\n}\n\n/** An available entry with its link built. */\nexport interface ResolvedAgentCreateEntry\n extends Omit<AgentCreateEntry, 'route' | 'availability'> {\n /** `<agentType>:<id>`, unique across agents. */\n key: string;\n agentType: string;\n agentClass: string;\n href: string;\n}\n\nfunction isRelativeRoute(route: string): boolean {\n const trimmed = route.trim();\n return (\n trimmed.length > 0 &&\n !trimmed.startsWith('//') &&\n !/^[a-z][a-z0-9+.-]*:/i.test(trimmed)\n );\n}\n\n/** Whether an entry is available for these agent permissions and host facts. */\nexport function isAgentCreateEntryAvailable(\n entry: AgentCreateEntry,\n permissions: Record<string, boolean> | undefined,\n context: Omit<AgentCreateEntryContext, 'basePath'>,\n): boolean {\n const availability = entry.availability;\n if (!availability) return true;\n if (\n availability.permission &&\n permissions?.[availability.permission] !== true\n ) {\n return false;\n }\n if (\n availability.accessLevels &&\n (!context.accessLevel ||\n !availability.accessLevels.includes(context.accessLevel))\n ) {\n return false;\n }\n if (availability.requires?.length) {\n const on = new Set(context.capabilities ?? []);\n if (!availability.requires.every((flag) => on.has(flag))) return false;\n }\n return true;\n}\n\n/**\n * The create entries a host should offer: every available entry of the given\n * (already tenant-resolved, enabled) agents, with `href` built from\n * `basePath`, sorted by type, then `order`, then label. Entries whose route is\n * not relative are dropped.\n */\nexport function resolveAgentCreateEntries(\n agents: readonly AgentCreateEntrySource[],\n context: AgentCreateEntryContext,\n): ResolvedAgentCreateEntry[] {\n const base = context.basePath.replace(/\\/+$/, '');\n const resolved: ResolvedAgentCreateEntry[] = [];\n for (const agent of agents) {\n for (const entry of agent.createEntries ?? []) {\n if (!isRelativeRoute(entry.route)) continue;\n if (!isAgentCreateEntryAvailable(entry, agent.permissions, context)) {\n continue;\n }\n const { route, availability: _availability, ...rest } = entry;\n resolved.push({\n ...rest,\n key: `${agent.agentType}:${entry.id}`,\n agentType: agent.agentType,\n agentClass: agent.agentClass,\n href: `${base}/${route.trim().replace(/^\\/+/, '')}`,\n });\n }\n }\n return resolved.sort(\n (a, b) =>\n a.type.localeCompare(b.type) ||\n (a.order ?? 100) - (b.order ?? 100) ||\n a.label.localeCompare(b.label),\n );\n}\n\n/**\n * Context passed to agent route load functions\n *\n * A normalized subset of SvelteKit's ServerLoadEvent,\n * so agent load functions don't need a direct SvelteKit dependency.\n */\nexport interface AgentRouteLoadContext {\n params: Record<string, string>;\n parent: () => Promise<Record<string, unknown>>;\n fetch: typeof fetch;\n url: URL;\n}\n\n/**\n * Agent route load function signature\n *\n * Returned data is spread into the page's `data` prop.\n */\nexport type AgentRouteLoadFn = (\n context: AgentRouteLoadContext,\n) => Promise<Record<string, unknown>> | Record<string, unknown>;\n\n/**\n * Agent manifest type (re-exported from smrt-core scanner types)\n * Duplicated here to avoid hard dependency on scanner internals\n */\nexport interface AgentManifestInfo {\n name: string;\n slug: string;\n icon?: string;\n tier: 'free' | 'standard' | 'premium';\n description?: string;\n uiSlots: Record<string, AgentUISlot>;\n adminRoutes?: AgentAdminRoute[];\n /** Create options this agent contributes (`static createEntries`) */\n createEntries?: AgentCreateEntry[];\n /** Default signal subscriptions declared by this agent */\n signalSubscriptions?: string[];\n permissions: Array<{\n id: string;\n label: string;\n category: string;\n defaultGranted?: boolean;\n }>;\n features: Array<{\n id: string;\n label: string;\n description?: string;\n type: string;\n }>;\n menuItems: Array<{\n id: string;\n label: string;\n icon?: string;\n order: number;\n path: string;\n requiredPermission?: string;\n }>;\n components: Array<{ exportPath: string; type: string }>;\n}\n\n/**\n * Registry of UI component implementations\n * Maps agent class name + slot ID to Svelte component\n */\nexport interface AgentUIComponentRegistry {\n /** Register a component for an agent's slot */\n register<TProps extends AdminPanelBaseProps>(\n agentClass: string,\n slotId: string,\n component: ComponentType<TProps>,\n ): void;\n\n /** Get a component for an agent's slot */\n get<TProps extends AdminPanelBaseProps>(\n agentClass: string,\n slotId: string,\n ): ComponentType<TProps> | undefined;\n\n /** Get all registered slot IDs for an agent */\n getSlots(agentClass: string): string[];\n\n /** Check if a component is registered */\n has(agentClass: string, slotId: string): boolean;\n\n /** Get all registered agent class names */\n getAgents(): string[];\n\n /** Unregister a component (useful for testing) */\n unregister(agentClass: string, slotId: string): boolean;\n\n /** Clear all registrations (useful for testing) */\n clear(): void;\n\n /** Register a component by composite key (e.g., 'praeco:sources') */\n registerByKey(key: string, component: ComponentType): void;\n\n /** Get a component by composite key */\n getByKey(key: string): ComponentType | undefined;\n\n /** Register an agent manifest for runtime access */\n registerManifest(agentClass: string, manifest: AgentManifestInfo): void;\n\n /** Get a registered agent manifest */\n getManifest(agentClass: string): AgentManifestInfo | undefined;\n\n /** Get all registered manifests */\n getAllManifests(): Map<string, AgentManifestInfo>;\n\n /** Register a route component for an agent */\n registerRouteComponent(\n agentClass: string,\n path: string,\n component: ComponentType,\n ): void;\n\n /** Get a route component for an agent */\n getRouteComponent(\n agentClass: string,\n path: string,\n ): ComponentType | undefined;\n\n /** Register a route load function for an agent */\n registerRouteLoad(\n agentClass: string,\n path: string,\n loadFn: AgentRouteLoadFn,\n ): void;\n\n /** Get a route load function for an agent */\n getRouteLoad(agentClass: string, path: string): AgentRouteLoadFn | undefined;\n}\n\n/**\n * Create a new UI component registry\n *\n * @example\n * ```typescript\n * const registry = createUIRegistry();\n * registry.register('MyAgent', 'settings', SettingsPanel);\n *\n * const Component = registry.get('MyAgent', 'settings');\n * if (Component) {\n * // Render component\n * }\n * ```\n */\nexport function createUIRegistry(): AgentUIComponentRegistry {\n const components = new Map<string, ComponentType>();\n const manifests = new Map<string, AgentManifestInfo>();\n const routeComponents = new Map<string, ComponentType>();\n const routeLoads = new Map<string, AgentRouteLoadFn>();\n\n const makeKey = (agentClass: string, slotId: string) =>\n `${agentClass}:${slotId}`;\n\n const makeRouteKey = (agentClass: string, path: string) =>\n `${agentClass}:route:${path}`;\n\n return {\n register(agentClass, slotId, component) {\n components.set(makeKey(agentClass, slotId), component);\n },\n\n get(agentClass, slotId) {\n return components.get(makeKey(agentClass, slotId));\n },\n\n getSlots(agentClass) {\n const prefix = `${agentClass}:`;\n return Array.from(components.keys())\n .filter((k) => k.startsWith(prefix))\n .map((k) => k.slice(prefix.length));\n },\n\n has(agentClass, slotId) {\n return components.has(makeKey(agentClass, slotId));\n },\n\n getAgents() {\n const agents = new Set<string>();\n for (const key of components.keys()) {\n const agentClass = key.split(':')[0];\n agents.add(agentClass);\n }\n return Array.from(agents);\n },\n\n unregister(agentClass, slotId) {\n return components.delete(makeKey(agentClass, slotId));\n },\n\n clear() {\n components.clear();\n manifests.clear();\n routeComponents.clear();\n routeLoads.clear();\n },\n\n registerByKey(key, component) {\n components.set(key, component);\n },\n\n getByKey(key) {\n return components.get(key);\n },\n\n registerManifest(agentClass, manifest) {\n manifests.set(agentClass, manifest);\n },\n\n getManifest(agentClass) {\n return manifests.get(agentClass);\n },\n\n getAllManifests() {\n return new Map(manifests);\n },\n\n registerRouteComponent(agentClass, path, component) {\n routeComponents.set(makeRouteKey(agentClass, path), component);\n },\n\n getRouteComponent(agentClass, path) {\n return routeComponents.get(makeRouteKey(agentClass, path));\n },\n\n registerRouteLoad(agentClass, path, loadFn) {\n routeLoads.set(makeRouteKey(agentClass, path), loadFn);\n },\n\n getRouteLoad(agentClass, path) {\n return routeLoads.get(makeRouteKey(agentClass, path));\n },\n };\n}\n\n/**\n * Global UI registry singleton\n *\n * Agent UI packages register their components here at import time,\n * enabling discovery by host applications.\n *\n * Uses a `globalThis.__smrtAgentUIRegistry` property to guarantee a\n * single registry instance per JavaScript runtime, even when bundlers\n * (Vite, webpack) duplicate this module across optimized dependency\n * chunks or package versions.\n *\n * @example\n * ```typescript\n * // In agent package (e.g., @happyvertical/praeco/admin)\n * import { AgentUIRegistry } from '@happyvertical/smrt-agents/ui';\n * import SourcesPanel from './SourcesPanel.svelte';\n *\n * AgentUIRegistry.register('Praeco', 'sources', SourcesPanel);\n *\n * // In host SvelteKit app\n * import { AgentUIRegistry } from '@happyvertical/smrt-agents/ui';\n * import '@happyvertical/praeco/admin'; // Registers components\n *\n * const Component = AgentUIRegistry.get('Praeco', 'sources');\n * ```\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtAgentUIRegistry: AgentUIComponentRegistry | undefined;\n}\n\nif (!globalThis.__smrtAgentUIRegistry) {\n globalThis.__smrtAgentUIRegistry = createUIRegistry();\n}\nexport const AgentUIRegistry: AgentUIComponentRegistry =\n globalThis.__smrtAgentUIRegistry;\n\n/**\n * What an agent's `./admin` entry point must export.\n *\n * This is the contract between agent packages and host apps.\n * Instead of registering individual slot components, agents export\n * a single root component that handles its own sub-navigation.\n *\n * @example\n * ```typescript\n * // In agent package: histrio/src/ui/admin/index.ts\n * export { default } from './AdminRoot.svelte';\n * export { createAPIClient } from '../types.js';\n * export const navItems: AgentAdminNavItem[] = [\n * { id: 'characters', label: 'Characters', icon: 'users', order: 1 },\n * { id: 'performers', label: 'Performers', icon: 'mic', order: 2 },\n * ];\n * ```\n */\nexport interface AgentAdminExport {\n /** Root admin component — renders all panels, handles its own sub-navigation */\n default?: ComponentType;\n /** Create a typed API client for this agent */\n createAPIClient?: (baseUrl: string) => unknown;\n /** Navigation items for tabs/sidebar within the agent admin */\n navItems?: AgentAdminNavItem[];\n}\n\n/**\n * Props passed to the root admin component\n */\nexport interface AgentAdminRootProps {\n /** Typed API client created by the agent's own factory */\n apiClient: unknown;\n /** Which panel to show (from URL hash, e.g., 'sources') */\n activePanel?: string;\n /** Called when user navigates within the agent */\n onNavigate?: (panelId: string) => void;\n /** Whether admin is in read-only mode */\n readonly?: boolean;\n}\n\n/**\n * Navigation item within an agent's admin UI\n */\nexport interface AgentAdminNavItem {\n /** Matches hash fragment and panel ID */\n id: string;\n /** Display label */\n label: string;\n /** Icon identifier */\n icon?: string;\n /** Display order (lower numbers first) */\n order?: number;\n}\n\n/**\n * Agents module UI slots (for ModuleUIRegistry)\n */\nexport const AGENTS_UI_SLOTS: Record<string, ModuleUISlot> = {\n 'agent-dashboard': {\n id: 'agent-dashboard',\n label: 'Agent Dashboard',\n description: 'Combined overview panel for agent schedules',\n icon: 'activity',\n category: 'admin',\n order: 1,\n propsInterface: 'AgentDashboardProps',\n },\n 'agent-schedule-list': {\n id: 'agent-schedule-list',\n label: 'Agent Schedule List',\n description: 'List of scheduled agents',\n icon: 'calendar',\n category: 'list',\n order: 2,\n propsInterface: 'AgentScheduleListProps',\n },\n 'agent-schedule-form': {\n id: 'agent-schedule-form',\n label: 'Agent Schedule Form',\n description: 'Form for creating or editing agent schedules',\n icon: 'edit',\n category: 'form',\n order: 3,\n propsInterface: 'AgentScheduleFormProps',\n },\n 'agent-run-history': {\n id: 'agent-run-history',\n label: 'Agent Run History',\n description: 'History of agent runs',\n icon: 'clock',\n category: 'list',\n order: 4,\n propsInterface: 'AgentRunHistoryProps',\n },\n 'schedule-status-badge': {\n id: 'schedule-status-badge',\n label: 'Schedule Status Badge',\n description: 'Status indicator for schedule states',\n icon: 'tag',\n category: 'display',\n order: 5,\n propsInterface: 'ScheduleStatusBadgeProps',\n },\n};\n\n/**\n * Agents module metadata\n */\nexport const AGENTS_MODULE_META: SmrtModuleMeta = {\n name: '@happyvertical/smrt-agents',\n displayName: 'Agents',\n description: 'Agent framework for building autonomous actors',\n uiSlots: AGENTS_UI_SLOTS,\n models: ['Agent', 'AgentConfig', 'AgentSchedule', 'TenantAgent'],\n collections: [\n 'AgentConfigCollection',\n 'AgentScheduleCollection',\n 'TenantAgentCollection',\n ],\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"smrtRawPrimitives": "strict",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"AGENTS.md"
|
|
50
50
|
],
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"svelte": "^5.57.
|
|
52
|
+
"svelte": "^5.57.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"svelte": {
|
|
@@ -57,25 +57,25 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@happyvertical/ai": "^0.96.
|
|
61
|
-
"@happyvertical/files": "^0.96.
|
|
62
|
-
"@happyvertical/logger": "^0.96.
|
|
63
|
-
"@happyvertical/smrt-config": "0.
|
|
64
|
-
"@happyvertical/smrt-core": "0.
|
|
65
|
-
"@happyvertical/smrt-jobs": "0.
|
|
66
|
-
"@happyvertical/smrt-playbooks": "0.
|
|
67
|
-
"@happyvertical/smrt-reports": "0.
|
|
68
|
-
"@happyvertical/smrt-secrets": "0.
|
|
69
|
-
"@happyvertical/smrt-tenancy": "0.
|
|
70
|
-
"@happyvertical/smrt-types": "0.
|
|
71
|
-
"@happyvertical/smrt-ui": "0.
|
|
72
|
-
"@happyvertical/smrt-users": "0.
|
|
73
|
-
"@happyvertical/sql": "^0.96.
|
|
74
|
-
"@happyvertical/utils": "^0.96.
|
|
60
|
+
"@happyvertical/ai": "^0.96.1",
|
|
61
|
+
"@happyvertical/files": "^0.96.1",
|
|
62
|
+
"@happyvertical/logger": "^0.96.1",
|
|
63
|
+
"@happyvertical/smrt-config": "0.52.0",
|
|
64
|
+
"@happyvertical/smrt-core": "0.52.0",
|
|
65
|
+
"@happyvertical/smrt-jobs": "0.52.0",
|
|
66
|
+
"@happyvertical/smrt-playbooks": "0.52.0",
|
|
67
|
+
"@happyvertical/smrt-reports": "0.52.0",
|
|
68
|
+
"@happyvertical/smrt-secrets": "0.52.0",
|
|
69
|
+
"@happyvertical/smrt-tenancy": "0.52.0",
|
|
70
|
+
"@happyvertical/smrt-types": "0.52.0",
|
|
71
|
+
"@happyvertical/smrt-ui": "0.52.0",
|
|
72
|
+
"@happyvertical/smrt-users": "0.52.0",
|
|
73
|
+
"@happyvertical/sql": "^0.96.1",
|
|
74
|
+
"@happyvertical/utils": "^0.96.1"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@happyvertical/smrt-vitest": "0.
|
|
78
|
-
"@happyvertical/sql": "^0.96.
|
|
77
|
+
"@happyvertical/smrt-vitest": "0.52.0",
|
|
78
|
+
"@happyvertical/sql": "^0.96.1",
|
|
79
79
|
"@sentry/node": "^10.75.0",
|
|
80
80
|
"@sveltejs/package": "^2.5.8",
|
|
81
81
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"execute-as-principal-uuJq7EFs.js","names":["tenantId"],"sources":["../../src/identity.ts","../../src/config.ts","../../src/execute-as-principal.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 configuration owner:\n * - agentId: The durable config owner ID (persona ID for persona-backed\n * instances; Agent STI row ID for legacy/singleton instances)\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: { skipApiCheck: 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 * Durable configuration owner ID.\n *\n * The database column retains the historical `agent_id` name for backward\n * compatibility. Persona-backed runtimes store their `AgentPersona.id` here;\n * legacy runtimes store the persisted Agent STI row id.\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) for backward compatibility with legacy blobs, so this is\n * excluded from generated API/MCP responses and rejected as a `where` filter\n * key. New settings schemas must keep credentials in a dedicated secrets\n * service rather than this field.\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","/**\n * ExecuteAsPrincipal — run an agent's work AS its persona's bound user.\n *\n * This is deliberately NOT a new authorization layer. It reuses the framework's\n * existing principal-context machinery:\n *\n * - {@link withPrincipalPermissionContext} resolves the bound user's *live*\n * permission set (the standard {@link PermissionResolver} cascade) and\n * publishes `(smrt.user_id, smrt.tenant_id, smrt.permissions[])` onto the DB\n * session. With Postgres RLS enabled, the manifest-derived policies then bound\n * every query the agent makes per-`(table, action)` and per-tenant — through\n * any door (in-process \"side door\", REST, MCP), with no per-call re-checking.\n *\n * - Because RLS is Postgres-only and opt-in, the exec/tool seam must ALSO assert\n * the catalog permission for the `(collection, action)` when RLS is off\n * (SQLite/dev). {@link PrincipalRun.assertOperation} wraps\n * `assertOperationPermission` for exactly that, so the authority bound holds\n * on every adapter.\n *\n * The effective authority of an agent action is therefore:\n *\n * bound-user RBAC ∩ agent-class capability ceiling ∩ persona allowedTools\n *\n * where the RBAC half is enforced at the data layer (RLS) or the catalog seam\n * (`assertOperation`), and the tool half is enforced by\n * {@link PrincipalRun.assertToolAllowed} against `allowedTools` — which a\n * resolved persona has already intersected with the `TenantAgent` ceiling.\n *\n * Actions audit as on-behalf-of the originating user (see {@link PrincipalAuditEntry}).\n *\n * @packageDocumentation\n */\n\nimport { createLogger, type Logger } from '@happyvertical/logger';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n assertOperationPermission,\n type OperationPermissionCollectionInput,\n type OperationPermissionDecision,\n type PermissionResolver,\n type SessionPermissionRuntimeContext,\n withPrincipalPermissionContext,\n} from '@happyvertical/smrt-users';\n\n/**\n * The bound principal an agent runs as. A resolved persona structurally\n * satisfies this once its optional `runAsUserId` has been narrowed to a\n * concrete id — `allowedTools` on a `ResolvedPersona` is already the persona's\n * tools intersected with the `TenantAgent` capability ceiling.\n */\nexport interface PrincipalBinding {\n /** The user whose live permissions bound this execution. Required. */\n runAsUserId: string;\n /** Tenant the principal acts within. */\n tenantId: string | null;\n /**\n * The persona's tool allow-list (already capped by the agent-class ceiling).\n * This is a **fail-closed** whitelist, mirroring\n * `@happyvertical/smrt-chat`'s `AgentSession.isToolAllowed()` (S5 #1392): an\n * absent (`undefined`) or empty allow-list permits **NO** tools, never all of\n * them, so forgetting to pass it can only tighten authority. Resolved personas\n * always provide a concrete `string[]`.\n */\n allowedTools?: string[];\n /** Optional acting `Bot` profile id, recorded in the audit entry. */\n actsAsProfileId?: string | null;\n}\n\n/**\n * A single audit record describing an agent action performed by the bound\n * principal (`actorUserId`) on behalf of the originating user\n * (`onBehalfOfUserId`).\n */\nexport interface PrincipalAuditEntry {\n /** Action label, e.g. `'agent.run'`. */\n action: string;\n /** The persona's bound user the work ran as. */\n actorUserId: string;\n /** The user who triggered the agent, if known. */\n onBehalfOfUserId: string | null;\n /** Tenant the action ran within. */\n tenantId: string | null;\n /** Canonical agent class, when the caller supplies it. */\n agentClass?: string;\n /** Acting profile id, when the persona sets one. */\n actsAsProfileId?: string | null;\n /** Free-form additional context. */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Sink that records a {@link PrincipalAuditEntry}. Provide one to persist audit\n * rows (e.g. via `AuditLog.record`); when omitted, the entry is emitted as a\n * structured log line.\n */\nexport type PrincipalAuditSink = (\n entry: PrincipalAuditEntry,\n) => void | Promise<void>;\n\n/**\n * Options for {@link executeAsPrincipal}.\n */\nexport interface ExecuteAsPrincipalOptions extends SmrtClassOptions {\n /** The bound principal to run as. */\n principal: PrincipalBinding;\n /** The originating user the action is performed on behalf of (for audit). */\n onBehalfOfUserId?: string | null;\n /** Canonical agent class, recorded in the audit entry. */\n agentClass?: string;\n /** Audit action label. Defaults to `'agent.run'`. */\n action?: string;\n /** Extra audit metadata merged into the emitted entry. */\n auditMetadata?: Record<string, unknown>;\n /**\n * Pre-resolved permission slugs. When omitted, the principal's permissions\n * are resolved live so role changes reflect on the next execution.\n */\n permissions?: string[];\n /** Reuse an initialized resolver across executions. */\n resolver?: PermissionResolver;\n /** Opt into Postgres RLS transaction wrapping (defaults to package config). */\n postgresRls?: boolean;\n /**\n * Enter tenant context so tenant auto-filtering applies on every adapter.\n * Defaults to `true` whenever the principal has a tenant.\n */\n enterTenantContext?: boolean;\n /** Audit sink. Defaults to a structured log line. */\n audit?: PrincipalAuditSink;\n /** Logger used for the default audit sink. */\n logger?: Logger;\n}\n\n/**\n * Thrown when the persona attempts a tool outside its `allowedTools`.\n */\nexport class PrincipalToolNotAllowedError extends Error {\n readonly tool: string;\n readonly status = 403;\n\n constructor(tool: string) {\n super(`Tool '${tool}' is not permitted for this persona.`);\n this.name = 'PrincipalToolNotAllowedError';\n this.tool = tool;\n }\n}\n\n/**\n * The handle passed to the {@link executeAsPrincipal} body. Its\n * session-permission {@link context} is already published for the principal, so\n * data operations are bounded by RLS on Postgres. The assertions enforce the\n * remaining two authority dimensions.\n */\nexport interface PrincipalRun {\n /** The published session-permission runtime context for the principal. */\n context: SessionPermissionRuntimeContext;\n /** The principal's published (snapshot) permission slugs. */\n permissions: string[];\n /**\n * Present only when the caller bound this run to an explicit `permissions`\n * snapshot (rather than live resolution). Delegation carries it forward as a\n * ceiling so a narrowed run cannot widen through a worker (#2978).\n */\n permissionSnapshot?: readonly string[];\n /**\n * The effective, fail-closed tool allow-list — always a concrete array (an\n * absent binding allow-list normalizes to `[]`, i.e. no tools).\n */\n allowedTools: string[];\n /**\n * Whether `tool` is within the fail-closed allow-list. An empty allow-list,\n * or an empty/non-string tool name, permits nothing.\n */\n isToolAllowed(tool: string): boolean;\n /** Throw {@link PrincipalToolNotAllowedError} unless `tool` is allowed. */\n assertToolAllowed(tool: string): void;\n /**\n * Assert the principal holds the catalog permission for `(collection,\n * action)`, authorizing against the **published** principal set\n * (`context.permissionSet`) — the same snapshot the RLS session enforces — so\n * the bound is adapter-independent. This is the door-agnostic teeth for the\n * RLS-off adapters; under Postgres RLS it is a redundant (but harmless)\n * second gate. Throws `OperationPermissionError` on denial.\n */\n assertOperation(\n collection: OperationPermissionCollectionInput,\n action: string,\n extraOptions?: SmrtClassOptions,\n ): Promise<OperationPermissionDecision>;\n}\n\nasync function emitAudit(\n entry: PrincipalAuditEntry,\n audit: PrincipalAuditSink | undefined,\n logger: Logger | undefined,\n): Promise<void> {\n if (audit) {\n await audit(entry);\n return;\n }\n const log = logger ?? createLogger({ level: 'info' });\n log.info('agent action executed on behalf of originating user', {\n ...entry,\n });\n}\n\n/**\n * Run `fn` AS the persona's bound principal.\n *\n * Resolves the bound user's live permissions, publishes them onto the DB\n * session (so Postgres RLS bounds every query per-`(table, action)`), emits an\n * on-behalf-of audit entry, and hands `fn` a {@link PrincipalRun} whose\n * assertions enforce the persona tool ceiling and the RLS-off catalog gate.\n *\n * @example\n * ```typescript\n * await executeAsPrincipal(\n * {\n * db,\n * principal: {\n * runAsUserId: persona.runAsUserId,\n * tenantId: persona.tenantId,\n * allowedTools: persona.allowedTools,\n * },\n * onBehalfOfUserId: triggeringUserId,\n * agentClass: persona.agentClass,\n * },\n * async (run) => {\n * run.assertToolAllowed('articles.publish');\n * await run.assertOperation('articles', 'update');\n * await agent.run();\n * },\n * );\n * ```\n */\nexport async function executeAsPrincipal<T>(\n options: ExecuteAsPrincipalOptions,\n fn: (run: PrincipalRun) => Promise<T>,\n): Promise<T> {\n const {\n principal,\n onBehalfOfUserId = null,\n agentClass,\n action = 'agent.run',\n auditMetadata,\n permissions,\n resolver,\n postgresRls,\n enterTenantContext,\n audit,\n logger,\n ...smrtOptions\n } = options;\n\n const { runAsUserId, tenantId, actsAsProfileId = null } = principal;\n\n // Fail-closed tool allow-list (mirrors chat's AgentSession, S5 #1392): an\n // absent or non-array binding allow-list normalizes to `[]` — no tools — so a\n // missing ceiling can only tighten authority, never open it up.\n const toolWhitelist = Array.isArray(principal.allowedTools)\n ? principal.allowedTools\n : [];\n const isToolAllowed = (tool: string): boolean =>\n typeof tool === 'string' && tool.length > 0 && toolWhitelist.includes(tool);\n\n await emitAudit(\n {\n action,\n actorUserId: runAsUserId,\n onBehalfOfUserId,\n tenantId,\n agentClass,\n actsAsProfileId,\n metadata: auditMetadata,\n },\n audit,\n logger,\n );\n\n return withPrincipalPermissionContext(\n {\n ...smrtOptions,\n userId: runAsUserId,\n tenantId,\n permissions,\n resolver,\n postgresRls,\n enterTenantContext: enterTenantContext ?? tenantId !== null,\n },\n async (context) => {\n const run: PrincipalRun = {\n context,\n permissions: context.permissions,\n ...(permissions !== undefined\n ? { permissionSnapshot: [...context.permissions] }\n : {}),\n allowedTools: toolWhitelist,\n isToolAllowed,\n assertToolAllowed(tool: string): void {\n if (!isToolAllowed(tool)) {\n throw new PrincipalToolNotAllowedError(tool);\n }\n },\n async assertOperation(\n collection: OperationPermissionCollectionInput,\n operationAction: string,\n extraOptions?: SmrtClassOptions,\n ): Promise<OperationPermissionDecision> {\n return assertOperationPermission({\n ...smrtOptions,\n ...extraOptions,\n collection,\n action: operationAction,\n // Authorize against the PUBLISHED principal set (the same snapshot\n // Postgres RLS enforces for this context), not a fresh live\n // re-resolve — keeps the RLS-off gate adapter-independent.\n permissionSet: context.permissionSet,\n });\n },\n };\n return fn(run);\n },\n );\n}\n"],"mappings":"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"}
|