@happyvertical/smrt-agents 0.51.6 → 0.51.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -243,6 +243,28 @@ const tool = createInvokeAgentTool({
243
243
  - **`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.
244
244
  - **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.
245
245
 
246
+ ## Server Entry Points Stay Svelte-Free (issue #2924)
247
+
248
+ `SmrtDataSurfaceActionTask`, `DataSurfaceActionTokenState`, and
249
+ `DataSurfaceActionIdempotencyState` stamp `@happyvertical/smrt-agents/server`
250
+ as their manifest `importPath`, so core's consumer plugin emits that specifier
251
+ verbatim into a consumer's generated `.smrt/register.js` — which the `smrt` CLI
252
+ loads with a bare `import()` under plain Node for `db:migrate`. **No module
253
+ reachable from `./server` (or from the package root, or `./vite`) may import a
254
+ Svelte component barrel.** A Vite build of the same graph succeeds, so this
255
+ regresses silently and then breaks `db:migrate` for every object in the
256
+ consuming app, not just this package's.
257
+
258
+ - Import shared data-surface protocol limits from the Svelte-free
259
+ `@happyvertical/smrt-ui/data-surface`, never `@happyvertical/smrt-ui/data`
260
+ (a component barrel that re-exports `.svelte` files).
261
+ - `src/server/__tests__/node-entry-loadability.integration.test.ts` imports
262
+ every server-side subpath in a fresh plain-Node process; it needs a built
263
+ `dist/` (turbo's `test` task supplies it).
264
+ - `scripts/verify-manifest-exports.mjs` fails a publish when any **non-root**
265
+ manifest `importPath` is unloadable under plain Node. A bundler-only *root*
266
+ barrel is still only a warning.
267
+
246
268
  ## Key Files
247
269
 
248
270
  | File | Purpose |
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { BackfillTracker } from "@happyvertical/smrt-core/migrations";
11
11
  import { createServerStepEvaluator, preflightPlaybook } from "@happyvertical/smrt-playbooks";
12
12
  import { applyReportExport, applyReportRefresh, buildReportAdapterDescriptor, buildReportDrilldownQuery, createReportExportRequest, createReportExportSnapshot, previewReportExport, previewReportRefresh, queryReportMaterializedRows } from "@happyvertical/smrt-reports";
13
13
  //#region src/__smrt-register__.ts
14
- ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-agents\",\"packageVersion\":\"0.51.6\",\"objects\":{\"@happyvertical/smrt-agents:Agent\":{\"name\":\"agent\",\"className\":\"Agent\",\"qualifiedName\":\"@happyvertical/smrt-agents:Agent\",\"collection\":\"agents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"created_at\":{\"type\":\"datetime\",\"required\":false},\"updated_at\":{\"type\":\"datetime\",\"required\":false},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"status\":{\"type\":\"text\",\"required\":false,\"default\":\"idle\"}},\"methods\":{\"getInstanceKey\":{\"name\":\"getInstanceKey\",\"async\":false,\"parameters\":[],\"returnType\":\"string | null\",\"isStatic\":false,\"isPublic\":true},\"getConfigOwnerId\":{\"name\":\"getConfigOwnerId\",\"async\":false,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":true}],\"returnType\":\"string | null\",\"isStatic\":false,\"isPublic\":true},\"getDispatchSubscriber\":{\"name\":\"getDispatchSubscriber\",\"async\":false,\"parameters\":[],\"returnType\":\"string\",\"isStatic\":false,\"isPublic\":true},\"getUISlots\":{\"name\":\"getUISlots\",\"async\":false,\"parameters\":[],\"returnType\":\"AgentUISlots\",\"isStatic\":false,\"isPublic\":true},\"loadConfigs\":{\"name\":\"loadConfigs\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<Map<string, Record<string, unknown>>>\",\"isStatic\":false,\"isPublic\":true},\"saveSlotConfig\":{\"name\":\"saveSlotConfig\",\"async\":true,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false},{\"name\":\"data\",\"type\":\"Record<string, unknown>\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"getMergedConfig\":{\"name\":\"getMergedConfig\",\"async\":true,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Record<string, unknown>>\",\"isStatic\":false,\"isPublic\":true},\"exportConfig\":{\"name\":\"exportConfig\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"boolean\"]}],\"returnType\":\"Promise<Record<string, unknown>>\",\"isStatic\":false,\"isPublic\":true},\"getDispatch\":{\"name\":\"getDispatch\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<DispatchBus>\",\"isStatic\":false,\"isPublic\":true},\"handleDispatch\":{\"name\":\"handleDispatch\",\"async\":true,\"parameters\":[{\"name\":\"_payload\",\"type\":\"unknown\",\"optional\":false},{\"name\":\"_metadata\",\"type\":\"DispatchMetadata\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"processDispatches\":{\"name\":\"processDispatches\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<number>\",\"isStatic\":false,\"isPublic\":true},\"getLearningMemory\":{\"name\":\"getLearningMemory\",\"async\":false,\"parameters\":[],\"returnType\":\"LearningMemory | null\",\"isStatic\":false,\"isPublic\":true},\"initialize\":{\"name\":\"initialize\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<this>\",\"isStatic\":false,\"isPublic\":true},\"validate\":{\"name\":\"validate\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"shutdown\":{\"name\":\"shutdown\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"execute\":{\"name\":\"execute\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"interesting\":{\"name\":\"interesting\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<InterestResult[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"cli\":false,\"api\":false,\"mcp\":false,\"tableStrategy\":\"sti\",\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\",\"_meta_type\"]},\"extends\":\"SmrtObject\",\"exportName\":\"Agent\",\"collectionExportName\":\"AgentCollection\",\"staticProperties\":{\"uiSlots\":{},\"adminRoutes\":[],\"signalSubscriptions\":[]},\"schema\":{\"tableName\":\"agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"_meta_type\\\" TEXT NOT NULL,\\n \\\"_meta_data\\\" JSON,\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"status\\\" TEXT DEFAULT 'idle'\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"_meta_type\":{\"type\":\"TEXT\",\"notNull\":true},\"_meta_data\":{\"type\":\"JSON\",\"notNull\":false},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"default\":\"idle\"}},\"indexes\":[{\"name\":\"agents_slug_context_meta_type_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\",\"_meta_type\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agents_meta_type_idx\",\"columns\":[\"_meta_type\"]},{\"name\":\"agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"dde8777b\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentConfig\":{\"name\":\"agentconfig\",\"className\":\"AgentConfig\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentConfig\",\"collection\":\"agentconfigs\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/config.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentId\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"agentClass\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"slotId\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"configData\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sensitive\":true},\"sensitive\":true},\"schemaVersion\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}}},\"methods\":{\"forAgent\":{\"name\":\"forAgent\",\"async\":true,\"parameters\":[{\"name\":\"agentId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Record<string, unknown>>>\",\"isStatic\":true,\"isPublic\":true},\"forAgents\":{\"name\":\"forAgents\",\"async\":true,\"parameters\":[{\"name\":\"agentIds\",\"type\":\"string[]\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Map<string, Record<string, unknown>>>>\",\"isStatic\":true,\"isPublic\":true},\"forSlot\":{\"name\":\"forSlot\",\"async\":true,\"parameters\":[{\"name\":\"agentId\",\"type\":\"string\",\"optional\":false},{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Record<string, unknown> | undefined>\",\"isStatic\":true,\"isPublic\":true},\"saveSlot\":{\"name\":\"saveSlot\",\"async\":true,\"parameters\":[{\"name\":\"data\",\"type\":\"object\",\"optional\":false,\"memberTypes\":[\"string\",\"Record<string, unknown>\"]},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig>\",\"isStatic\":true,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"agent_configs\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"mcp\":{\"include\":[\"list\",\"get\"]},\"cli\":{\"skipApiCheck\":true},\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"AgentConfig\",\"collectionExportName\":\"AgentConfigCollection\",\"schema\":{\"tableName\":\"agent_configs\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agent_configs\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_class\\\" TEXT,\\n \\\"slot_id\\\" TEXT,\\n \\\"config_data\\\" JSON,\\n \\\"schema_version\\\" INTEGER\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"slot_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"config_data\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"schema_version\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"agent_configs_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agent_configs_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"25c6ac4c\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentConfigCollection\":{\"name\":\"agentconfigcollection\",\"className\":\"AgentConfigCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentConfigCollection\",\"collection\":\"agentconfigs\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/config.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"findByTenant\":{\"name\":\"findByTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true},\"findGlobal\":{\"name\":\"findGlobal\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true},\"findWithGlobals\":{\"name\":\"findWithGlobals\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"agent_configs\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"AgentConfig\",\"exportName\":\"AgentConfigCollection\",\"collectionExportName\":\"AgentConfigCollectionCollection\",\"schema\":{\"tableName\":\"agent_configs\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agent_configs\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_class\\\" TEXT,\\n \\\"slot_id\\\" TEXT,\\n \\\"config_data\\\" JSON,\\n \\\"schema_version\\\" INTEGER\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"slot_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"config_data\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"schema_version\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"agent_configs_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agent_configs_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"25c6ac4c\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentSchedule\":{\"name\":\"agentschedule\",\"className\":\"AgentSchedule\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentSchedule\",\"collection\":\"agentschedules\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/schedule.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentType\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"agentId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"agentConfig\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sqlType\":\"TEXT\",\"sensitive\":true},\"sensitive\":true},\"cron\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"timezone\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"enabled\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"lastRun\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"nextRun\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"lastStatus\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"lastError\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"runCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"successCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"failureCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"maxConcurrent\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"runningCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"timeout\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"method\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"methodArgs\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sqlType\":\"TEXT\"}}},\"methods\":{\"enable\":{\"name\":\"enable\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"disable\":{\"name\":\"disable\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"pause\":{\"name\":\"pause\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"resume\":{\"name\":\"resume\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"calculateNextRun\":{\"name\":\"calculateNextRun\",\"async\":false,\"parameters\":[],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getDescription\":{\"name\":\"getDescription\",\"async\":false,\"parameters\":[],\"returnType\":\"string\",\"isStatic\":false,\"isPublic\":true},\"beforeSave\":{\"name\":\"beforeSave\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_agent_schedules\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\",\"enable\",\"disable\"],\"skipApiCheck\":true},\"mcp\":{\"include\":[\"list\",\"get\"]},\"indexes\":[{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"nextRun\"]}],\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"AgentSchedule\",\"collectionExportName\":\"AgentScheduleCollection\",\"schema\":{\"tableName\":\"_smrt_agent_schedules\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_agent_schedules\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_type\\\" TEXT,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_config\\\" TEXT,\\n \\\"cron\\\" TEXT,\\n \\\"timezone\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"status\\\" TEXT,\\n \\\"last_run\\\" TIMESTAMP,\\n \\\"next_run\\\" TIMESTAMP,\\n \\\"last_status\\\" TEXT,\\n \\\"last_error\\\" TEXT,\\n \\\"run_count\\\" INTEGER,\\n \\\"success_count\\\" INTEGER,\\n \\\"failure_count\\\" INTEGER,\\n \\\"max_concurrent\\\" INTEGER,\\n \\\"running_count\\\" INTEGER,\\n \\\"timeout\\\" INTEGER,\\n \\\"method\\\" TEXT,\\n \\\"method_args\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_type\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_config\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"cron\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"timezone\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"next_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"last_status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_error\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"run_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"success_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"failure_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"max_concurrent\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"running_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"timeout\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"method\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"method_args\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_agent_schedules_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"next_run\"]},{\"name\":\"_smrt_agent_schedules_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"c21a1f29\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentScheduleCollection\":{\"name\":\"agentschedulecollection\",\"className\":\"AgentScheduleCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentScheduleCollection\",\"collection\":\"agentschedules\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/schedule.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"findByTenant\":{\"name\":\"findByTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"findGlobal\":{\"name\":\"findGlobal\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"findWithGlobals\":{\"name\":\"findWithGlobals\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"listByStatus\":{\"name\":\"listByStatus\",\"async\":true,\"parameters\":[{\"name\":\"status\",\"type\":\"ScheduleStatus | ScheduleStatus[]\",\"optional\":false,\"unionBranches\":[{\"type\":\"ScheduleStatus\"},{\"type\":\"ScheduleStatus[]\"}]},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"number\"]}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"listByAgentType\":{\"name\":\"listByAgentType\",\"async\":true,\"parameters\":[{\"name\":\"agentType\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"number\",\"boolean\"]}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_agent_schedules\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"AgentSchedule\",\"exportName\":\"AgentScheduleCollection\",\"collectionExportName\":\"AgentScheduleCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_agent_schedules\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_agent_schedules\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_type\\\" TEXT,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_config\\\" TEXT,\\n \\\"cron\\\" TEXT,\\n \\\"timezone\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"status\\\" TEXT,\\n \\\"last_run\\\" TIMESTAMP,\\n \\\"next_run\\\" TIMESTAMP,\\n \\\"last_status\\\" TEXT,\\n \\\"last_error\\\" TEXT,\\n \\\"run_count\\\" INTEGER,\\n \\\"success_count\\\" INTEGER,\\n \\\"failure_count\\\" INTEGER,\\n \\\"max_concurrent\\\" INTEGER,\\n \\\"running_count\\\" INTEGER,\\n \\\"timeout\\\" INTEGER,\\n \\\"method\\\" TEXT,\\n \\\"method_args\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_type\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_config\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"cron\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"timezone\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"next_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"last_status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_error\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"run_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"success_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"failure_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"max_concurrent\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"running_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"timeout\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"method\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"method_args\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_agent_schedules_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"next_run\"]},{\"name\":\"_smrt_agent_schedules_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"c21a1f29\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:TenantAgent\":{\"name\":\"tenantagent\",\"className\":\"TenantAgent\",\"qualifiedName\":\"@happyvertical/smrt-agents:TenantAgent\",\"collection\":\"tenantagents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/tenant-agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false,\"mode\":\"required\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentClass\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"permissions\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}},\"config\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true,\"sensitive\":true},\"sensitive\":true}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"tenant_agents\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\"]},\"mcp\":{\"include\":[\"list\",\"get\"]},\"conflictColumns\":[\"tenant_id\",\"agent_class\"],\"tenantScoped\":{\"mode\":\"required\"}},\"extends\":\"SmrtObject\",\"exportName\":\"TenantAgent\",\"collectionExportName\":\"TenantAgentCollection\",\"validationRules\":[],\"schema\":{\"tableName\":\"tenant_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"tenant_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"agent_class\\\" TEXT,\\n \\\"status\\\" TEXT,\\n \\\"permissions\\\" JSON,\\n \\\"config\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"permissions\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"config\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"tenant_agents_tenant_id_agent_class_idx\",\"columns\":[\"tenant_id\",\"agent_class\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"tenant_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"tenant_agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"9892a054\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:TenantAgentCollection\":{\"name\":\"tenantagentcollection\",\"className\":\"TenantAgentCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:TenantAgentCollection\",\"collection\":\"tenantagents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/tenant-agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"resolveForTenant\":{\"name\":\"resolveForTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"getAncestorIds\",\"type\":\"Function\",\"optional\":false},{\"name\":\"manifests\",\"type\":\"Map<string, AgentManifestInfo>\",\"optional\":true}],\"returnType\":\"Promise<ResolvedAgentAvailability[]>\",\"isStatic\":false,\"isPublic\":true},\"enableAgent\":{\"name\":\"enableAgent\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"disableAgent\":{\"name\":\"disableAgent\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"clearOverride\":{\"name\":\"clearOverride\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"setPermissions\":{\"name\":\"setPermissions\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false},{\"name\":\"permissions\",\"type\":\"Record<string, boolean>\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"findByTenantAndClass\":{\"name\":\"findByTenantAndClass\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent | null>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"tenant_agents\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"TenantAgent\",\"exportName\":\"TenantAgentCollection\",\"collectionExportName\":\"TenantAgentCollectionCollection\",\"schema\":{\"tableName\":\"tenant_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"tenant_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"agent_class\\\" TEXT,\\n \\\"status\\\" TEXT,\\n \\\"permissions\\\" JSON,\\n \\\"config\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"permissions\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"config\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"tenant_agents_tenant_id_agent_class_idx\",\"columns\":[\"tenant_id\",\"agent_class\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"tenant_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"tenant_agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"9892a054\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:SmrtDataSurfaceActionTask\":{\"name\":\"smrtdatasurfaceactiontask\",\"className\":\"SmrtDataSurfaceActionTask\",\"qualifiedName\":\"@happyvertical/smrt-agents:SmrtDataSurfaceActionTask\",\"collection\":\"smrtdatasurfaceactiontasks\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/jobs-data-surface-action-queue.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"args\":{\"type\":\"json\",\"required\":true,\"_meta\":{\"required\":true}}},\"methods\":{\"run\":{\"name\":\"run\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"DataSurfaceActionJobArgs\",\"optional\":true},{\"name\":\"context\",\"type\":\"JobExecutionContext\",\"optional\":true}],\"returnType\":\"Promise<DataSurfaceActionResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_tasks\",\"api\":false,\"cli\":false,\"mcp\":false,\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"SmrtDataSurfaceActionTask\",\"collectionExportName\":\"SmrtDataSurfaceActionTaskCollection\",\"validationRules\":[{\"field\":\"args\",\"rule\":\"required\",\"fieldType\":\"json\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_tasks\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_tasks\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"args\\\" JSON NOT NULL\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"args\":{\"type\":\"JSON\",\"notNull\":true,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_tasks_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_data_surface_action_tasks_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"d1c4adf8\"},\"importPath\":\"@happyvertical/smrt-agents/server\"},\"@happyvertical/smrt-agents:DataSurfaceActionTokenState\":{\"name\":\"datasurfaceactiontokenstate\",\"className\":\"DataSurfaceActionTokenState\",\"qualifiedName\":\"@happyvertical/smrt-agents:DataSurfaceActionTokenState\",\"collection\":\"datasurfaceactiontokenstates\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/sql-data-surface-action-state.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tokenHash\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true,\"unique\":true}},\"record\":{\"type\":\"json\",\"required\":true,\"_meta\":{\"required\":true}},\"consumedBy\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_tokens\",\"api\":false,\"cli\":false,\"mcp\":false},\"extends\":\"SmrtObject\",\"exportName\":\"DataSurfaceActionTokenState\",\"collectionExportName\":\"DataSurfaceActionTokenStateCollection\",\"validationRules\":[{\"field\":\"tokenHash\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"record\",\"rule\":\"required\",\"fieldType\":\"json\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_tokens\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_tokens\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"token_hash\\\" TEXT NOT NULL UNIQUE,\\n \\\"record\\\" JSON NOT NULL,\\n \\\"consumed_by\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"token_hash\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":true},\"record\":{\"type\":\"JSON\",\"notNull\":true,\"unique\":false},\"consumed_by\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_tokens_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true},{\"name\":\"_smrt_data_surface_action_tokens_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"497cf260\"},\"importPath\":\"@happyvertical/smrt-agents/server\"},\"@happyvertical/smrt-agents:DataSurfaceActionIdempotencyState\":{\"name\":\"datasurfaceactionidempotencystate\",\"className\":\"DataSurfaceActionIdempotencyState\",\"qualifiedName\":\"@happyvertical/smrt-agents:DataSurfaceActionIdempotencyState\",\"collection\":\"datasurfaceactionidempotencystates\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/sql-data-surface-action-state.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"keyHash\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true,\"unique\":true}},\"status\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"requestFingerprint\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"ownerHash\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"reservedAt\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"result\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}},\"recovery\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_idempotency\",\"api\":false,\"cli\":false,\"mcp\":false},\"extends\":\"SmrtObject\",\"exportName\":\"DataSurfaceActionIdempotencyState\",\"collectionExportName\":\"DataSurfaceActionIdempotencyStateCollection\",\"validationRules\":[{\"field\":\"keyHash\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"status\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"requestFingerprint\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_idempotency\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_idempotency\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"key_hash\\\" TEXT NOT NULL UNIQUE,\\n \\\"status\\\" TEXT NOT NULL,\\n \\\"request_fingerprint\\\" TEXT NOT NULL,\\n \\\"owner_hash\\\" TEXT,\\n \\\"reserved_at\\\" TEXT,\\n \\\"result\\\" JSON,\\n \\\"recovery\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"key_hash\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":true},\"status\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"request_fingerprint\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"owner_hash\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"reserved_at\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"result\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"recovery\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_idempotency_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true},{\"name\":\"_smrt_data_surface_action_idempotency_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"eb65ff1e\"},\"importPath\":\"@happyvertical/smrt-agents/server\"}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-jobs\",\"@happyvertical/smrt-playbooks\",\"@happyvertical/smrt-reports\",\"@happyvertical/smrt-secrets\",\"@happyvertical/smrt-tenancy\",\"@happyvertical/smrt-users\"]}"));
14
+ ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-agents\",\"packageVersion\":\"0.51.8\",\"objects\":{\"@happyvertical/smrt-agents:Agent\":{\"name\":\"agent\",\"className\":\"Agent\",\"qualifiedName\":\"@happyvertical/smrt-agents:Agent\",\"collection\":\"agents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"created_at\":{\"type\":\"datetime\",\"required\":false},\"updated_at\":{\"type\":\"datetime\",\"required\":false},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"status\":{\"type\":\"text\",\"required\":false,\"default\":\"idle\"}},\"methods\":{\"getInstanceKey\":{\"name\":\"getInstanceKey\",\"async\":false,\"parameters\":[],\"returnType\":\"string | null\",\"isStatic\":false,\"isPublic\":true},\"getConfigOwnerId\":{\"name\":\"getConfigOwnerId\",\"async\":false,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":true}],\"returnType\":\"string | null\",\"isStatic\":false,\"isPublic\":true},\"getDispatchSubscriber\":{\"name\":\"getDispatchSubscriber\",\"async\":false,\"parameters\":[],\"returnType\":\"string\",\"isStatic\":false,\"isPublic\":true},\"getUISlots\":{\"name\":\"getUISlots\",\"async\":false,\"parameters\":[],\"returnType\":\"AgentUISlots\",\"isStatic\":false,\"isPublic\":true},\"loadConfigs\":{\"name\":\"loadConfigs\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<Map<string, Record<string, unknown>>>\",\"isStatic\":false,\"isPublic\":true},\"saveSlotConfig\":{\"name\":\"saveSlotConfig\",\"async\":true,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false},{\"name\":\"data\",\"type\":\"Record<string, unknown>\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"getMergedConfig\":{\"name\":\"getMergedConfig\",\"async\":true,\"parameters\":[{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Record<string, unknown>>\",\"isStatic\":false,\"isPublic\":true},\"exportConfig\":{\"name\":\"exportConfig\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"boolean\"]}],\"returnType\":\"Promise<Record<string, unknown>>\",\"isStatic\":false,\"isPublic\":true},\"getDispatch\":{\"name\":\"getDispatch\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<DispatchBus>\",\"isStatic\":false,\"isPublic\":true},\"handleDispatch\":{\"name\":\"handleDispatch\",\"async\":true,\"parameters\":[{\"name\":\"_payload\",\"type\":\"unknown\",\"optional\":false},{\"name\":\"_metadata\",\"type\":\"DispatchMetadata\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"processDispatches\":{\"name\":\"processDispatches\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<number>\",\"isStatic\":false,\"isPublic\":true},\"getLearningMemory\":{\"name\":\"getLearningMemory\",\"async\":false,\"parameters\":[],\"returnType\":\"LearningMemory | null\",\"isStatic\":false,\"isPublic\":true},\"initialize\":{\"name\":\"initialize\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<this>\",\"isStatic\":false,\"isPublic\":true},\"validate\":{\"name\":\"validate\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"shutdown\":{\"name\":\"shutdown\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"execute\":{\"name\":\"execute\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"interesting\":{\"name\":\"interesting\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<InterestResult[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"cli\":false,\"api\":false,\"mcp\":false,\"tableStrategy\":\"sti\",\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\",\"_meta_type\"]},\"extends\":\"SmrtObject\",\"exportName\":\"Agent\",\"collectionExportName\":\"AgentCollection\",\"staticProperties\":{\"uiSlots\":{},\"adminRoutes\":[],\"signalSubscriptions\":[]},\"schema\":{\"tableName\":\"agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"_meta_type\\\" TEXT NOT NULL,\\n \\\"_meta_data\\\" JSON,\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"status\\\" TEXT DEFAULT 'idle'\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"_meta_type\":{\"type\":\"TEXT\",\"notNull\":true},\"_meta_data\":{\"type\":\"JSON\",\"notNull\":false},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"default\":\"idle\"}},\"indexes\":[{\"name\":\"agents_slug_context_meta_type_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\",\"_meta_type\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agents_meta_type_idx\",\"columns\":[\"_meta_type\"]},{\"name\":\"agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"dde8777b\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentConfig\":{\"name\":\"agentconfig\",\"className\":\"AgentConfig\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentConfig\",\"collection\":\"agentconfigs\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/config.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentId\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"agentClass\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"slotId\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"configData\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sensitive\":true},\"sensitive\":true},\"schemaVersion\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}}},\"methods\":{\"forAgent\":{\"name\":\"forAgent\",\"async\":true,\"parameters\":[{\"name\":\"agentId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Record<string, unknown>>>\",\"isStatic\":true,\"isPublic\":true},\"forAgents\":{\"name\":\"forAgents\",\"async\":true,\"parameters\":[{\"name\":\"agentIds\",\"type\":\"string[]\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Map<string, Record<string, unknown>>>>\",\"isStatic\":true,\"isPublic\":true},\"forSlot\":{\"name\":\"forSlot\",\"async\":true,\"parameters\":[{\"name\":\"agentId\",\"type\":\"string\",\"optional\":false},{\"name\":\"slotId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<Record<string, unknown> | undefined>\",\"isStatic\":true,\"isPublic\":true},\"saveSlot\":{\"name\":\"saveSlot\",\"async\":true,\"parameters\":[{\"name\":\"data\",\"type\":\"object\",\"optional\":false,\"memberTypes\":[\"string\",\"Record<string, unknown>\"]},{\"name\":\"options\",\"type\":\"SmrtClassOptions\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig>\",\"isStatic\":true,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"agent_configs\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"mcp\":{\"include\":[\"list\",\"get\"]},\"cli\":{\"skipApiCheck\":true},\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"AgentConfig\",\"collectionExportName\":\"AgentConfigCollection\",\"schema\":{\"tableName\":\"agent_configs\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agent_configs\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_class\\\" TEXT,\\n \\\"slot_id\\\" TEXT,\\n \\\"config_data\\\" JSON,\\n \\\"schema_version\\\" INTEGER\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"slot_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"config_data\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"schema_version\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"agent_configs_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agent_configs_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"25c6ac4c\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentConfigCollection\":{\"name\":\"agentconfigcollection\",\"className\":\"AgentConfigCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentConfigCollection\",\"collection\":\"agentconfigs\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/config.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"findByTenant\":{\"name\":\"findByTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true},\"findGlobal\":{\"name\":\"findGlobal\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true},\"findWithGlobals\":{\"name\":\"findWithGlobals\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentConfig[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"agent_configs\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"AgentConfig\",\"exportName\":\"AgentConfigCollection\",\"collectionExportName\":\"AgentConfigCollectionCollection\",\"schema\":{\"tableName\":\"agent_configs\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"agent_configs\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_class\\\" TEXT,\\n \\\"slot_id\\\" TEXT,\\n \\\"config_data\\\" JSON,\\n \\\"schema_version\\\" INTEGER\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"slot_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"config_data\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"schema_version\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"agent_configs_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"agent_configs_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"25c6ac4c\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentSchedule\":{\"name\":\"agentschedule\",\"className\":\"AgentSchedule\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentSchedule\",\"collection\":\"agentschedules\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/schedule.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentType\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"agentId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"agentConfig\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sqlType\":\"TEXT\",\"sensitive\":true},\"sensitive\":true},\"cron\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"timezone\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"enabled\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"lastRun\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"nextRun\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"lastStatus\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"lastError\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"runCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"successCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"failureCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"maxConcurrent\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"runningCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"timeout\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"method\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"methodArgs\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"sqlType\":\"TEXT\"}}},\"methods\":{\"enable\":{\"name\":\"enable\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"disable\":{\"name\":\"disable\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"pause\":{\"name\":\"pause\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"resume\":{\"name\":\"resume\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"calculateNextRun\":{\"name\":\"calculateNextRun\",\"async\":false,\"parameters\":[],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getDescription\":{\"name\":\"getDescription\",\"async\":false,\"parameters\":[],\"returnType\":\"string\",\"isStatic\":false,\"isPublic\":true},\"beforeSave\":{\"name\":\"beforeSave\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_agent_schedules\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\",\"enable\",\"disable\"],\"skipApiCheck\":true},\"mcp\":{\"include\":[\"list\",\"get\"]},\"indexes\":[{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"nextRun\"]}],\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"AgentSchedule\",\"collectionExportName\":\"AgentScheduleCollection\",\"schema\":{\"tableName\":\"_smrt_agent_schedules\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_agent_schedules\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_type\\\" TEXT,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_config\\\" TEXT,\\n \\\"cron\\\" TEXT,\\n \\\"timezone\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"status\\\" TEXT,\\n \\\"last_run\\\" TIMESTAMP,\\n \\\"next_run\\\" TIMESTAMP,\\n \\\"last_status\\\" TEXT,\\n \\\"last_error\\\" TEXT,\\n \\\"run_count\\\" INTEGER,\\n \\\"success_count\\\" INTEGER,\\n \\\"failure_count\\\" INTEGER,\\n \\\"max_concurrent\\\" INTEGER,\\n \\\"running_count\\\" INTEGER,\\n \\\"timeout\\\" INTEGER,\\n \\\"method\\\" TEXT,\\n \\\"method_args\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_type\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_config\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"cron\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"timezone\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"next_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"last_status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_error\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"run_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"success_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"failure_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"max_concurrent\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"running_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"timeout\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"method\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"method_args\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_agent_schedules_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"next_run\"]},{\"name\":\"_smrt_agent_schedules_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"c21a1f29\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:AgentScheduleCollection\":{\"name\":\"agentschedulecollection\",\"className\":\"AgentScheduleCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:AgentScheduleCollection\",\"collection\":\"agentschedules\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/schedule.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"findByTenant\":{\"name\":\"findByTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"findGlobal\":{\"name\":\"findGlobal\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"findWithGlobals\":{\"name\":\"findWithGlobals\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"listByStatus\":{\"name\":\"listByStatus\",\"async\":true,\"parameters\":[{\"name\":\"status\",\"type\":\"ScheduleStatus | ScheduleStatus[]\",\"optional\":false,\"unionBranches\":[{\"type\":\"ScheduleStatus\"},{\"type\":\"ScheduleStatus[]\"}]},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"number\"]}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true},\"listByAgentType\":{\"name\":\"listByAgentType\",\"async\":true,\"parameters\":[{\"name\":\"agentType\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"number\",\"boolean\"]}],\"returnType\":\"Promise<AgentSchedule[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_agent_schedules\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"AgentSchedule\",\"exportName\":\"AgentScheduleCollection\",\"collectionExportName\":\"AgentScheduleCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_agent_schedules\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_agent_schedules\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"agent_type\\\" TEXT,\\n \\\"agent_id\\\" TEXT,\\n \\\"agent_config\\\" TEXT,\\n \\\"cron\\\" TEXT,\\n \\\"timezone\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"status\\\" TEXT,\\n \\\"last_run\\\" TIMESTAMP,\\n \\\"next_run\\\" TIMESTAMP,\\n \\\"last_status\\\" TEXT,\\n \\\"last_error\\\" TEXT,\\n \\\"run_count\\\" INTEGER,\\n \\\"success_count\\\" INTEGER,\\n \\\"failure_count\\\" INTEGER,\\n \\\"max_concurrent\\\" INTEGER,\\n \\\"running_count\\\" INTEGER,\\n \\\"timeout\\\" INTEGER,\\n \\\"method\\\" TEXT,\\n \\\"method_args\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"agent_type\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_id\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"agent_config\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"cron\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"timezone\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"next_run\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"last_status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"last_error\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"run_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"success_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"failure_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"max_concurrent\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"running_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"timeout\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"method\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"method_args\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_agent_schedules_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_agent_schedules_enabled_status_next_run_idx\",\"columns\":[\"enabled\",\"status\",\"next_run\"]},{\"name\":\"_smrt_agent_schedules_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"c21a1f29\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:TenantAgent\":{\"name\":\"tenantagent\",\"className\":\"TenantAgent\",\"qualifiedName\":\"@happyvertical/smrt-agents:TenantAgent\",\"collection\":\"tenantagents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/tenant-agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false,\"mode\":\"required\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"agentClass\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"permissions\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}},\"config\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true,\"sensitive\":true},\"sensitive\":true}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"tenant_agents\",\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\"]},\"mcp\":{\"include\":[\"list\",\"get\"]},\"conflictColumns\":[\"tenant_id\",\"agent_class\"],\"tenantScoped\":{\"mode\":\"required\"}},\"extends\":\"SmrtObject\",\"exportName\":\"TenantAgent\",\"collectionExportName\":\"TenantAgentCollection\",\"validationRules\":[],\"schema\":{\"tableName\":\"tenant_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"tenant_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"agent_class\\\" TEXT,\\n \\\"status\\\" TEXT,\\n \\\"permissions\\\" JSON,\\n \\\"config\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"permissions\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"config\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"tenant_agents_tenant_id_agent_class_idx\",\"columns\":[\"tenant_id\",\"agent_class\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"tenant_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"tenant_agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"9892a054\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:TenantAgentCollection\":{\"name\":\"tenantagentcollection\",\"className\":\"TenantAgentCollection\",\"qualifiedName\":\"@happyvertical/smrt-agents:TenantAgentCollection\",\"collection\":\"tenantagents\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/tenant-agent.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{},\"methods\":{\"resolveForTenant\":{\"name\":\"resolveForTenant\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"getAncestorIds\",\"type\":\"Function\",\"optional\":false},{\"name\":\"manifests\",\"type\":\"Map<string, AgentManifestInfo>\",\"optional\":true}],\"returnType\":\"Promise<ResolvedAgentAvailability[]>\",\"isStatic\":false,\"isPublic\":true},\"enableAgent\":{\"name\":\"enableAgent\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"disableAgent\":{\"name\":\"disableAgent\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"clearOverride\":{\"name\":\"clearOverride\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true},\"setPermissions\":{\"name\":\"setPermissions\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false},{\"name\":\"permissions\",\"type\":\"Record<string, boolean>\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent>\",\"isStatic\":false,\"isPublic\":true},\"findByTenantAndClass\":{\"name\":\"findByTenantAndClass\",\"async\":true,\"parameters\":[{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"agentClass\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<TenantAgent | null>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"tenant_agents\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"TenantAgent\",\"exportName\":\"TenantAgentCollection\",\"collectionExportName\":\"TenantAgentCollectionCollection\",\"schema\":{\"tableName\":\"tenant_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"tenant_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"agent_class\\\" TEXT,\\n \\\"status\\\" TEXT,\\n \\\"permissions\\\" JSON,\\n \\\"config\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"agent_class\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"permissions\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"config\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"tenant_agents_tenant_id_agent_class_idx\",\"columns\":[\"tenant_id\",\"agent_class\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"tenant_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"tenant_agents_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"9892a054\"},\"importPath\":\"@happyvertical/smrt-agents\"},\"@happyvertical/smrt-agents:SmrtDataSurfaceActionTask\":{\"name\":\"smrtdatasurfaceactiontask\",\"className\":\"SmrtDataSurfaceActionTask\",\"qualifiedName\":\"@happyvertical/smrt-agents:SmrtDataSurfaceActionTask\",\"collection\":\"smrtdatasurfaceactiontasks\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/jobs-data-surface-action-queue.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true,\"mode\":\"optional\",\"field\":\"tenantId\",\"allowSuperAdminBypass\":false}}},\"args\":{\"type\":\"json\",\"required\":true,\"_meta\":{\"required\":true}}},\"methods\":{\"run\":{\"name\":\"run\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"DataSurfaceActionJobArgs\",\"optional\":true},{\"name\":\"context\",\"type\":\"JobExecutionContext\",\"optional\":true}],\"returnType\":\"Promise<DataSurfaceActionResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_tasks\",\"api\":false,\"cli\":false,\"mcp\":false,\"tenantScoped\":{\"mode\":\"optional\"},\"conflictColumns\":[\"tenant_id\",\"slug\",\"context\"]},\"extends\":\"SmrtObject\",\"exportName\":\"SmrtDataSurfaceActionTask\",\"collectionExportName\":\"SmrtDataSurfaceActionTaskCollection\",\"validationRules\":[{\"field\":\"args\",\"rule\":\"required\",\"fieldType\":\"json\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_tasks\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_tasks\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID,\\n \\\"args\\\" JSON NOT NULL\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"args\":{\"type\":\"JSON\",\"notNull\":true,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_tasks_slug_context_idx\",\"columns\":[\"tenant_id\",\"slug\",\"context\"],\"unique\":true,\"nullsNotDistinct\":true},{\"name\":\"_smrt_data_surface_action_tasks_tenant_id_created_at_idx\",\"columns\":[\"tenant_id\",\"created_at\"]}],\"version\":\"d1c4adf8\"},\"importPath\":\"@happyvertical/smrt-agents/server\"},\"@happyvertical/smrt-agents:DataSurfaceActionTokenState\":{\"name\":\"datasurfaceactiontokenstate\",\"className\":\"DataSurfaceActionTokenState\",\"qualifiedName\":\"@happyvertical/smrt-agents:DataSurfaceActionTokenState\",\"collection\":\"datasurfaceactiontokenstates\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/sql-data-surface-action-state.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"tokenHash\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true,\"unique\":true}},\"record\":{\"type\":\"json\",\"required\":true,\"_meta\":{\"required\":true}},\"consumedBy\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_tokens\",\"api\":false,\"cli\":false,\"mcp\":false},\"extends\":\"SmrtObject\",\"exportName\":\"DataSurfaceActionTokenState\",\"collectionExportName\":\"DataSurfaceActionTokenStateCollection\",\"validationRules\":[{\"field\":\"tokenHash\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"record\",\"rule\":\"required\",\"fieldType\":\"json\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_tokens\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_tokens\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"token_hash\\\" TEXT NOT NULL UNIQUE,\\n \\\"record\\\" JSON NOT NULL,\\n \\\"consumed_by\\\" TEXT\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"token_hash\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":true},\"record\":{\"type\":\"JSON\",\"notNull\":true,\"unique\":false},\"consumed_by\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_tokens_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true},{\"name\":\"_smrt_data_surface_action_tokens_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"497cf260\"},\"importPath\":\"@happyvertical/smrt-agents/server\"},\"@happyvertical/smrt-agents:DataSurfaceActionIdempotencyState\":{\"name\":\"datasurfaceactionidempotencystate\",\"className\":\"DataSurfaceActionIdempotencyState\",\"qualifiedName\":\"@happyvertical/smrt-agents:DataSurfaceActionIdempotencyState\",\"collection\":\"datasurfaceactionidempotencystates\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/agents/src/server/sql-data-surface-action-state.ts\",\"packageName\":\"@happyvertical/smrt-agents\",\"fields\":{\"keyHash\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true,\"unique\":true}},\"status\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"requestFingerprint\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"ownerHash\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"reservedAt\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"result\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}},\"recovery\":{\"type\":\"json\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_data_surface_action_idempotency\",\"api\":false,\"cli\":false,\"mcp\":false},\"extends\":\"SmrtObject\",\"exportName\":\"DataSurfaceActionIdempotencyState\",\"collectionExportName\":\"DataSurfaceActionIdempotencyStateCollection\",\"validationRules\":[{\"field\":\"keyHash\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"status\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"requestFingerprint\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_data_surface_action_idempotency\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_data_surface_action_idempotency\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"key_hash\\\" TEXT NOT NULL UNIQUE,\\n \\\"status\\\" TEXT NOT NULL,\\n \\\"request_fingerprint\\\" TEXT NOT NULL,\\n \\\"owner_hash\\\" TEXT,\\n \\\"reserved_at\\\" TEXT,\\n \\\"result\\\" JSON,\\n \\\"recovery\\\" JSON\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"key_hash\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":true},\"status\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"request_fingerprint\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"owner_hash\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"reserved_at\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"result\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false},\"recovery\":{\"type\":\"JSON\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_data_surface_action_idempotency_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true},{\"name\":\"_smrt_data_surface_action_idempotency_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"eb65ff1e\"},\"importPath\":\"@happyvertical/smrt-agents/server\"}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-jobs\",\"@happyvertical/smrt-playbooks\",\"@happyvertical/smrt-reports\",\"@happyvertical/smrt-secrets\",\"@happyvertical/smrt-tenancy\",\"@happyvertical/smrt-users\"]}"));
15
15
  //#endregion
16
16
  //#region src/ai-config.ts
17
17
  var DEFAULT_SECRET_NAMES = {
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-agents",
5
- "packageVersion": "0.51.6",
5
+ "packageVersion": "0.51.8",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-agents:Agent": {
8
8
  "name": "agent",
@@ -1 +1 @@
1
- {"version":3,"file":"sveltekit-data-surface-routes.d.ts","sourceRoot":"","sources":["../../src/server/sveltekit-data-surface-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EACV,wBAAwB,EAEzB,MAAM,2BAA2B,CAAC;AAEnC,8EAA8E;AAC9E,MAAM,MAAM,iCAAiC,GAAG,CAC9C,OAAO,EAAE,OAAO,KACb,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAEpE,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,wBAAwB,CAAC;IAClC,gBAAgB,EAAE,iCAAiC,CAAC;IACpD,0EAA0E;IAC1E,WAAW,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,gGAAgG;IAChG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC5C;AA0HD;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,8BAA8B,GACtC,wBAAwB,CA2D1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gCAAgC;IAC/C,8EAA8E;IAC9E,MAAM,CACJ,YAAY,EAAE,gBAAgB,EAAE,GAC/B,OAAO,CAAC,gBAAgB,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC;CACrD;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,gBAAgB,EAAE,EAChC,QAAQ,EAAE,gCAAgC,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAW7B"}
1
+ {"version":3,"file":"sveltekit-data-surface-routes.d.ts","sourceRoot":"","sources":["../../src/server/sveltekit-data-surface-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EACV,wBAAwB,EAEzB,MAAM,2BAA2B,CAAC;AAEnC,8EAA8E;AAC9E,MAAM,MAAM,iCAAiC,GAAG,CAC9C,OAAO,EAAE,OAAO,KACb,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAEpE,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,wBAAwB,CAAC;IAClC,gBAAgB,EAAE,iCAAiC,CAAC;IACpD,0EAA0E;IAC1E,WAAW,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,gGAAgG;IAChG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC5C;AA0HD;;;;;;;;;GASG;AACH,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,8BAA8B,GACtC,wBAAwB,CA2D1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gCAAgC;IAC/C,8EAA8E;IAC9E,MAAM,CACJ,YAAY,EAAE,gBAAgB,EAAE,GAC/B,OAAO,CAAC,gBAAgB,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC;CACrD;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,gBAAgB,EAAE,EAChC,QAAQ,EAAE,gCAAgC,GACzC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAW7B"}
package/dist/server.js CHANGED
@@ -5,7 +5,7 @@ import { sanitizeConfig } from "@happyvertical/smrt-config";
5
5
  import { TenantScoped, getTenantId, tenantId } from "@happyvertical/smrt-tenancy";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { SmrtJobCollection, backgroundEligible, createHmacDurableJobPayloadSigner, getActiveJobExecutionContext, isRunnerExecutionContext } from "@happyvertical/smrt-jobs";
8
- import { DATA_SURFACE_MAX_REQUEST_BYTES } from "@happyvertical/smrt-ui/data";
8
+ import { DATA_SURFACE_MAX_REQUEST_BYTES } from "@happyvertical/smrt-ui/data-surface";
9
9
  //#region src/server/api-routes.ts
10
10
  function buildRouteMap(manifests) {
11
11
  const routes = /* @__PURE__ */ new Map();
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":["result"],"sources":["../src/server/api-routes.ts","../src/server/config-loader.ts","../src/server/data-surface-actions.ts","../src/server/jobs-data-surface-action-queue.ts","../src/server/serialization.ts","../src/server/sql-data-surface-action-state.ts","../src/server/sveltekit-data-surface-routes.ts"],"sourcesContent":["/**\n * Server-side API route resolution for SMRT agents\n *\n * Reads agent package manifests and builds a route map from resource\n * paths (e.g., 'performers', 'video-contents') to SmrtObject class\n * names and allowed CRUD actions. The catch-all API handler uses this\n * to resolve incoming requests.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport type { PackageManifest } from './manifest-utils.js';\n\n/**\n * Info about a single API route (one SmrtObject with api.include)\n */\nexport interface AgentAPIRouteInfo {\n /** SmrtObject class name (e.g., 'Performer') */\n className: string;\n /** Allowed CRUD actions (e.g., ['list', 'get', 'create', 'update', 'delete']) */\n allowedActions: string[];\n /** Package that owns this resource */\n packageName?: string;\n}\n\n/**\n * Result of resolving a URL path against the route map\n */\nexport interface ResolvedAPIRoute {\n /** The matched route info */\n route: AgentAPIRouteInfo;\n /** Resource ID if path includes one (e.g., 'performers/abc-123') */\n id?: string;\n /** Custom action name if path includes one (e.g., 'performers/abc-123/generate-image') */\n action?: string;\n}\n\n/**\n * Build a route map from loaded package manifests.\n *\n * Iterates all objects in each manifest, and for any object with a\n * `decoratorConfig.api.include` array, registers a route. The route\n * path is derived from `decoratorConfig.api.path` if set, otherwise\n * from the table name with underscores converted to hyphens.\n *\n * @param manifests - Array of parsed package manifest JSON objects\n * @returns Map of resource path -> route info\n *\n * @example\n * ```typescript\n * const manifests = [histrioManifest, praecoManifest];\n * const routes = buildRouteMap(manifests);\n * // routes.get('performers') => { className: 'Performer', allowedActions: ['list', 'get', 'create', 'update', 'delete'] }\n * // routes.get('video-contents') => { className: 'VideoShot', allowedActions: ['list', 'get', 'create', 'update'] }\n * ```\n */\nexport function buildRouteMap(\n manifests: PackageManifest[],\n): Map<string, AgentAPIRouteInfo> {\n const routes = new Map<string, AgentAPIRouteInfo>();\n\n for (const manifest of manifests) {\n const packageName = (manifest as Record<string, unknown>).packageName as\n | string\n | undefined;\n\n for (const obj of Object.values(manifest.objects)) {\n const config = obj.decoratorConfig as Record<string, unknown> | undefined;\n if (!config) continue;\n\n const api = config.api as\n | { include?: string[]; path?: string }\n | undefined;\n if (!api?.include || api.include.length === 0) continue;\n\n // Derive the URL path: explicit api.path, or table name with _ -> -\n const tableName = config.tableName as string | undefined;\n const path =\n api.path || (tableName ? tableName.replace(/_/g, '-') : null);\n if (!path) continue;\n\n routes.set(path, {\n className: obj.className,\n allowedActions: api.include,\n packageName,\n });\n }\n }\n\n return routes;\n}\n\n/**\n * Resolve a URL resource path against a route map.\n *\n * Handles three URL patterns:\n * - `performers` → list/create (no id)\n * - `performers/abc-123` → get/update/delete (with id)\n * - `performers/abc-123/generate-image` → custom action\n *\n * @param urlPath - The resource portion of the URL (after `/api/agents/{agentId}/`)\n * @param routes - Route map from {@link buildRouteMap}\n * @returns Resolved route with optional id/action, or null if no match\n */\nexport function resolveAPIRoute(\n urlPath: string,\n routes: Map<string, AgentAPIRouteInfo>,\n): ResolvedAPIRoute | null {\n // Normalize: strip leading/trailing slashes\n const normalized = urlPath.replace(/^\\/+|\\/+$/g, '');\n if (!normalized) return null;\n\n const segments = normalized.split('/');\n\n // Try 1-segment: \"performers\"\n if (segments.length === 1) {\n const route = routes.get(segments[0]);\n if (route) return { route };\n return null;\n }\n\n // Try 2-segment: \"performers/{id}\"\n if (segments.length === 2) {\n const route = routes.get(segments[0]);\n if (route) return { route, id: segments[1] };\n return null;\n }\n\n // Try 3-segment: \"performers/{id}/{action}\"\n if (segments.length === 3) {\n const route = routes.get(segments[0]);\n if (route) return { route, id: segments[1], action: segments[2] };\n return null;\n }\n\n return null;\n}\n","/**\n * Server-side agent config loading utilities\n *\n * Loads slot configurations from the agent_configs table for a set of agents.\n * Agent-specific table loading (e.g., praeco_sources) stays in the host app.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { AgentConfig } from '../config.js';\n\n/**\n * Load slot configs for multiple agents from the agent_configs table.\n *\n * Returns a nested map: agentId -> slotId -> configData.\n * Agent-specific tables (e.g., praeco_sources, praeco_reports)\n * are NOT loaded here — those stay in the host application.\n *\n * @param agents - Array of agent identifiers (id + agentClass)\n * @param dbOptions - Database options for SmrtCollection.create()\n * @returns Map of agentId -> slotId -> config data\n */\nexport async function loadSlotConfigs(\n agents: Array<{ id: string; agentClass: string }>,\n dbOptions: SmrtClassOptions,\n): Promise<Record<string, Record<string, unknown>>> {\n if (agents.length === 0) {\n return {};\n }\n\n try {\n const configsByAgent = await AgentConfig.forAgents(\n agents.map((agent) => agent.id),\n dbOptions,\n );\n\n const configs: Record<string, Record<string, unknown>> = {};\n for (const [agentId, slotConfigs] of configsByAgent) {\n const agentConfig: Record<string, unknown> = {};\n for (const [slotId, configData] of slotConfigs) {\n agentConfig[slotId] = configData;\n }\n if (Object.keys(agentConfig).length > 0) {\n configs[agentId] = agentConfig;\n }\n }\n\n return configs;\n } catch (error) {\n if (isMissingAgentConfigTableError(error)) {\n return {};\n }\n throw error;\n }\n}\n\nfunction isMissingAgentConfigTableError(error: unknown): boolean {\n const message = String((error as Error)?.message || error || '');\n\n return (\n message.includes(\"Run 'smrt db:migrate'\") ||\n /no such table[:\\s]+agent_configs/i.test(message) ||\n /relation .*agent_configs.*does not exist/i.test(message) ||\n /table .*agent_configs.*doesn'?t exist/i.test(message)\n );\n}\n","/**\n * Principal-bound preview/apply orchestration for data-surface actions.\n *\n * Browser state is treated only as an input hint. Every preview and apply is\n * executed under the bound principal, resolves the surface and selection\n * afresh, and delegates durable work only after authorization and eligibility\n * checks have passed.\n */\nimport { createHash, randomBytes } from 'node:crypto';\nimport {\n createHmacDurableJobPayloadSigner,\n type DurableJobPayloadIntegrity,\n} from '@happyvertical/smrt-jobs';\nimport type {\n DataSurfaceActionDescriptor,\n DataSurfaceActionResult,\n DataSurfaceActionRowOutcome,\n DataSurfaceActionWireRequest,\n DataSurfaceDescriptor,\n DataSurfaceIdentity,\n DataSurfaceJsonObject,\n DataSurfaceJsonValue,\n DataSurfaceRowId,\n DataSurfaceSelectionReference,\n} from '@happyvertical/smrt-types';\nimport {\n type ExecuteAsPrincipalOptions,\n executeAsPrincipal,\n type PrincipalRun,\n} from '../execute-as-principal.js';\n\nexport type DataSurfaceConfirmationPolicy = 'required' | 'none';\nexport type DataSurfaceActionExecution = 'foreground' | 'background';\n\nexport interface DataSurfaceActionEligibility {\n eligible: boolean;\n reason?: string;\n}\n\nexport type DataSurfaceActionPayloadValidation =\n | { valid: true }\n | { valid: false; reason?: string };\n\nexport interface ResolvedDataSurfaceSelection {\n /** Fresh server-side revision of the selected surface/query. */\n revision: number;\n /** Canonical fingerprint of the frozen query represented by the selection. */\n queryFingerprint: string;\n /** Authoritatively resolved row ids. Browser-provided ids are only hints. */\n rowIds: DataSurfaceRowId[];\n}\n\nexport interface DataSurfaceActionInvocation {\n run: PrincipalRun;\n request: DataSurfaceServerActionRequest;\n descriptor: DataSurfaceDescriptor;\n action: DataSurfaceServerActionDefinition;\n selection: ResolvedDataSurfaceSelection;\n}\n\nexport interface DataSurfaceServerActionDefinition {\n descriptor: DataSurfaceActionDescriptor;\n /** Serializable declaration for transport/schema generators; null means no input. */\n inputSchema: DataSurfaceJsonObject | null;\n /** Runtime enforcement for the declared schema; absence is never permissive. */\n validatePayload(\n payload: DataSurfaceJsonValue | undefined,\n ):\n | DataSurfaceActionPayloadValidation\n | Promise<DataSurfaceActionPayloadValidation>;\n /** Explicit for every action, including sensitive/public/destructive ones. */\n confirmation: DataSurfaceConfirmationPolicy;\n execution: DataSurfaceActionExecution;\n /** Fail-closed persona capability checked by PrincipalRun. */\n tool: string;\n /** Explicit RBAC catalog gate, enforced independently of callback convention. */\n operation: {\n id: string;\n collection: Parameters<PrincipalRun['assertOperation']>[0];\n action: string;\n };\n /** Fresh permission/domain authorization check, run for preview and apply. */\n authorize(\n invocation: DataSurfaceActionInvocation,\n ): boolean | Promise<boolean>;\n /** Fresh per-row domain precondition check, repeated at apply time. */\n eligible(\n invocation: DataSurfaceActionInvocation,\n rowId: DataSurfaceRowId,\n ): DataSurfaceActionEligibility | Promise<DataSurfaceActionEligibility>;\n /** Foreground mutation. Background definitions are run by the injected queue. */\n apply(\n invocation: DataSurfaceActionInvocation,\n rowId: DataSurfaceRowId,\n ):\n | undefined\n | DataSurfaceJsonValue\n | Promise<undefined | DataSurfaceJsonValue>;\n}\n\nexport interface ResolvedDataSurfaceActions {\n descriptor: DataSurfaceDescriptor;\n /** Current server-side revision, never trusted from the browser. */\n revision: number;\n actions: Record<string, DataSurfaceServerActionDefinition>;\n}\n\nexport interface DataSurfaceServerActionRequest\n extends DataSurfaceActionWireRequest {}\n\nexport interface DataSurfaceActionContext {\n principal: ExecuteAsPrincipalOptions;\n}\n\nexport interface DataSurfaceDeferredPrincipalReference {\n runAsUserId: string;\n tenantId: string | null;\n actsAsProfileId: string | null;\n onBehalfOfUserId: string | null;\n agentClass?: string;\n}\n\n/** Serializable, versioned payload persisted by a durable background queue. */\nexport interface DataSurfaceBackgroundActionEnvelope {\n version: 1;\n handlerId: string;\n request: DataSurfaceServerActionRequest;\n principal: DataSurfaceDeferredPrincipalReference;\n previewToken?: DataSurfacePreviewTokenRecord;\n /** Server-authenticated binding over every other persisted envelope field. */\n binding: DurableJobPayloadIntegrity;\n}\n\nexport interface DataSurfaceBackgroundActionJob {\n idempotencyKey: string;\n identity: DataSurfaceIdentity;\n actionId: string;\n rowIds: DataSurfaceRowId[];\n envelope: DataSurfaceBackgroundActionEnvelope;\n /**\n * The queue must call this task to perform the work. It re-enters the bound\n * principal and repeats descriptor, authorization, selection, and eligibility\n * checks before any mutation.\n */\n run: () => Promise<DataSurfaceActionResult>;\n}\n\nexport interface DataSurfaceBackgroundQueue {\n enqueue(\n job: DataSurfaceBackgroundActionJob,\n ): Promise<{ jobId: string; details?: DataSurfaceJsonObject }>;\n}\n\nexport interface DataSurfacePreviewTokenRecord {\n expiresAt: number;\n actorUserId: string;\n tenantId: string | null;\n onBehalfOfUserId: string | null;\n actsAsProfileId: string | null;\n agentClass: string | null;\n identityKey: string;\n actionId: string;\n actionFingerprint: string;\n revision: number;\n queryFingerprint: string;\n selectionFingerprint: string;\n resolvedRowsFingerprint: string;\n requestFingerprint: string;\n consumedBy?: string;\n}\n\nexport type DataSurfaceIdempotencyRecord =\n | {\n status: 'reserved';\n requestFingerprint: string;\n ownerToken: string;\n reservedAt: number;\n }\n | {\n status: 'completed';\n requestFingerprint: string;\n result: DataSurfaceActionResult;\n recovery?: DataSurfaceIdempotencyRecoveryEvidence;\n };\n\nexport interface DataSurfaceIdempotencyRecoveryEvidence {\n authorizedBy: string;\n evidence: string;\n reconciledAt: number;\n}\n\nexport interface DataSurfaceIdempotencyReservation {\n requestFingerprint: string;\n ownerToken: string;\n reservedAt: number;\n}\n\nexport interface DataSurfaceActionStateStore {\n putToken(\n token: string,\n record: DataSurfacePreviewTokenRecord,\n ): Promise<void> | void;\n getToken(\n token: string,\n ):\n | Promise<DataSurfacePreviewTokenRecord | undefined>\n | DataSurfacePreviewTokenRecord\n | undefined;\n markTokenConsumed(\n token: string,\n idempotencyKey: string,\n ): Promise<boolean> | boolean;\n /** Atomically consume a preview token and create/read its apply reservation. */\n consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ):\n | Promise<DataSurfaceIdempotencyRecord | undefined>\n | DataSurfaceIdempotencyRecord\n | undefined;\n getIdempotency(\n key: string,\n ):\n | Promise<DataSurfaceIdempotencyRecord | undefined>\n | DataSurfaceIdempotencyRecord\n | undefined;\n /** Atomically create a durable reservation or return the existing record. */\n reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord> | DataSurfaceIdempotencyRecord;\n completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): Promise<boolean> | boolean;\n releaseIdempotency(\n key: string,\n ownerToken: string,\n ): Promise<boolean> | boolean;\n}\n\n/** Explicit single-process/testing store; production callers inject shared state. */\nexport class InMemoryDataSurfaceActionStateStore\n implements DataSurfaceActionStateStore\n{\n private readonly tokens = new Map<string, DataSurfacePreviewTokenRecord>();\n private readonly idempotency = new Map<\n string,\n DataSurfaceIdempotencyRecord\n >();\n\n putToken(token: string, record: DataSurfacePreviewTokenRecord): void {\n this.tokens.set(token, record);\n }\n\n getToken(token: string): DataSurfacePreviewTokenRecord | undefined {\n return this.tokens.get(token);\n }\n\n markTokenConsumed(token: string, idempotencyKey: string): boolean {\n const record = this.tokens.get(token);\n if (!record) return false;\n if (record.consumedBy && record.consumedBy !== idempotencyKey) return false;\n record.consumedBy = idempotencyKey;\n return true;\n }\n\n consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): DataSurfaceIdempotencyRecord | undefined {\n const tokenRecord = this.tokens.get(token);\n if (\n !tokenRecord ||\n (tokenRecord.consumedBy && tokenRecord.consumedBy !== idempotencyKey)\n ) {\n return undefined;\n }\n const existing = this.idempotency.get(scope);\n if (!existing) {\n this.idempotency.set(scope, { status: 'reserved', ...reservation });\n }\n tokenRecord.consumedBy = idempotencyKey;\n return this.idempotency.get(scope);\n }\n\n getIdempotency(key: string): DataSurfaceIdempotencyRecord | undefined {\n return this.idempotency.get(key);\n }\n\n reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): DataSurfaceIdempotencyRecord {\n const existing = this.idempotency.get(key);\n if (existing) return existing;\n const record: DataSurfaceIdempotencyRecord = {\n status: 'reserved',\n ...reservation,\n };\n this.idempotency.set(key, record);\n return record;\n }\n\n completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): boolean {\n const existing = this.idempotency.get(key);\n if (existing?.status !== 'reserved' || existing.ownerToken !== ownerToken)\n return false;\n this.idempotency.set(key, {\n status: 'completed',\n requestFingerprint: existing.requestFingerprint,\n result,\n });\n return true;\n }\n\n releaseIdempotency(key: string, ownerToken: string): boolean {\n const existing = this.idempotency.get(key);\n if (existing?.status !== 'reserved' || existing.ownerToken !== ownerToken)\n return false;\n return this.idempotency.delete(key);\n }\n}\n\nexport interface DataSurfaceActionAdapterOptions {\n resolveSurface(\n run: PrincipalRun,\n identity: DataSurfaceIdentity,\n ): Promise<ResolvedDataSurfaceActions>;\n resolveSelection(\n invocation: Omit<DataSurfaceActionInvocation, 'selection'>,\n selection: DataSurfaceSelectionReference,\n ): Promise<ResolvedDataSurfaceSelection>;\n backgroundQueue?: DataSurfaceBackgroundQueue;\n /** Stable worker registration key required by durable background queues. */\n backgroundHandlerId?: string;\n /** Secret used only in-process to authenticate durable background envelopes. */\n deferredEnvelopeSigningKey?: string | Uint8Array;\n /** Required durable, shared backend in production; memory storage is opt-in. */\n state: DataSurfaceActionStateStore;\n tokenTtlMs?: number;\n now?: () => number;\n createToken?: () => string;\n runAsPrincipal?: typeof executeAsPrincipal;\n /**\n * Re-resolve the complete current binding immediately before deferred work.\n * Background execution fails closed when this seam is absent or returns a\n * binding for a different principal.\n */\n resolveDeferredPrincipal?(\n reference: Readonly<{\n runAsUserId: string;\n tenantId: string | null;\n actsAsProfileId: string | null;\n onBehalfOfUserId: string | null;\n agentClass?: string;\n }>,\n ): ExecuteAsPrincipalOptions | Promise<ExecuteAsPrincipalOptions>;\n idempotencyPollIntervalMs?: number;\n idempotencyWaitTimeoutMs?: number;\n /** Domain-specific request input that must participate in confirmation/idempotency. */\n requestFingerprintExtension?(\n request: DataSurfaceServerActionRequest,\n ): DataSurfaceJsonValue | undefined;\n /** Maps terminal domain failures; return undefined to preserve queue retries. */\n mapError?(\n error: unknown,\n request: DataSurfaceServerActionRequest,\n ): string | undefined;\n}\n\nexport interface DataSurfaceActionAdapter {\n preview(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult>;\n apply(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult>;\n /** Worker entry point; re-resolves current authority before any mutation. */\n executeDeferred(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult>;\n}\n\nconst DEFAULT_TOKEN_TTL_MS = 5 * 60 * 1_000;\nconst MAX_IDENTIFIER_LENGTH = 256;\nconst MAX_JSON_DEPTH = 16;\nconst MAX_JSON_ITEMS = 1_000;\nconst FORBIDDEN_JSON_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction isBoundedJsonValue(\n value: unknown,\n depth = 0,\n seen = new Set<object>(),\n): value is DataSurfaceJsonValue {\n if (value === null) return true;\n if (['string', 'boolean'].includes(typeof value)) return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || depth >= MAX_JSON_DEPTH || seen.has(value))\n return false;\n seen.add(value);\n if (Array.isArray(value)) {\n if (value.length > MAX_JSON_ITEMS) return false;\n return value.every((item) => isBoundedJsonValue(item, depth + 1, seen));\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return false;\n const entries = Object.entries(value);\n if (entries.length > MAX_JSON_ITEMS) return false;\n return entries.every(\n ([key, item]) =>\n !FORBIDDEN_JSON_KEYS.has(key) &&\n isBoundedJsonValue(item, depth + 1, seen),\n );\n}\n\n/** Validates untrusted extension values before they enter canonical hashing. */\nexport function isBoundedDataSurfaceJsonValue(\n value: unknown,\n): value is DataSurfaceJsonValue {\n return isBoundedJsonValue(value);\n}\n\nfunction validIdentifier(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_IDENTIFIER_LENGTH\n );\n}\n\nfunction validSelection(\n selection: unknown,\n): selection is DataSurfaceSelectionReference {\n if (!selection || typeof selection !== 'object') return false;\n const candidate = selection as Record<string, unknown>;\n if (candidate.scope === 'current-page') return true;\n if (candidate.scope === 'all-matching')\n return validIdentifier(candidate.queryFingerprint);\n if (candidate.scope !== 'explicit-ids' || !Array.isArray(candidate.rowIds))\n return false;\n if (candidate.rowIds.length > MAX_JSON_ITEMS) return false;\n return candidate.rowIds.every(\n (rowId) =>\n (typeof rowId === 'string' && rowId.length > 0) ||\n (typeof rowId === 'number' && Number.isFinite(rowId)),\n );\n}\n\nfunction stable(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;\n return `{${Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`)\n .join(',')}}`;\n}\n\nfunction fingerprint(value: unknown): string {\n return createHash('sha256').update(stable(value)).digest('hex');\n}\n\nfunction envelopeBinding(\n envelope: Omit<DataSurfaceBackgroundActionEnvelope, 'binding'>,\n key: string | Uint8Array,\n): DurableJobPayloadIntegrity {\n return createHmacDurableJobPayloadSigner({\n keyId: 'data-surface-envelope-v1',\n key,\n }).sign(envelope);\n}\n\nfunction validSigningKey(key: string | Uint8Array | undefined): boolean {\n return (\n key !== undefined &&\n (typeof key === 'string' ? Buffer.byteLength(key) : key.byteLength) >= 32\n );\n}\n\nfunction bindingMatches(\n envelope: DataSurfaceBackgroundActionEnvelope,\n key: string | Uint8Array,\n): boolean {\n const { binding, ...unsigned } = envelope;\n return createHmacDurableJobPayloadSigner({\n keyId: 'data-surface-envelope-v1',\n key,\n }).verify(unsigned, binding);\n}\n\nfunction identityKey(identity: DataSurfaceIdentity): string {\n return stable(canonicalIdentity(identity));\n}\n\nfunction canonicalIdentity(identity: DataSurfaceIdentity): DataSurfaceIdentity {\n return {\n kind: identity.kind,\n surfaceId: identity.surfaceId,\n ...(identity.subject\n ? {\n subject: {\n type: identity.subject.type,\n id: identity.subject.id,\n },\n }\n : {}),\n };\n}\n\nfunction rowIdKey(rowId: DataSurfaceRowId): string {\n return `${typeof rowId}:${String(rowId)}`;\n}\n\nfunction compareRowIds(\n left: DataSurfaceRowId,\n right: DataSurfaceRowId,\n): number {\n if (typeof left !== typeof right) return typeof left === 'number' ? -1 : 1;\n if (typeof left === 'number' && typeof right === 'number')\n return left - right;\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction canonicalRowIds(\n rowIds: readonly DataSurfaceRowId[],\n): DataSurfaceRowId[] {\n const ids = new Map<string, DataSurfaceRowId>();\n for (const rowId of rowIds) ids.set(rowIdKey(rowId), rowId);\n return [...ids.values()].sort(compareRowIds);\n}\n\nfunction canonicalSelection(\n selection: DataSurfaceSelectionReference,\n): DataSurfaceSelectionReference {\n if (selection.scope !== 'explicit-ids') return selection;\n return { scope: selection.scope, rowIds: canonicalRowIds(selection.rowIds) };\n}\n\nfunction requestFingerprint(\n request: DataSurfaceServerActionRequest,\n extension?: DataSurfaceJsonValue,\n): string {\n return fingerprint({\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n selection: canonicalSelection(request.selection),\n payload: request.payload,\n expectedRevision: request.expectedRevision,\n ...(extension === undefined ? {} : { extension }),\n });\n}\n\nfunction deepFreeze<T>(value: T, seen = new WeakSet<object>()): T {\n if (!value || typeof value !== 'object' || seen.has(value)) return value;\n seen.add(value);\n for (const nested of Object.values(value)) deepFreeze(nested, seen);\n Object.freeze(value);\n return value;\n}\n\nfunction snapshotRequest(\n request: DataSurfaceServerActionRequest,\n): DataSurfaceServerActionRequest {\n return deepFreeze(structuredClone(request));\n}\n\nfunction snapshotActionContext(\n context: DataSurfaceActionContext,\n): DataSurfaceActionContext {\n const principal = {\n ...context.principal.principal,\n ...(context.principal.principal.allowedTools\n ? { allowedTools: [...context.principal.principal.allowedTools] }\n : {}),\n };\n if (principal.allowedTools) Object.freeze(principal.allowedTools);\n Object.freeze(principal);\n const permissions = context.principal.permissions\n ? [...context.principal.permissions]\n : undefined;\n if (permissions) Object.freeze(permissions);\n const auditMetadata = context.principal.auditMetadata\n ? deepFreeze(structuredClone(context.principal.auditMetadata))\n : undefined;\n const principalOptions: ExecuteAsPrincipalOptions = {\n ...context.principal,\n principal,\n ...(permissions ? { permissions } : {}),\n ...(auditMetadata ? { auditMetadata } : {}),\n };\n Object.freeze(principalOptions);\n return Object.freeze({ principal: principalOptions });\n}\n\nfunction actionFingerprint(action: DataSurfaceServerActionDefinition): string {\n return fingerprint({\n descriptor: action.descriptor,\n inputSchema: action.inputSchema,\n confirmation: action.confirmation,\n execution: action.execution,\n tool: action.tool,\n operationId: action.operation.id,\n operationCollection: action.operation.collection,\n operationAction: action.operation.action,\n });\n}\n\nfunction result(\n request: DataSurfaceServerActionRequest,\n ok: boolean,\n reason?: string,\n details?: DataSurfaceJsonObject,\n confirmationToken?: string,\n): DataSurfaceActionResult {\n return {\n version: 1,\n requestId: request.requestId,\n identity: request.identity,\n actionId: request.actionId,\n phase: request.phase,\n ok,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n ...(confirmationToken ? { confirmationToken } : {}),\n };\n}\n\nfunction replayResult(\n request: DataSurfaceServerActionRequest,\n stored: DataSurfaceActionResult,\n): DataSurfaceActionResult {\n // Idempotency keys identify one logical execution, but each transport retry\n // has its own correlation id. Preserve the stored outcome while binding the\n // replay envelope to the request that is receiving it.\n return { ...stored, requestId: request.requestId };\n}\n\nfunction outcomesDetails(\n outcomes: DataSurfaceActionRowOutcome[],\n extra: DataSurfaceJsonObject = {},\n): DataSurfaceJsonObject {\n const accepted = outcomes.filter(\n ({ status }) => status === 'accepted',\n ).length;\n const skipped = outcomes.filter(({ status }) => status === 'skipped').length;\n const failed = outcomes.filter(({ status }) => status === 'failed').length;\n return {\n accepted,\n skipped,\n failed,\n outcomes: outcomes.map(({ metadata, ...outcome }) => ({\n ...(metadata ?? {}),\n ...outcome,\n })),\n ...extra,\n };\n}\n\nfunction validateRequest(\n request: DataSurfaceServerActionRequest,\n phase: 'preview' | 'apply',\n): string | undefined {\n if (!request || typeof request !== 'object') return 'invalid_request';\n if (request.version !== 1 || request.phase !== phase)\n return 'invalid_request';\n if (\n !validIdentifier(request.requestId) ||\n !validIdentifier(request.actionId) ||\n !validIdentifier(request.identity?.surfaceId) ||\n !['table', 'list', 'report', 'custom'].includes(request.identity?.kind) ||\n !validSelection(request.selection) ||\n (request.payload !== undefined && !isBoundedJsonValue(request.payload))\n )\n return 'invalid_request';\n if (\n !Number.isSafeInteger(request.expectedRevision) ||\n request.expectedRevision < 0\n )\n return 'invalid_request';\n if (\n phase === 'apply' &&\n (!validIdentifier(request.idempotencyKey) ||\n (request.confirmationToken !== undefined &&\n !validIdentifier(request.confirmationToken)))\n )\n return 'invalid_request';\n return undefined;\n}\n\n/** Create a transport-neutral, principal-bound data-surface action adapter. */\nexport function createDataSurfaceActionAdapter(\n options: DataSurfaceActionAdapterOptions,\n): DataSurfaceActionAdapter {\n const state = options.state;\n const now = options.now ?? Date.now;\n const createToken =\n options.createToken ?? (() => randomBytes(32).toString('base64url'));\n const tokenTtlMs = options.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;\n const runAsPrincipal = options.runAsPrincipal ?? executeAsPrincipal;\n const idempotencyPollIntervalMs = Math.max(\n 1,\n options.idempotencyPollIntervalMs ?? 10,\n );\n const idempotencyWaitTimeoutMs = Math.max(\n 0,\n options.idempotencyWaitTimeoutMs ?? 5_000,\n );\n const fingerprintRequest = (request: DataSurfaceServerActionRequest) =>\n requestFingerprint(request, options.requestFingerprintExtension?.(request));\n\n async function resolveInvocation(\n request: DataSurfaceServerActionRequest,\n run: PrincipalRun,\n ): Promise<DataSurfaceActionInvocation | DataSurfaceActionResult> {\n const surface = await options.resolveSurface(run, request.identity);\n if (\n identityKey(surface.descriptor.identity) !== identityKey(request.identity)\n ) {\n return result(request, false, 'not_found');\n }\n const action = surface.actions[request.actionId];\n const declared = surface.descriptor.actions.find(\n ({ id }) => id === request.actionId,\n );\n if (\n !action ||\n !declared ||\n action.descriptor.id !== declared.id ||\n Boolean(declared.requiresConfirmation) !==\n (action.confirmation === 'required')\n ) {\n return result(request, false, 'unsupported');\n }\n if (\n !action.tool ||\n !validIdentifier(action.operation?.id) ||\n !validIdentifier(action.operation?.action)\n )\n return result(request, false, 'denied');\n run.assertToolAllowed(action.tool);\n await run.assertOperation(\n action.operation.collection,\n action.operation.action,\n );\n const payloadValidation = await action.validatePayload(request.payload);\n if (!payloadValidation.valid)\n return result(\n request,\n false,\n payloadValidation.reason ?? 'invalid_payload',\n );\n if (\n !declared.selectionScopes.includes(request.selection.scope) ||\n !action.descriptor.selectionScopes.includes(request.selection.scope)\n ) {\n return result(request, false, 'selection_not_supported');\n }\n const base = {\n run,\n request,\n descriptor: surface.descriptor,\n action,\n };\n const resolvedSelection = await options.resolveSelection(\n base,\n canonicalSelection(request.selection),\n );\n const selection = {\n ...resolvedSelection,\n rowIds: canonicalRowIds(resolvedSelection.rowIds),\n };\n const invocation = { ...base, selection };\n if (!(await action.authorize(invocation))) {\n return result(request, false, 'denied');\n }\n if (selection.rowIds.length > surface.descriptor.limits.maxSelectionSize) {\n return result(request, false, 'limit_exceeded');\n }\n return invocation;\n }\n\n async function preview(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult> {\n const invalid = validateRequest(request, 'preview');\n if (invalid) return result(request, false, invalid);\n const boundContext = snapshotActionContext(context);\n return runAsPrincipal(\n {\n ...boundContext.principal,\n action: 'data_surface.action.preview',\n auditMetadata: {\n ...boundContext.principal.auditMetadata,\n surfaceId: request.identity.surfaceId,\n actionId: request.actionId,\n requestId: request.requestId,\n },\n },\n async (run) => {\n const invocation = await resolveInvocation(request, run);\n if ('ok' in invocation) return invocation;\n if (invocation.selection.revision !== request.expectedRevision) {\n return result(request, false, 'stale_revision');\n }\n const outcomes: DataSurfaceActionRowOutcome[] = [];\n for (const rowId of invocation.selection.rowIds) {\n const eligibility = await invocation.action.eligible(\n invocation,\n rowId,\n );\n outcomes.push({\n rowId,\n status: eligibility.eligible ? 'accepted' : 'skipped',\n ...(eligibility.reason ? { reason: eligibility.reason } : {}),\n });\n }\n const confirmationToken = createToken();\n const selectionFingerprint = fingerprint(\n canonicalSelection(request.selection),\n );\n const requestFingerprintValue = fingerprintRequest(request);\n const expiresAt = now() + tokenTtlMs;\n await state.putToken(confirmationToken, {\n expiresAt,\n actorUserId: boundContext.principal.principal.runAsUserId,\n tenantId: boundContext.principal.principal.tenantId,\n onBehalfOfUserId: boundContext.principal.onBehalfOfUserId ?? null,\n actsAsProfileId:\n boundContext.principal.principal.actsAsProfileId ?? null,\n agentClass: boundContext.principal.agentClass ?? null,\n identityKey: identityKey(request.identity),\n actionId: request.actionId,\n actionFingerprint: actionFingerprint(invocation.action),\n revision: invocation.selection.revision,\n queryFingerprint: invocation.selection.queryFingerprint,\n selectionFingerprint,\n resolvedRowsFingerprint: fingerprint(\n canonicalRowIds(invocation.selection.rowIds),\n ),\n requestFingerprint: requestFingerprintValue,\n });\n return result(\n request,\n true,\n undefined,\n outcomesDetails(outcomes, {\n count: invocation.selection.rowIds.length,\n revision: invocation.selection.revision,\n queryFingerprint: invocation.selection.queryFingerprint,\n expiresAt,\n }),\n confirmationToken,\n );\n },\n );\n }\n\n async function executeForeground(\n request: DataSurfaceServerActionRequest,\n invocation: DataSurfaceActionInvocation,\n ): Promise<DataSurfaceActionResult> {\n const outcomes: DataSurfaceActionRowOutcome[] = [];\n for (const rowId of invocation.selection.rowIds) {\n try {\n const eligibility = await invocation.action.eligible(invocation, rowId);\n if (!eligibility.eligible) {\n outcomes.push({\n rowId,\n status: 'skipped',\n ...(eligibility.reason ? { reason: eligibility.reason } : {}),\n });\n continue;\n }\n const applied = await invocation.action.apply(invocation, rowId);\n outcomes.push({\n rowId,\n status: 'accepted',\n ...(applied !== null &&\n typeof applied === 'object' &&\n !Array.isArray(applied)\n ? { metadata: applied }\n : {}),\n });\n } catch (error) {\n outcomes.push({\n rowId,\n status: 'failed',\n reason: options.mapError?.(error, request) ?? 'execution_failed',\n });\n }\n }\n return result(request, true, undefined, outcomesDetails(outcomes));\n }\n\n async function executeBackgroundOnce(\n request: DataSurfaceServerActionRequest,\n token: DataSurfacePreviewTokenRecord | undefined,\n reference: Readonly<DataSurfaceDeferredPrincipalReference>,\n ): Promise<DataSurfaceActionResult> {\n const ownerToken = randomBytes(16).toString('base64url');\n const executionFingerprint = fingerprint({\n kind: 'background-execution',\n request: token?.requestFingerprint ?? fingerprintRequest(request),\n action: token?.actionFingerprint ?? request.actionId,\n });\n const executionScope = fingerprint({\n kind: 'background-execution',\n actorUserId: reference.runAsUserId,\n tenantId: reference.tenantId,\n onBehalfOfUserId: reference.onBehalfOfUserId,\n actsAsProfileId: reference.actsAsProfileId,\n agentClass: reference.agentClass ?? null,\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n idempotencyKey: request.idempotencyKey,\n });\n const maxPolls = Math.max(\n 1,\n Math.ceil(idempotencyWaitTimeoutMs / idempotencyPollIntervalMs),\n );\n for (let poll = 0; poll <= maxPolls; poll += 1) {\n const winner = await state.reserveIdempotency(executionScope, {\n requestFingerprint: executionFingerprint,\n ownerToken,\n reservedAt: now(),\n });\n if (winner.requestFingerprint !== executionFingerprint)\n return result(request, false, 'idempotency_conflict');\n if (winner.status === 'completed')\n return replayResult(request, winner.result);\n if (winner.ownerToken === ownerToken) {\n let refreshed: ExecuteAsPrincipalOptions;\n try {\n // A queued job may run long after the request that created it. The\n // complete persona binding (including the TenantAgent-capped tool\n // allow-list) must therefore be resolved again at execution time.\n const resolved = await options.resolveDeferredPrincipal?.(reference);\n if (\n !resolved ||\n resolved.principal.runAsUserId !== reference.runAsUserId ||\n resolved.principal.tenantId !== reference.tenantId ||\n (resolved.principal.actsAsProfileId ?? null) !==\n reference.actsAsProfileId ||\n (resolved.onBehalfOfUserId ?? null) !==\n reference.onBehalfOfUserId ||\n (resolved.agentClass ?? null) !== (reference.agentClass ?? null) ||\n !Array.isArray(resolved.principal.allowedTools)\n ) {\n throw new Error(\n 'Deferred data-surface action principal binding could not be resolved safely',\n );\n }\n refreshed = resolved;\n } catch (error) {\n const reason = options.mapError?.(error, request);\n if (!reason) {\n // No side effect has started, so a later queue attempt may safely\n // acquire a fresh reservation and re-check current authority.\n await state.releaseIdempotency(executionScope, ownerToken);\n throw error;\n }\n const denied = result(request, false, reason);\n if (\n !(await state.completeIdempotency(\n executionScope,\n ownerToken,\n denied,\n ))\n ) {\n throw new Error('Lost background action idempotency reservation');\n }\n return denied;\n }\n let executed: DataSurfaceActionResult;\n let mutationStarted = false;\n try {\n // Permission snapshots are never carried across the queue boundary;\n // executeAsPrincipal resolves current RBAC/membership immediately.\n const { permissions: _permissions, ...livePrincipal } = refreshed;\n executed = await authorizedApply(\n request,\n { principal: livePrincipal },\n token,\n false,\n () => {\n mutationStarted = true;\n },\n );\n } catch (error) {\n const reason = options.mapError?.(error, request);\n if (!reason) {\n if (!mutationStarted) {\n await state.releaseIdempotency(executionScope, ownerToken);\n }\n // Once mutation starts, effects are unknown; retain the reservation\n // until evidence-based reconciliation rather than retrying blindly.\n throw error;\n }\n executed = result(request, false, reason);\n }\n if (\n !(await state.completeIdempotency(\n executionScope,\n ownerToken,\n executed,\n ))\n ) {\n throw new Error('Lost background action idempotency reservation');\n }\n return executed;\n }\n if (poll < maxPolls) {\n await new Promise<void>((resolve) =>\n setTimeout(resolve, idempotencyPollIntervalMs),\n );\n const current = await state.getIdempotency(executionScope);\n if (current?.status === 'completed')\n return replayResult(request, current.result);\n }\n }\n return result(request, false, 'idempotency_in_progress');\n }\n\n async function authorizedApply(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n token: DataSurfacePreviewTokenRecord | undefined,\n allowBackground: boolean,\n beforeMutation?: () => void,\n ): Promise<DataSurfaceActionResult> {\n const idempotencyKey = request.idempotencyKey;\n if (!idempotencyKey) return result(request, false, 'invalid_request');\n // Capture the immutable binding before any asynchronous authorization.\n // The host may reuse or mutate its request context after enqueue returns.\n const deferredPrincipalReference = Object.freeze({\n runAsUserId: context.principal.principal.runAsUserId,\n tenantId: context.principal.principal.tenantId,\n actsAsProfileId: context.principal.principal.actsAsProfileId ?? null,\n onBehalfOfUserId: context.principal.onBehalfOfUserId ?? null,\n ...(context.principal.agentClass\n ? { agentClass: context.principal.agentClass }\n : {}),\n });\n return runAsPrincipal(\n {\n ...context.principal,\n action: 'data_surface.action.apply',\n auditMetadata: {\n ...context.principal.auditMetadata,\n surfaceId: request.identity.surfaceId,\n actionId: request.actionId,\n requestId: request.requestId,\n idempotencyKey: request.idempotencyKey,\n },\n },\n async (run) => {\n const invocation = await resolveInvocation(request, run);\n if ('ok' in invocation) return invocation;\n if (token) {\n if (\n fingerprintRequest(request) !== token.requestFingerprint ||\n invocation.selection.revision !== token.revision ||\n invocation.selection.revision !== request.expectedRevision ||\n invocation.selection.queryFingerprint !== token.queryFingerprint ||\n fingerprint(canonicalSelection(request.selection)) !==\n token.selectionFingerprint ||\n actionFingerprint(invocation.action) !== token.actionFingerprint ||\n fingerprint(canonicalRowIds(invocation.selection.rowIds)) !==\n token.resolvedRowsFingerprint\n ) {\n return result(request, false, 'stale_preview');\n }\n } else if (invocation.action.confirmation === 'required') {\n return result(request, false, 'confirmation_required');\n } else if (invocation.selection.revision !== request.expectedRevision) {\n return result(request, false, 'stale_revision');\n }\n if (invocation.action.execution === 'background' && allowBackground) {\n if (\n !options.backgroundQueue ||\n !validIdentifier(options.backgroundHandlerId) ||\n !options.resolveDeferredPrincipal ||\n !validSigningKey(options.deferredEnvelopeSigningKey)\n ) {\n return result(request, false, 'background_unavailable');\n }\n const { confirmationToken: _confirmationToken, ...deferredRequest } =\n request;\n const unsignedEnvelope = {\n version: 1,\n handlerId: options.backgroundHandlerId ?? '',\n request: deferredRequest,\n principal: deferredPrincipalReference,\n ...(token ? { previewToken: token } : {}),\n } as const;\n const queued = await options.backgroundQueue.enqueue({\n idempotencyKey,\n identity: request.identity,\n actionId: request.actionId,\n rowIds: invocation.selection.rowIds,\n envelope: {\n ...unsignedEnvelope,\n binding: envelopeBinding(\n unsignedEnvelope,\n options.deferredEnvelopeSigningKey as string | Uint8Array,\n ),\n },\n run: () =>\n executeBackgroundOnce(request, token, deferredPrincipalReference),\n });\n return result(request, true, undefined, {\n accepted: invocation.selection.rowIds.length,\n skipped: 0,\n failed: 0,\n ...(queued.details ?? {}),\n background: true,\n jobId: queued.jobId,\n // A replayed apply has a new transport request id, while the\n // already-queued job still returns the original execution result.\n // Preserve that correlation id across the replay envelope.\n jobRequestId: request.requestId,\n });\n }\n beforeMutation?.();\n return executeForeground(request, invocation);\n },\n );\n }\n\n async function apply(\n input: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult> {\n const invalid = validateRequest(input, 'apply');\n if (invalid) return result(input, false, invalid);\n const request = snapshotRequest(input);\n const boundContext = snapshotActionContext(context);\n const confirmationToken = request.confirmationToken;\n const idempotencyKey = request.idempotencyKey;\n if (!idempotencyKey) return result(request, false, 'invalid_request');\n const actorUserId = boundContext.principal.principal.runAsUserId;\n const tenantId = boundContext.principal.principal.tenantId;\n const onBehalfOfUserId = boundContext.principal.onBehalfOfUserId ?? null;\n const actsAsProfileId =\n boundContext.principal.principal.actsAsProfileId ?? null;\n const agentClass = boundContext.principal.agentClass ?? null;\n const requestFingerprintValue = fingerprintRequest(request);\n const idempotencyScope = fingerprint({\n actorUserId,\n tenantId,\n onBehalfOfUserId,\n actsAsProfileId,\n agentClass,\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n idempotencyKey,\n });\n const prior = await state.getIdempotency(idempotencyScope);\n if (prior && prior.requestFingerprint !== requestFingerprintValue)\n return result(request, false, 'idempotency_conflict');\n // A completed durable result is safe to replay from its actor/tenant-bound\n // idempotency scope even when the one-time confirmation has expired.\n if (prior?.status === 'completed')\n return replayResult(request, prior.result);\n\n let token: DataSurfacePreviewTokenRecord | undefined;\n if (confirmationToken) {\n token = await state.getToken(confirmationToken);\n if (!token || token.expiresAt <= now()) {\n return result(request, false, 'invalid_or_expired_confirmation');\n }\n if (\n token.actorUserId !== actorUserId ||\n token.tenantId !== tenantId ||\n token.onBehalfOfUserId !== onBehalfOfUserId ||\n token.actsAsProfileId !== actsAsProfileId ||\n token.agentClass !== agentClass ||\n token.identityKey !== identityKey(request.identity) ||\n token.actionId !== request.actionId ||\n token.requestFingerprint !== requestFingerprintValue\n ) {\n return result(request, false, 'confirmation_mismatch');\n }\n }\n // Resolve and validate deferred execution before consuming a one-time\n // confirmation or reserving idempotency. Execution re-authorizes below;\n // this preflight only prevents broken worker configuration from burning a\n // retryable preview.\n const backgroundPreflight = await runAsPrincipal(\n {\n ...boundContext.principal,\n action: 'data_surface.action.apply',\n auditMetadata: boundContext.principal.auditMetadata,\n },\n async (run) => {\n const surface = await options.resolveSurface(run, request.identity);\n if (\n identityKey(surface.descriptor.identity) !==\n identityKey(request.identity)\n )\n return undefined;\n const action = surface.actions[request.actionId];\n const declared = surface.descriptor.actions.find(\n ({ id }) => id === request.actionId,\n );\n if (!action || !declared || action.descriptor.id !== declared.id)\n return undefined;\n if (\n action.execution === 'background' &&\n (!options.backgroundQueue ||\n !validIdentifier(options.backgroundHandlerId) ||\n !options.resolveDeferredPrincipal ||\n !validSigningKey(options.deferredEnvelopeSigningKey))\n ) {\n return result(request, false, 'background_unavailable');\n }\n return undefined;\n },\n );\n if (backgroundPreflight) return backgroundPreflight;\n // Ownership is an internal compare-and-set nonce. Keep it independent of\n // the injectable preview-token factory, which tests or callers may make\n // deterministic without weakening concurrent winner selection.\n const ownerToken = randomBytes(16).toString('base64url');\n const reservation = {\n requestFingerprint: requestFingerprintValue,\n ownerToken,\n reservedAt: now(),\n };\n let firstWinner: DataSurfaceIdempotencyRecord | undefined;\n if (confirmationToken) {\n firstWinner = await state.consumeTokenAndReserveIdempotency(\n confirmationToken,\n idempotencyKey,\n idempotencyScope,\n reservation,\n );\n if (!firstWinner) return result(request, false, 'confirmation_replayed');\n }\n const maxPolls = Math.max(\n 1,\n Math.ceil(idempotencyWaitTimeoutMs / idempotencyPollIntervalMs),\n );\n for (let poll = 0; poll <= maxPolls; poll += 1) {\n const winner =\n poll === 0 && firstWinner\n ? firstWinner\n : await state.reserveIdempotency(idempotencyScope, reservation);\n if (winner.requestFingerprint !== requestFingerprintValue)\n return result(request, false, 'idempotency_conflict');\n if (winner.status === 'completed')\n return replayResult(request, winner.result);\n if (winner.ownerToken === ownerToken) {\n let applied: DataSurfaceActionResult;\n try {\n applied = await authorizedApply(request, boundContext, token, true);\n } catch (error) {\n await state.releaseIdempotency(idempotencyScope, ownerToken);\n throw error;\n }\n // A confirmation-required request without a token is a recoverable\n // precondition failure. Do not consume its idempotency key: the caller\n // may preview and retry with the same key.\n if (!applied.ok && applied.reason === 'confirmation_required') {\n await state.releaseIdempotency(idempotencyScope, ownerToken);\n return applied;\n }\n // Once execution returns, never release on a persistence failure: a\n // durable reservation is safer than allowing duplicate side effects.\n if (\n !(await state.completeIdempotency(\n idempotencyScope,\n ownerToken,\n applied,\n ))\n ) {\n throw new Error('Lost data-surface idempotency reservation');\n }\n return applied;\n }\n if (poll < maxPolls) {\n await new Promise<void>((resolve) =>\n setTimeout(resolve, idempotencyPollIntervalMs),\n );\n const current = await state.getIdempotency(idempotencyScope);\n if (current?.status === 'completed')\n return replayResult(request, current.result);\n }\n }\n return result(request, false, 'idempotency_in_progress');\n }\n\n async function executeDeferred(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult> {\n if (\n !validSigningKey(options.deferredEnvelopeSigningKey) ||\n !bindingMatches(\n envelope,\n options.deferredEnvelopeSigningKey as string | Uint8Array,\n )\n ) {\n throw new Error('Invalid durable data-surface action envelope binding');\n }\n const principal = envelope.principal;\n if (\n envelope.version !== 1 ||\n !options.backgroundHandlerId ||\n envelope.handlerId !== options.backgroundHandlerId ||\n !principal ||\n !validIdentifier(principal.runAsUserId) ||\n (principal.tenantId !== null && !validIdentifier(principal.tenantId)) ||\n (principal.actsAsProfileId !== null &&\n !validIdentifier(principal.actsAsProfileId)) ||\n (principal.onBehalfOfUserId !== null &&\n !validIdentifier(principal.onBehalfOfUserId)) ||\n (principal.agentClass !== undefined &&\n !validIdentifier(principal.agentClass))\n ) {\n return result(envelope.request, false, 'invalid_request');\n }\n const invalid = validateRequest(envelope.request, 'apply');\n if (invalid) return result(envelope.request, false, invalid);\n return executeBackgroundOnce(\n snapshotRequest(envelope.request),\n envelope.previewToken,\n Object.freeze({ ...envelope.principal }),\n );\n }\n\n return { preview, apply, executeDeferred };\n}\n","import {\n field,\n ObjectRegistry,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n backgroundEligible,\n getActiveJobExecutionContext,\n isRunnerExecutionContext,\n type JobExecutionContext,\n type SmrtJob,\n SmrtJobCollection,\n} from '@happyvertical/smrt-jobs';\nimport {\n getTenantId,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport type {\n DataSurfaceActionResult,\n DataSurfaceJsonObject,\n} from '@happyvertical/smrt-types';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type {\n DataSurfaceBackgroundActionEnvelope,\n DataSurfaceBackgroundActionJob,\n DataSurfaceBackgroundQueue,\n} from './data-surface-actions.js';\n\nconst handlers = new Map<\n string,\n (\n envelope: DataSurfaceBackgroundActionEnvelope,\n ) => Promise<DataSurfaceActionResult>\n>();\n\nexport interface DataSurfaceActionJobArgs {\n version: 1;\n envelope: DataSurfaceBackgroundActionEnvelope;\n}\n\nexport interface JobsDataSurfaceBackgroundQueueOptions {\n db: DatabaseInterface;\n handlerId: string;\n execute(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult>;\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_data_surface_action_tasks',\n api: false,\n cli: false,\n mcp: false,\n})\nexport class SmrtDataSurfaceActionTask extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'json', required: true })\n args: DataSurfaceActionJobArgs = {\n version: 1,\n envelope: {} as DataSurfaceBackgroundActionEnvelope,\n };\n\n @backgroundEligible()\n async run(\n args: DataSurfaceActionJobArgs = this.args,\n context?: JobExecutionContext,\n ): Promise<DataSurfaceActionResult> {\n const envelope = args?.envelope;\n const executionContext = getActiveJobExecutionContext() ?? context;\n if (executionContext && !isRunnerExecutionContext(executionContext)) {\n throw new Error('Invalid durable data-surface action job context');\n }\n // A runner context owns scope even when the queued job is global. Persisted\n // instance configuration must never replace that explicit null.\n let jobTenantId: string | null;\n if (executionContext) {\n const runnerTenantId = executionContext.job.tenantId;\n if (runnerTenantId === null) {\n jobTenantId = null;\n } else if (\n typeof runnerTenantId === 'string' &&\n runnerTenantId.length > 0\n ) {\n jobTenantId = runnerTenantId;\n } else {\n throw new Error('Invalid durable data-surface action job tenant');\n }\n } else {\n jobTenantId = this.tenantId ?? getTenantId() ?? null;\n }\n if (\n args?.version !== 1 ||\n envelope?.version !== 1 ||\n typeof envelope.handlerId !== 'string' ||\n envelope.handlerId.length === 0 ||\n envelope.principal?.tenantId !== jobTenantId\n ) {\n throw new Error('Invalid durable data-surface action envelope');\n }\n const handler = handlers.get(envelope.handlerId);\n if (!handler) {\n throw new Error(\n `No data-surface action handler registered for ${envelope.handlerId}`,\n );\n }\n const result = await handler(envelope);\n if (!result.ok && result.reason === 'idempotency_in_progress') {\n throw new Error('Data-surface action outcome requires reconciliation');\n }\n return result;\n }\n}\n\n/**\n * Register the host handler used by workers after process restart.\n * The returned disposer only removes the same registration.\n */\nexport function registerDataSurfaceBackgroundActionHandler(\n handlerId: string,\n execute: (\n envelope: DataSurfaceBackgroundActionEnvelope,\n ) => Promise<DataSurfaceActionResult>,\n): () => void {\n if (!handlerId || handlerId.length > 256) {\n throw new Error(\n 'Data-surface action handlerId must contain 1-256 characters',\n );\n }\n const existing = handlers.get(handlerId);\n if (existing && existing !== execute) {\n throw new Error(\n `Data-surface action handler already registered: ${handlerId}`,\n );\n }\n handlers.set(handlerId, execute);\n return () => {\n if (handlers.get(handlerId) === execute) handlers.delete(handlerId);\n };\n}\n\nexport function createJobsDataSurfaceBackgroundQueue(\n options: JobsDataSurfaceBackgroundQueueOptions,\n): DataSurfaceBackgroundQueue & { unregister(): void } {\n const unregister = registerDataSurfaceBackgroundActionHandler(\n options.handlerId,\n options.execute,\n );\n return {\n unregister,\n async enqueue(job: DataSurfaceBackgroundActionJob) {\n if (job.envelope.handlerId !== options.handlerId) {\n throw new Error('Data-surface action envelope handler mismatch');\n }\n const persisted = await enqueueDataSurfaceActionJob(\n options,\n job.envelope,\n );\n if (!persisted.id)\n throw new Error('Durable data-surface action job has no ID');\n return {\n jobId: persisted.id,\n details: { queue: persisted.queue } as DataSurfaceJsonObject,\n };\n },\n };\n}\n\nasync function enqueueDataSurfaceActionJob(\n options: JobsDataSurfaceBackgroundQueueOptions,\n envelope: DataSurfaceBackgroundActionEnvelope,\n): Promise<SmrtJob> {\n await ObjectRegistry.ensureManifestLoaded('SmrtJob');\n const jobs = await SmrtJobCollection.create({ db: options.db });\n const registered =\n ObjectRegistry.getClassByConstructor(SmrtDataSurfaceActionTask) ??\n ObjectRegistry.getClass('SmrtDataSurfaceActionTask');\n const objectType =\n registered?.qualifiedName ??\n registered?.name ??\n SmrtDataSurfaceActionTask.name;\n return jobs.enqueueJob(\n {\n tenantId: envelope.principal.tenantId,\n queue: options.queue ?? 'data-surface-actions',\n objectType,\n objectId: null,\n method: 'run',\n args: { version: 1, envelope },\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 300_000,\n maxAttempts: options.maxAttempts ?? 3,\n },\n { tenantJobCap: options.tenantJobCap },\n );\n}\n","/**\n * Serialization utilities for resolved agents\n *\n * Converts ResolvedAgentAvailability (database + manifest data) into\n * a JSON-safe shape suitable for passing to client components.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport { sanitizeConfig } from '@happyvertical/smrt-config';\nimport type { ResolvedAgentAvailability } from '../tenant-agent.js';\nimport type { AgentAdminRoute, AgentUISlots } from '../ui.js';\n\n/**\n * Serialized agent data for passing to client components.\n *\n * Includes manifest-derived fields (icon, permissions, slots)\n * alongside resolution metadata (source, sourceTenantId).\n */\nexport interface SerializedAgent {\n /** Agent instance ID, or a synthetic key if no instance exists */\n id: string;\n /** Human-readable name from manifest */\n name?: string;\n /** Human-readable agent class name (e.g., 'Praeco') */\n agentClass: string;\n /** Canonical agent type (qualified name when available) */\n agentType: string;\n /** STI type discriminator (same as agentType) */\n _meta_type?: string;\n /** UI slot definitions from manifest */\n slots?: AgentUISlots;\n /** Admin route declarations from manifest */\n adminRoutes?: AgentAdminRoute[];\n /** How this agent was resolved for the tenant */\n source?: 'explicit' | 'inherited';\n /** Which tenant the binding came from */\n sourceTenantId?: string;\n /** Merged permissions from manifest + tenant overrides */\n permissions?: Record<string, boolean>;\n /** Agent icon from manifest */\n icon?: string;\n /**\n * Tenant-level config overrides, **secret-sanitized** for client transport.\n *\n * SECURITY (#1553, follow-up to #1552): the raw `TenantAgent.config` is the\n * tenant's own override blob and is `@field({ sensitive: true })` (stripped\n * from the generated CRUD api/mcp surfaces). This hand-written admin\n * serialization runs it through `sanitizeConfig()` from\n * `@happyvertical/smrt-config` before it leaves the server, so secret-shaped\n * keys (apiKey/token/password/…) are dropped and secret-shaped values\n * (`sk-…`, `AKIA…`, `Bearer …`, URL credentials, PEM blocks) are masked —\n * non-secret config still reaches the authorized admin UI for display.\n *\n * This is **display-only**: do not edit-round-trip it back to the server\n * (a masked value would overwrite the real secret). Best practice remains to\n * reference secrets by id via `@happyvertical/smrt-secrets` so only an opaque\n * handle is ever stored in tenant config.\n */\n config?: Record<string, unknown>;\n}\n\n/**\n * Convert a ResolvedAgentAvailability to a serializable shape for the UI.\n *\n * @param resolved - Output from TenantAgentCollection.resolveForTenant()\n * @returns Serialized agent data safe for JSON transport\n */\nexport function serializeResolvedAgent(\n resolved: ResolvedAgentAvailability,\n): SerializedAgent {\n const manifest = resolved.manifest;\n\n return {\n id: resolved.agentId || `${resolved.sourceTenantId}:${resolved.agentType}`,\n name: manifest?.name || resolved.agentClass,\n agentClass: resolved.agentClass,\n agentType: resolved.agentType,\n _meta_type: resolved.agentType,\n slots: manifest?.uiSlots as AgentUISlots | undefined,\n adminRoutes: manifest?.adminRoutes as AgentAdminRoute[] | undefined,\n source: resolved.source,\n sourceTenantId: resolved.sourceTenantId,\n permissions: resolved.permissions,\n icon: manifest?.icon,\n // Secret-sanitize before the blob crosses into the client payload (#1553).\n config: sanitizeConfig(resolved.config) as SerializedAgent['config'],\n };\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { DataSurfaceActionResult } from '@happyvertical/smrt-types';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type {\n DataSurfaceActionStateStore,\n DataSurfaceIdempotencyRecord,\n DataSurfaceIdempotencyRecoveryEvidence,\n DataSurfaceIdempotencyReservation,\n DataSurfacePreviewTokenRecord,\n} from './data-surface-actions.js';\n\nconst TOKEN_TABLE = '_smrt_data_surface_action_tokens';\nconst IDEMPOTENCY_TABLE = '_smrt_data_surface_action_idempotency';\n\nconst INTERNAL_SURFACE = {\n api: false,\n cli: false,\n mcp: false,\n} as const;\n\n@smrt({ tableName: '_smrt_data_surface_action_tokens', ...INTERNAL_SURFACE })\nexport class DataSurfaceActionTokenState extends SmrtObject {\n @field({ type: 'text', required: true, unique: true })\n tokenHash: string = '';\n\n @field({ type: 'json', required: true })\n record: DataSurfacePreviewTokenRecord = emptyTokenRecord();\n\n @field({ type: 'text', nullable: true })\n consumedBy: string | null = null;\n}\n\n@smrt({\n tableName: '_smrt_data_surface_action_idempotency',\n ...INTERNAL_SURFACE,\n})\nexport class DataSurfaceActionIdempotencyState extends SmrtObject {\n @field({ type: 'text', required: true, unique: true })\n keyHash: string = '';\n\n @field({ type: 'text', required: true })\n status: 'reserved' | 'completed' = 'reserved';\n\n @field({ type: 'text', required: true })\n requestFingerprint: string = '';\n\n @field({ type: 'text', nullable: true })\n ownerHash: string | null = null;\n\n @field({ type: 'text', nullable: true })\n reservedAt: string | null = null;\n\n @field({ type: 'json', nullable: true })\n result: DataSurfaceActionResult | null = null;\n\n @field({ type: 'json', nullable: true })\n recovery: DataSurfaceIdempotencyRecoveryEvidence | null = null;\n}\n\nexport interface DataSurfaceIdempotencyRecoveryRequest {\n requestFingerprint: string;\n reservedAt: number;\n result: DataSurfaceActionResult;\n authorizedBy: string;\n evidence: string;\n}\n\nexport interface SqlDataSurfaceActionStateStoreOptions {\n db: DatabaseInterface;\n now?: () => number;\n authorizeRecovery?: (\n request: Readonly<DataSurfaceIdempotencyRecoveryRequest>,\n ) => boolean | Promise<boolean>;\n}\n\nexport class DataSurfaceActionStateCorruptionError extends Error {\n constructor(table: string) {\n super(`Malformed durable data-surface action state in ${table}`);\n this.name = 'DataSurfaceActionStateCorruptionError';\n }\n}\n\n/**\n * SQL-backed action state shared by server processes.\n *\n * Preview tokens and owner nonces are stored only as hashes. Runtime schema\n * creation remains the application's normal SMRT migration responsibility.\n * An orphaned reservation is never expired or released automatically: a host\n * may only reconcile it to a concrete terminal result after its live authority\n * callback accepts immutable evidence for the exact reservation timestamp.\n */\nexport class SqlDataSurfaceActionStateStore\n implements DataSurfaceActionStateStore\n{\n private readonly db: DatabaseInterface;\n private readonly now: () => number;\n private readonly authorizeRecovery?: SqlDataSurfaceActionStateStoreOptions['authorizeRecovery'];\n\n constructor(options: SqlDataSurfaceActionStateStoreOptions) {\n this.db = options.db;\n this.now = options.now ?? Date.now;\n this.authorizeRecovery = options.authorizeRecovery;\n }\n\n async putToken(\n token: string,\n record: DataSurfacePreviewTokenRecord,\n ): Promise<void> {\n const timestamp = new Date(this.now()).toISOString();\n await this.db.query(\n `INSERT INTO ${TOKEN_TABLE}\n (id, slug, context, created_at, updated_at, token_hash, record, consumed_by)\n VALUES (?, ?, '', ?, ?, ?, ?, NULL)\n ON CONFLICT(token_hash) DO NOTHING`,\n randomUUID(),\n `action-token-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(token),\n JSON.stringify(record),\n );\n }\n\n async getToken(\n token: string,\n ): Promise<DataSurfacePreviewTokenRecord | undefined> {\n const found = await this.db.query(\n `SELECT record, consumed_by FROM ${TOKEN_TABLE} WHERE token_hash = ? LIMIT 1`,\n secretHash(token),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n if (!row) return undefined;\n const record = tokenRecord(parseObject(row.record, TOKEN_TABLE));\n return {\n ...record,\n ...(typeof row.consumed_by === 'string'\n ? { consumedBy: row.consumed_by }\n : {}),\n };\n }\n\n async markTokenConsumed(\n token: string,\n idempotencyKey: string,\n ): Promise<boolean> {\n const updated = await this.db.query(\n `UPDATE ${TOKEN_TABLE}\n SET consumed_by = ?, updated_at = ?\n WHERE token_hash = ? AND consumed_by IS NULL\n RETURNING token_hash`,\n secretHash(idempotencyKey),\n new Date(this.now()).toISOString(),\n secretHash(token),\n );\n return updated.rows.length === 1;\n }\n\n async consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const transaction = this.db.transaction;\n if (!transaction) {\n throw new Error(\n 'Durable data-surface action state requires database transactions',\n );\n }\n return (await transaction.call(this.db, async (tx) => {\n const timestamp = new Date(this.now()).toISOString();\n const consumed = await tx.query(\n `UPDATE ${TOKEN_TABLE}\n SET consumed_by = ?, updated_at = ?\n WHERE token_hash = ?\n AND (consumed_by IS NULL OR consumed_by = ?)\n RETURNING token_hash`,\n secretHash(idempotencyKey),\n timestamp,\n secretHash(token),\n secretHash(idempotencyKey),\n );\n if (consumed.rows.length !== 1) return undefined;\n await tx.query(\n `INSERT INTO ${IDEMPOTENCY_TABLE}\n (id, slug, context, created_at, updated_at, key_hash, status,\n request_fingerprint, owner_hash, reserved_at, result, recovery)\n VALUES (?, ?, '', ?, ?, ?, 'reserved', ?, ?, ?, NULL, NULL)\n ON CONFLICT(key_hash) DO NOTHING`,\n randomUUID(),\n `action-idempotency-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(scope),\n reservation.requestFingerprint,\n secretHash(reservation.ownerToken),\n String(reservation.reservedAt),\n );\n const current = await this.getIdempotencyWithOwner(\n scope,\n reservation.ownerToken,\n tx,\n );\n if (!current) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return current;\n })) as DataSurfaceIdempotencyRecord | undefined;\n }\n\n async getIdempotency(\n key: string,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const found = await this.db.query(\n `SELECT status, request_fingerprint, owner_hash, reserved_at, result, recovery\n FROM ${IDEMPOTENCY_TABLE} WHERE key_hash = ? LIMIT 1`,\n secretHash(key),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n return row ? idempotencyRecord(row) : undefined;\n }\n\n async reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord> {\n const timestamp = new Date(this.now()).toISOString();\n await this.db.query(\n `INSERT INTO ${IDEMPOTENCY_TABLE}\n (id, slug, context, created_at, updated_at, key_hash, status,\n request_fingerprint, owner_hash, reserved_at, result, recovery)\n VALUES (?, ?, '', ?, ?, ?, 'reserved', ?, ?, ?, NULL, NULL)\n ON CONFLICT(key_hash) DO NOTHING`,\n randomUUID(),\n `action-idempotency-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(key),\n reservation.requestFingerprint,\n secretHash(reservation.ownerToken),\n String(reservation.reservedAt),\n );\n const current = await this.getIdempotencyWithOwner(\n key,\n reservation.ownerToken,\n );\n if (!current)\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n return current;\n }\n\n async completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): Promise<boolean> {\n const updated = await this.db.query(\n `UPDATE ${IDEMPOTENCY_TABLE}\n SET status = 'completed', result = ?, owner_hash = NULL,\n reserved_at = NULL, updated_at = ?\n WHERE key_hash = ? AND status = 'reserved' AND owner_hash = ?\n RETURNING key_hash`,\n JSON.stringify(result),\n new Date(this.now()).toISOString(),\n secretHash(key),\n secretHash(ownerToken),\n );\n return updated.rows.length === 1;\n }\n\n async releaseIdempotency(key: string, ownerToken: string): Promise<boolean> {\n const removed = await this.db.query(\n `DELETE FROM ${IDEMPOTENCY_TABLE}\n WHERE key_hash = ? AND status = 'reserved' AND owner_hash = ?\n RETURNING key_hash`,\n secretHash(key),\n secretHash(ownerToken),\n );\n return removed.rows.length === 1;\n }\n\n async reconcileIdempotency(\n key: string,\n request: DataSurfaceIdempotencyRecoveryRequest,\n ): Promise<boolean> {\n if (\n !request.requestFingerprint ||\n !Number.isSafeInteger(request.reservedAt) ||\n request.reservedAt < 0 ||\n !request.authorizedBy ||\n request.authorizedBy.length > 256 ||\n !request.evidence ||\n request.evidence.length > 2_048\n ) {\n return false;\n }\n if (\n !this.authorizeRecovery ||\n !(await this.authorizeRecovery(Object.freeze({ ...request })))\n ) {\n return false;\n }\n const recovery: DataSurfaceIdempotencyRecoveryEvidence = {\n authorizedBy: request.authorizedBy,\n evidence: request.evidence,\n reconciledAt: this.now(),\n };\n const updated = await this.db.query(\n `UPDATE ${IDEMPOTENCY_TABLE}\n SET status = 'completed', result = ?, recovery = ?, owner_hash = NULL,\n reserved_at = NULL, updated_at = ?\n WHERE key_hash = ? AND status = 'reserved'\n AND request_fingerprint = ? AND reserved_at = ?\n RETURNING key_hash`,\n JSON.stringify(request.result),\n JSON.stringify(recovery),\n new Date(recovery.reconciledAt).toISOString(),\n secretHash(key),\n request.requestFingerprint,\n String(request.reservedAt),\n );\n return updated.rows.length === 1;\n }\n\n private async getIdempotencyWithOwner(\n key: string,\n ownerToken: string,\n db: DatabaseInterface = this.db,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const found = await db.query(\n `SELECT status, request_fingerprint, owner_hash, reserved_at, result, recovery\n FROM ${IDEMPOTENCY_TABLE} WHERE key_hash = ? LIMIT 1`,\n secretHash(key),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n if (!row) return undefined;\n const record = idempotencyRecord(row);\n if (\n record.status === 'reserved' &&\n row.owner_hash === secretHash(ownerToken)\n ) {\n return { ...record, ownerToken };\n }\n return record;\n }\n}\n\nexport function createSqlDataSurfaceActionStateStore(\n options: SqlDataSurfaceActionStateStoreOptions,\n): SqlDataSurfaceActionStateStore {\n return new SqlDataSurfaceActionStateStore(options);\n}\n\nfunction secretHash(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction parseObject(value: unknown, table: string): Record<string, unknown> {\n let parsed = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value) as unknown;\n } catch {\n throw new DataSurfaceActionStateCorruptionError(table);\n }\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new DataSurfaceActionStateCorruptionError(table);\n }\n return parsed as Record<string, unknown>;\n}\n\nfunction idempotencyRecord(\n row: Record<string, unknown>,\n): DataSurfaceIdempotencyRecord {\n const requestFingerprint = row.request_fingerprint;\n if (typeof requestFingerprint !== 'string') {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n if (row.status === 'reserved') {\n const reservedAt = Number(row.reserved_at);\n if (\n typeof row.owner_hash !== 'string' ||\n !Number.isSafeInteger(reservedAt)\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return {\n status: 'reserved',\n requestFingerprint,\n ownerToken: '',\n reservedAt,\n };\n }\n if (row.status !== 'completed') {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n const result = actionResult(parseObject(row.result, IDEMPOTENCY_TABLE));\n const recovery =\n row.recovery == null\n ? undefined\n : recoveryEvidence(parseObject(row.recovery, IDEMPOTENCY_TABLE));\n return {\n status: 'completed',\n requestFingerprint,\n result,\n ...(recovery ? { recovery } : {}),\n };\n}\n\nfunction tokenRecord(\n value: Record<string, unknown>,\n): DataSurfacePreviewTokenRecord {\n const stringKeys = [\n 'actorUserId',\n 'identityKey',\n 'actionId',\n 'actionFingerprint',\n 'queryFingerprint',\n 'selectionFingerprint',\n 'resolvedRowsFingerprint',\n 'requestFingerprint',\n ] as const;\n if (\n !Number.isSafeInteger(value.expiresAt) ||\n !Number.isSafeInteger(value.revision) ||\n stringKeys.some((key) => typeof value[key] !== 'string') ||\n !['tenantId', 'onBehalfOfUserId', 'actsAsProfileId', 'agentClass'].every(\n (key) => value[key] === null || typeof value[key] === 'string',\n )\n ) {\n throw new DataSurfaceActionStateCorruptionError(TOKEN_TABLE);\n }\n return value as unknown as DataSurfacePreviewTokenRecord;\n}\n\nfunction actionResult(value: Record<string, unknown>): DataSurfaceActionResult {\n if (\n value.version !== 1 ||\n typeof value.requestId !== 'string' ||\n typeof value.actionId !== 'string' ||\n value.phase !== 'apply' ||\n typeof value.ok !== 'boolean' ||\n !value.identity ||\n typeof value.identity !== 'object' ||\n Array.isArray(value.identity) ||\n (value.reason !== undefined && typeof value.reason !== 'string')\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return value as unknown as DataSurfaceActionResult;\n}\n\nfunction recoveryEvidence(\n value: Record<string, unknown>,\n): DataSurfaceIdempotencyRecoveryEvidence {\n if (\n typeof value.authorizedBy !== 'string' ||\n typeof value.evidence !== 'string' ||\n !Number.isSafeInteger(value.reconciledAt)\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return value as unknown as DataSurfaceIdempotencyRecoveryEvidence;\n}\n\nfunction emptyTokenRecord(): DataSurfacePreviewTokenRecord {\n return {\n expiresAt: 0,\n actorUserId: '',\n tenantId: null,\n onBehalfOfUserId: null,\n actsAsProfileId: null,\n agentClass: null,\n identityKey: '',\n actionId: '',\n actionFingerprint: '',\n revision: 0,\n queryFingerprint: '',\n selectionFingerprint: '',\n resolvedRowsFingerprint: '',\n requestFingerprint: '',\n };\n}\n","/**\n * SvelteKit `+server.ts` wiring for the data-surface action adapter (#2907).\n *\n * `createDataSurfaceActionAdapter()` and `createJobsDataSurfaceBackgroundQueue()`\n * already carry every principal, idempotency, and durable-queue rule. This\n * module only adapts a plain Web `Request`/`Response` pair (which is what a\n * SvelteKit `RequestHandler` receives/returns) to that adapter, so an app\n * never hand-rolls principal resolution, body parsing, or refusal-to-status\n * mapping per route. It has no `@sveltejs/kit` import and no SvelteKit\n * dependency: any framework whose route handler receives a standard\n * `Request` and returns a `Response` can use it the same way.\n */\n\nimport type {\n DataSurfaceActionResult,\n DataSurfaceRowId,\n} from '@happyvertical/smrt-types';\nimport { DATA_SURFACE_MAX_REQUEST_BYTES } from '@happyvertical/smrt-ui/data';\nimport type { ExecuteAsPrincipalOptions } from '../execute-as-principal.js';\nimport type {\n DataSurfaceActionAdapter,\n DataSurfaceServerActionRequest,\n} from './data-surface-actions.js';\n\n/** Resolves the bound principal for one incoming request. Throw to refuse. */\nexport type DataSurfaceRoutePrincipalResolver = (\n request: Request,\n) => ExecuteAsPrincipalOptions | Promise<ExecuteAsPrincipalOptions>;\n\nexport interface DataSurfaceRouteHandlerOptions {\n adapter: DataSurfaceActionAdapter;\n resolvePrincipal: DataSurfaceRoutePrincipalResolver;\n /** Called when `resolvePrincipal` throws; defaults to logging nothing. */\n onAuthError?(error: unknown, request: Request): void;\n /** Overrides the shared `DATA_SURFACE_MAX_REQUEST_BYTES` transport ceiling. */\n maxRequestBytes?: number;\n}\n\nexport interface DataSurfaceRouteHandlers {\n /** Wire as `export const POST: RequestHandler = ({ request }) => handlers.preview(request)`. */\n preview(request: Request): Promise<Response>;\n apply(request: Request): Promise<Response>;\n}\n\n/**\n * Refusal reasons that keep the confirm/apply loop alive for the caller\n * (bad or stale client state) map to 4xx; anything the adapter did not\n * recognize as a terminal client mistake maps to 422 so it is distinguishable\n * from a malformed request (400) or a hard authorization denial (403). A\n * `Map` (not a plain object) so an attacker-chosen reason like `constructor`\n * or `toString` can never resolve to an inherited function instead of\n * `undefined`.\n */\nconst REASON_STATUS: ReadonlyMap<string, number> = new Map([\n ['invalid_request', 400],\n ['not_found', 404],\n ['denied', 403],\n ['unsupported', 404],\n ['selection_not_supported', 400],\n ['limit_exceeded', 413],\n ['stale_revision', 409],\n ['stale_preview', 409],\n ['confirmation_required', 409],\n ['confirmation_mismatch', 409],\n ['confirmation_replayed', 409],\n ['invalid_or_expired_confirmation', 409],\n ['idempotency_conflict', 409],\n ['idempotency_in_progress', 202],\n ['background_unavailable', 503],\n]);\n\nfunction statusForResult(result: DataSurfaceActionResult): number {\n if (result.ok) return 200;\n return REASON_STATUS.get(result.reason ?? '') ?? 422;\n}\n\n/**\n * An error surfaced by `PrincipalRun.assertToolAllowed()` /\n * `assertOperation()` (e.g. `PrincipalToolNotAllowedError`,\n * `OperationPermissionError`) carries an explicit HTTP `status` — both are\n * already authored as authorization refusals, not unexpected failures.\n * Anything else is rethrown so it still surfaces as a real 500, not a\n * silently swallowed bug.\n */\nfunction authorizationErrorStatus(error: unknown): number | undefined {\n if (!(error instanceof Error)) return undefined;\n const status = (error as Error & { status?: unknown }).status;\n return typeof status === 'number' && status >= 400 && status < 500\n ? status\n : undefined;\n}\n\nfunction jsonResponse(\n body: DataSurfaceActionResult | { error: string },\n status: number,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n\ntype BoundedBodyResult =\n | { ok: true; text: string }\n | { ok: false; reason: 'payload_too_large' | 'invalid_request' };\n\n/** Cheap, buffer-free rejection of a declared-oversized body via its header. */\nfunction declaredContentLengthExceeds(\n request: Request,\n maxBytes: number,\n): boolean {\n const declared = request.headers.get('content-length');\n if (declared === null) return false;\n const bytes = Number(declared);\n return !Number.isFinite(bytes) || bytes > maxBytes;\n}\n\n/**\n * Read the request body without ever buffering more than `maxBytes`: a\n * chunked or otherwise mis-declared body is capped as it streams in, not\n * only via the (spoofable) `content-length` header.\n */\nasync function readBoundedBody(\n request: Request,\n maxBytes: number,\n): Promise<BoundedBodyResult> {\n const stream = request.body;\n if (!stream) return { ok: true, text: await request.text() };\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n let step: { done: boolean; value?: Uint8Array };\n try {\n step = await reader.read();\n } catch {\n return { ok: false, reason: 'invalid_request' };\n }\n if (step.done || !step.value) break;\n total += step.value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined);\n return { ok: false, reason: 'payload_too_large' };\n }\n chunks.push(step.value);\n }\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder('utf-8').decode(merged) };\n}\n\nfunction parseJson(text: string): DataSurfaceServerActionRequest | undefined {\n try {\n const parsed = JSON.parse(text);\n return parsed && typeof parsed === 'object' ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build the preview/apply `Request -> Response` pair for one action surface.\n * Principal/tenant resolution, idempotency, and durable queueing are entirely\n * owned by `options.adapter` (from `createDataSurfaceActionAdapter`); this\n * helper only bridges transport. Ordered deliberately: the request-byte cap\n * is enforced from the `content-length` header before anything else runs\n * (no buffering), then the principal is resolved (refusing before the body\n * is ever parsed), then the body is read under the same cap as it streams,\n * and only a resolved, in-budget request reaches the adapter.\n */\nexport function createDataSurfaceActionRouteHandlers(\n options: DataSurfaceRouteHandlerOptions,\n): DataSurfaceRouteHandlers {\n const { adapter, resolvePrincipal, onAuthError } = options;\n const maxRequestBytes =\n options.maxRequestBytes ?? DATA_SURFACE_MAX_REQUEST_BYTES;\n\n async function handle(\n request: Request,\n phase: 'preview' | 'apply',\n ): Promise<Response> {\n if (declaredContentLengthExceeds(request, maxRequestBytes)) {\n return jsonResponse({ error: 'payload_too_large' }, 413);\n }\n let principal: ExecuteAsPrincipalOptions;\n try {\n principal = await resolvePrincipal(request);\n } catch (error) {\n onAuthError?.(error, request);\n return jsonResponse({ error: 'unauthorized' }, 401);\n }\n const bodyResult = await readBoundedBody(request, maxRequestBytes);\n if (!bodyResult.ok) {\n return jsonResponse(\n { error: bodyResult.reason },\n bodyResult.reason === 'payload_too_large' ? 413 : 400,\n );\n }\n const body = parseJson(bodyResult.text);\n if (!body) {\n return jsonResponse({ error: 'invalid_request' }, 400);\n }\n let result: DataSurfaceActionResult;\n try {\n result =\n phase === 'preview'\n ? await adapter.preview(body, { principal })\n : await adapter.apply(body, { principal });\n } catch (error) {\n const status = authorizationErrorStatus(error);\n if (status === undefined) throw error;\n return jsonResponse(\n {\n version: 1,\n requestId: body.requestId,\n identity: body.identity,\n actionId: body.actionId,\n phase,\n ok: false,\n reason: 'denied',\n },\n status,\n );\n }\n return jsonResponse(result, statusForResult(result));\n }\n\n return {\n preview: (request) => handle(request, 'preview'),\n apply: (request) => handle(request, 'apply'),\n };\n}\n\n/**\n * Helper for the bulk-scope shape (e.g. \"apply this look to every reference\n * photo in the current selection\"): resolves an `explicit-ids` selection to\n * the full set of related row ids server-side, so the browser only ever\n * supplies the anchor id(s) and the framework — not the app — is the source\n * of truth for which rows a single idempotency key covers.\n */\nexport interface DataSurfaceBulkSelectionResolver {\n /** Expand the browser-supplied anchor ids into the full row set to act on. */\n expand(\n anchorRowIds: DataSurfaceRowId[],\n ): Promise<DataSurfaceRowId[]> | DataSurfaceRowId[];\n}\n\nexport async function resolveBulkExplicitIds(\n anchorRowIds: DataSurfaceRowId[],\n resolver: DataSurfaceBulkSelectionResolver,\n): Promise<DataSurfaceRowId[]> {\n const expanded = await resolver.expand(anchorRowIds);\n const seen = new Set<string>();\n const unique: DataSurfaceRowId[] = [];\n for (const rowId of expanded) {\n const key = `${typeof rowId}:${String(rowId)}`;\n if (seen.has(key)) continue;\n seen.add(key);\n unique.push(rowId);\n }\n return unique;\n}\n"],"mappings":";;;;;;;;;AAwDO,SAAS,cACd,WACgC;CAChC,MAAM,yBAAS,IAAI,IAA+B;CAElD,KAAA,MAAW,YAAY,WAAW;EAChC,MAAM,cAAe,SAAqC;EAI1D,KAAA,MAAW,OAAO,OAAO,OAAO,SAAS,OAAO,GAAG;GACjD,MAAM,SAAS,IAAI;GACnB,IAAI,CAAC,QAAQ;GAEb,MAAM,MAAM,OAAO;GAGnB,IAAI,CAAC,KAAK,WAAW,IAAI,QAAQ,WAAW,GAAG;GAG/C,MAAM,YAAY,OAAO;GACzB,MAAM,OACJ,IAAI,SAAS,YAAY,UAAU,QAAQ,MAAM,GAAG,IAAI;GAC1D,IAAI,CAAC,MAAM;GAEX,OAAO,IAAI,MAAM;IACf,WAAW,IAAI;IACf,gBAAgB,IAAI;IACpB;GACF,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAcO,SAAS,gBACd,SACA,QACyB;CAEzB,MAAM,aAAa,QAAQ,QAAQ,cAAc,EAAE;CACnD,IAAI,CAAC,YAAY,OAAO;CAExB,MAAM,WAAW,WAAW,MAAM,GAAG;CAGrC,IAAI,SAAS,WAAW,GAAG;EACzB,MAAM,QAAQ,OAAO,IAAI,SAAS,EAAE;EACpC,IAAI,OAAO,OAAO,EAAE,MAAM;EAC1B,OAAO;CACT;CAGA,IAAI,SAAS,WAAW,GAAG;EACzB,MAAM,QAAQ,OAAO,IAAI,SAAS,EAAE;EACpC,IAAI,OAAO,OAAO;GAAE;GAAO,IAAI,SAAS;EAAG;EAC3C,OAAO;CACT;CAGA,IAAI,SAAS,WAAW,GAAG;EACzB,MAAM,QAAQ,OAAO,IAAI,SAAS,EAAE;EACpC,IAAI,OAAO,OAAO;GAAE;GAAO,IAAI,SAAS;GAAI,QAAQ,SAAS;EAAG;EAChE,OAAO;CACT;CAEA,OAAO;AACT;;;ACjHA,eAAsB,gBACpB,QACA,WACkD;CAClD,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAGV,IAAI;EACF,MAAM,iBAAiB,MAAM,YAAY,UACvC,OAAO,KAAK,UAAU,MAAM,EAAE,GAC9B,SACF;EAEA,MAAM,UAAmD,CAAC;EAC1D,KAAA,MAAW,CAAC,SAAS,gBAAgB,gBAAgB;GACnD,MAAM,cAAuC,CAAC;GAC9C,KAAA,MAAW,CAAC,QAAQ,eAAe,aACjC,YAAY,UAAU;GAExB,IAAI,OAAO,KAAK,WAAW,CAAA,CAAE,SAAS,GACpC,QAAQ,WAAW;EAEvB;EAEA,OAAO;CACT,SAAS,OAAO;EACd,IAAI,+BAA+B,KAAK,GACtC,OAAO,CAAC;EAEV,MAAM;CACR;AACF;AAEA,SAAS,+BAA+B,OAAyB;CAC/D,MAAM,UAAU,OAAQ,OAAiB,WAAW,SAAS,EAAE;CAE/D,OACE,QAAQ,SAAS,uBAAuB,KACxC,oCAAoC,KAAK,OAAO,KAChD,4CAA4C,KAAK,OAAO,KACxD,yCAAyC,KAAK,OAAO;AAEzD;;;ACmLO,IAAM,sCAAN,MAEP;CACmB,yBAAS,IAAI,IAA2C;CACxD,8BAAc,IAAI,IAGjC;CAEF,SAAS,OAAe,QAA6C;EACnE,KAAK,OAAO,IAAI,OAAO,MAAM;CAC/B;CAEA,SAAS,OAA0D;EACjE,OAAO,KAAK,OAAO,IAAI,KAAK;CAC9B;CAEA,kBAAkB,OAAe,gBAAiC;EAChE,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK;EACpC,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI,OAAO,cAAc,OAAO,eAAe,gBAAgB,OAAO;EACtE,OAAO,aAAa;EACpB,OAAO;CACT;CAEA,kCACE,OACA,gBACA,OACA,aAC0C;EAC1C,MAAM,cAAc,KAAK,OAAO,IAAI,KAAK;EACzC,IACE,CAAC,eACA,YAAY,cAAc,YAAY,eAAe,gBAEtD;EAGF,IAAI,CADa,KAAK,YAAY,IAAI,KACjC,GACH,KAAK,YAAY,IAAI,OAAO;GAAE,QAAQ;GAAY,GAAG;EAAY,CAAC;EAEpE,YAAY,aAAa;EACzB,OAAO,KAAK,YAAY,IAAI,KAAK;CACnC;CAEA,eAAe,KAAuD;EACpE,OAAO,KAAK,YAAY,IAAI,GAAG;CACjC;CAEA,mBACE,KACA,aAC8B;EAC9B,MAAM,WAAW,KAAK,YAAY,IAAI,GAAG;EACzC,IAAI,UAAU,OAAO;EACrB,MAAM,SAAuC;GAC3C,QAAQ;GACR,GAAG;EACL;EACA,KAAK,YAAY,IAAI,KAAK,MAAM;EAChC,OAAO;CACT;CAEA,oBACE,KACA,YACAA,SACS;EACT,MAAM,WAAW,KAAK,YAAY,IAAI,GAAG;EACzC,IAAI,UAAU,WAAW,cAAc,SAAS,eAAe,YAC7D,OAAO;EACT,KAAK,YAAY,IAAI,KAAK;GACxB,QAAQ;GACR,oBAAoB,SAAS;GAC7B,QAAAA;EACF,CAAC;EACD,OAAO;CACT;CAEA,mBAAmB,KAAa,YAA6B;EAC3D,MAAM,WAAW,KAAK,YAAY,IAAI,GAAG;EACzC,IAAI,UAAU,WAAW,cAAc,SAAS,eAAe,YAC7D,OAAO;EACT,OAAO,KAAK,YAAY,OAAO,GAAG;CACpC;AACF;AAgEA,IAAM,uBAAuB,MAAS;AACtC,IAAM,wBAAwB;AAC9B,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,sCAAsB,IAAI,IAAI;CAAC;CAAa;CAAe;AAAW,CAAC;AAE7E,SAAS,mBACP,OACA,QAAQ,GACR,uBAAO,IAAI,IAAY,GACQ;CAC/B,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,CAAC,UAAU,SAAS,CAAA,CAAE,SAAS,OAAO,KAAK,GAAG,OAAO;CACzD,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,SAAS,KAAK;CAC3D,IAAI,OAAO,UAAU,YAAY,SAAS,kBAAkB,KAAK,IAAI,KAAK,GACxE,OAAO;CACT,KAAK,IAAI,KAAK;CACd,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,MAAM,SAAS,gBAAgB,OAAO;EAC1C,OAAO,MAAM,OAAO,SAAS,mBAAmB,MAAM,QAAQ,GAAG,IAAI,CAAC;CACxE;CACA,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,IAAI,cAAc,OAAO,aAAa,cAAc,MAAM,OAAO;CACjE,MAAM,UAAU,OAAO,QAAQ,KAAK;CACpC,IAAI,QAAQ,SAAS,gBAAgB,OAAO;CAC5C,OAAO,QAAQ,OACZ,CAAC,KAAK,UACL,CAAC,oBAAoB,IAAI,GAAG,KAC5B,mBAAmB,MAAM,QAAQ,GAAG,IAAI,CAC5C;AACF;AAGO,SAAS,8BACd,OAC+B;CAC/B,OAAO,mBAAmB,KAAK;AACjC;AAEA,SAAS,gBAAgB,OAAiC;CACxD,OACE,OAAO,UAAU,YACjB,MAAM,SAAS,KACf,MAAM,UAAU;AAEpB;AAEA,SAAS,eACP,WAC4C;CAC5C,IAAI,CAAC,aAAa,OAAO,cAAc,UAAU,OAAO;CACxD,MAAM,YAAY;CAClB,IAAI,UAAU,UAAU,gBAAgB,OAAO;CAC/C,IAAI,UAAU,UAAU,gBACtB,OAAO,gBAAgB,UAAU,gBAAgB;CACnD,IAAI,UAAU,UAAU,kBAAkB,CAAC,MAAM,QAAQ,UAAU,MAAM,GACvE,OAAO;CACT,IAAI,UAAU,OAAO,SAAS,gBAAgB,OAAO;CACrD,OAAO,UAAU,OAAO,OACrB,UACE,OAAO,UAAU,YAAY,MAAM,SAAS,KAC5C,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,CACvD;AACF;AAEA,SAAS,OAAO,OAAwB;CACtC,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO,KAAK,UAAU,KAAK;CAC5E,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,MAAM,CAAA,CAAE,KAAK,GAAG,EAAC;CAChE,OAAO,IAAI,OAAO,QAAQ,KAAgC,CAAA,CACvD,MAAM,CAAC,OAAO,CAAC,WAAY,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI,CAAE,CAAA,CACpE,KAAK,CAAC,KAAK,UAAU,GAAG,KAAK,UAAU,GAAG,EAAC,GAAI,OAAO,IAAI,GAAG,CAAA,CAC7D,KAAK,GAAG,EAAC;AACd;AAEA,SAAS,YAAY,OAAwB;CAC3C,OAAO,WAAW,QAAQ,CAAA,CAAE,OAAO,OAAO,KAAK,CAAC,CAAA,CAAE,OAAO,KAAK;AAChE;AAEA,SAAS,gBACP,UACA,KAC4B;CAC5B,OAAO,kCAAkC;EACvC,OAAO;EACP;CACF,CAAC,CAAA,CAAE,KAAK,QAAQ;AAClB;AAEA,SAAS,gBAAgB,KAA+C;CACtE,OACE,QAAQ,KAAA,MACP,OAAO,QAAQ,WAAW,OAAO,WAAW,GAAG,IAAI,IAAI,eAAe;AAE3E;AAEA,SAAS,eACP,UACA,KACS;CACT,MAAM,EAAE,SAAS,GAAG,aAAa;CACjC,OAAO,kCAAkC;EACvC,OAAO;EACP;CACF,CAAC,CAAA,CAAE,OAAO,UAAU,OAAO;AAC7B;AAEA,SAAS,YAAY,UAAuC;CAC1D,OAAO,OAAO,kBAAkB,QAAQ,CAAC;AAC3C;AAEA,SAAS,kBAAkB,UAAoD;CAC7E,OAAO;EACL,MAAM,SAAS;EACf,WAAW,SAAS;EACpB,GAAI,SAAS,UACT,EACE,SAAS;GACP,MAAM,SAAS,QAAQ;GACvB,IAAI,SAAS,QAAQ;EACvB,EACF,IACA,CAAC;CACP;AACF;AAEA,SAAS,SAAS,OAAiC;CACjD,OAAO,GAAG,OAAO,MAAK,GAAI,OAAO,KAAK;AACxC;AAEA,SAAS,cACP,MACA,OACQ;CACR,IAAI,OAAO,SAAS,OAAO,OAAO,OAAO,OAAO,SAAS,WAAW,KAAK;CACzE,IAAI,OAAO,SAAS,YAAY,OAAO,UAAU,UAC/C,OAAO,OAAO;CAChB,OAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI;AAChD;AAEA,SAAS,gBACP,QACoB;CACpB,MAAM,sBAAM,IAAI,IAA8B;CAC9C,KAAA,MAAW,SAAS,QAAQ,IAAI,IAAI,SAAS,KAAK,GAAG,KAAK;CAC1D,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,CAAA,CAAE,KAAK,aAAa;AAC7C;AAEA,SAAS,mBACP,WAC+B;CAC/B,IAAI,UAAU,UAAU,gBAAgB,OAAO;CAC/C,OAAO;EAAE,OAAO,UAAU;EAAO,QAAQ,gBAAgB,UAAU,MAAM;CAAE;AAC7E;AAEA,SAAS,mBACP,SACA,WACQ;CACR,OAAO,YAAY;EACjB,UAAU,kBAAkB,QAAQ,QAAQ;EAC5C,UAAU,QAAQ;EAClB,WAAW,mBAAmB,QAAQ,SAAS;EAC/C,SAAS,QAAQ;EACjB,kBAAkB,QAAQ;EAC1B,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;CACjD,CAAC;AACH;AAEA,SAAS,WAAc,OAAU,uBAAO,IAAI,QAAgB,GAAM;CAChE,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,KAAK,IAAI,KAAK,GAAG,OAAO;CACnE,KAAK,IAAI,KAAK;CACd,KAAA,MAAW,UAAU,OAAO,OAAO,KAAK,GAAG,WAAW,QAAQ,IAAI;CAClE,OAAO,OAAO,KAAK;CACnB,OAAO;AACT;AAEA,SAAS,gBACP,SACgC;CAChC,OAAO,WAAW,gBAAgB,OAAO,CAAC;AAC5C;AAEA,SAAS,sBACP,SAC0B;CAC1B,MAAM,YAAY;EAChB,GAAG,QAAQ,UAAU;EACrB,GAAI,QAAQ,UAAU,UAAU,eAC5B,EAAE,cAAc,CAAC,GAAG,QAAQ,UAAU,UAAU,YAAY,EAAE,IAC9D,CAAC;CACP;CACA,IAAI,UAAU,cAAc,OAAO,OAAO,UAAU,YAAY;CAChE,OAAO,OAAO,SAAS;CACvB,MAAM,cAAc,QAAQ,UAAU,cAClC,CAAC,GAAG,QAAQ,UAAU,WAAW,IACjC,KAAA;CACJ,IAAI,aAAa,OAAO,OAAO,WAAW;CAC1C,MAAM,gBAAgB,QAAQ,UAAU,gBACpC,WAAW,gBAAgB,QAAQ,UAAU,aAAa,CAAC,IAC3D,KAAA;CACJ,MAAM,mBAA8C;EAClD,GAAG,QAAQ;EACX;EACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;EACrC,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;CAC3C;CACA,OAAO,OAAO,gBAAgB;CAC9B,OAAO,OAAO,OAAO,EAAE,WAAW,iBAAiB,CAAC;AACtD;AAEA,SAAS,kBAAkB,QAAmD;CAC5E,OAAO,YAAY;EACjB,YAAY,OAAO;EACnB,aAAa,OAAO;EACpB,cAAc,OAAO;EACrB,WAAW,OAAO;EAClB,MAAM,OAAO;EACb,aAAa,OAAO,UAAU;EAC9B,qBAAqB,OAAO,UAAU;EACtC,iBAAiB,OAAO,UAAU;CACpC,CAAC;AACH;AAEA,SAAS,OACP,SACA,IACA,QACA,SACA,mBACyB;CACzB,OAAO;EACL,SAAS;EACT,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;EACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC7B,GAAI,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;CACnD;AACF;AAEA,SAAS,aACP,SACA,QACyB;CAIzB,OAAO;EAAE,GAAG;EAAQ,WAAW,QAAQ;CAAU;AACnD;AAEA,SAAS,gBACP,UACA,QAA+B,CAAC,GACT;CAMvB,OAAO;EACL,UANe,SAAS,QACvB,EAAE,aAAa,WAAW,UAC7B,CAAA,CAAE;EAKA,SAJc,SAAS,QAAQ,EAAE,aAAa,WAAW,SAAS,CAAA,CAAE;EAKpE,QAJa,SAAS,QAAQ,EAAE,aAAa,WAAW,QAAQ,CAAA,CAAE;EAKlE,UAAU,SAAS,KAAK,EAAE,UAAU,GAAG,eAAe;GACpD,GAAI,YAAY,CAAC;GACjB,GAAG;EACL,EAAE;EACF,GAAG;CACL;AACF;AAEA,SAAS,gBACP,SACA,OACoB;CACpB,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO;CACpD,IAAI,QAAQ,YAAY,KAAK,QAAQ,UAAU,OAC7C,OAAO;CACT,IACE,CAAC,gBAAgB,QAAQ,SAAS,KAClC,CAAC,gBAAgB,QAAQ,QAAQ,KACjC,CAAC,gBAAgB,QAAQ,UAAU,SAAS,KAC5C,CAAC;EAAC;EAAS;EAAQ;EAAU;CAAQ,CAAA,CAAE,SAAS,QAAQ,UAAU,IAAI,KACtE,CAAC,eAAe,QAAQ,SAAS,KAChC,QAAQ,YAAY,KAAA,KAAa,CAAC,mBAAmB,QAAQ,OAAO,GAErE,OAAO;CACT,IACE,CAAC,OAAO,cAAc,QAAQ,gBAAgB,KAC9C,QAAQ,mBAAmB,GAE3B,OAAO;CACT,IACE,UAAU,YACT,CAAC,gBAAgB,QAAQ,cAAc,KACrC,QAAQ,sBAAsB,KAAA,KAC7B,CAAC,gBAAgB,QAAQ,iBAAiB,IAE9C,OAAO;AAEX;AAGO,SAAS,+BACd,SAC0B;CAC1B,MAAM,QAAQ,QAAQ;CACtB,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,cACJ,QAAQ,sBAAsB,YAAY,EAAE,CAAA,CAAE,SAAS,WAAW;CACpE,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,4BAA4B,KAAK,IACrC,GACA,QAAQ,6BAA6B,EACvC;CACA,MAAM,2BAA2B,KAAK,IACpC,GACA,QAAQ,4BAA4B,GACtC;CACA,MAAM,sBAAsB,YAC1B,mBAAmB,SAAS,QAAQ,8BAA8B,OAAO,CAAC;CAE5E,eAAe,kBACb,SACA,KACgE;EAChE,MAAM,UAAU,MAAM,QAAQ,eAAe,KAAK,QAAQ,QAAQ;EAClE,IACE,YAAY,QAAQ,WAAW,QAAQ,MAAM,YAAY,QAAQ,QAAQ,GAEzE,OAAO,OAAO,SAAS,OAAO,WAAW;EAE3C,MAAM,SAAS,QAAQ,QAAQ,QAAQ;EACvC,MAAM,WAAW,QAAQ,WAAW,QAAQ,MACzC,EAAE,SAAS,OAAO,QAAQ,QAC7B;EACA,IACE,CAAC,UACD,CAAC,YACD,OAAO,WAAW,OAAO,SAAS,MAClC,QAAQ,SAAS,oBAAoB,OAClC,OAAO,iBAAiB,aAE3B,OAAO,OAAO,SAAS,OAAO,aAAa;EAE7C,IACE,CAAC,OAAO,QACR,CAAC,gBAAgB,OAAO,WAAW,EAAE,KACrC,CAAC,gBAAgB,OAAO,WAAW,MAAM,GAEzC,OAAO,OAAO,SAAS,OAAO,QAAQ;EACxC,IAAI,kBAAkB,OAAO,IAAI;EACjC,MAAM,IAAI,gBACR,OAAO,UAAU,YACjB,OAAO,UAAU,MACnB;EACA,MAAM,oBAAoB,MAAM,OAAO,gBAAgB,QAAQ,OAAO;EACtE,IAAI,CAAC,kBAAkB,OACrB,OAAO,OACL,SACA,OACA,kBAAkB,UAAU,iBAC9B;EACF,IACE,CAAC,SAAS,gBAAgB,SAAS,QAAQ,UAAU,KAAK,KAC1D,CAAC,OAAO,WAAW,gBAAgB,SAAS,QAAQ,UAAU,KAAK,GAEnE,OAAO,OAAO,SAAS,OAAO,yBAAyB;EAEzD,MAAM,OAAO;GACX;GACA;GACA,YAAY,QAAQ;GACpB;EACF;EACA,MAAM,oBAAoB,MAAM,QAAQ,iBACtC,MACA,mBAAmB,QAAQ,SAAS,CACtC;EACA,MAAM,YAAY;GAChB,GAAG;GACH,QAAQ,gBAAgB,kBAAkB,MAAM;EAClD;EACA,MAAM,aAAa;GAAE,GAAG;GAAM;EAAU;EACxC,IAAI,CAAE,MAAM,OAAO,UAAU,UAAU,GACrC,OAAO,OAAO,SAAS,OAAO,QAAQ;EAExC,IAAI,UAAU,OAAO,SAAS,QAAQ,WAAW,OAAO,kBACtD,OAAO,OAAO,SAAS,OAAO,gBAAgB;EAEhD,OAAO;CACT;CAEA,eAAe,QACb,SACA,SACkC;EAClC,MAAM,UAAU,gBAAgB,SAAS,SAAS;EAClD,IAAI,SAAS,OAAO,OAAO,SAAS,OAAO,OAAO;EAClD,MAAM,eAAe,sBAAsB,OAAO;EAClD,OAAO,eACL;GACE,GAAG,aAAa;GAChB,QAAQ;GACR,eAAe;IACb,GAAG,aAAa,UAAU;IAC1B,WAAW,QAAQ,SAAS;IAC5B,UAAU,QAAQ;IAClB,WAAW,QAAQ;GACrB;EACF,GACA,OAAO,QAAQ;GACb,MAAM,aAAa,MAAM,kBAAkB,SAAS,GAAG;GACvD,IAAI,QAAQ,YAAY,OAAO;GAC/B,IAAI,WAAW,UAAU,aAAa,QAAQ,kBAC5C,OAAO,OAAO,SAAS,OAAO,gBAAgB;GAEhD,MAAM,WAA0C,CAAC;GACjD,KAAA,MAAW,SAAS,WAAW,UAAU,QAAQ;IAC/C,MAAM,cAAc,MAAM,WAAW,OAAO,SAC1C,YACA,KACF;IACA,SAAS,KAAK;KACZ;KACA,QAAQ,YAAY,WAAW,aAAa;KAC5C,GAAI,YAAY,SAAS,EAAE,QAAQ,YAAY,OAAO,IAAI,CAAC;IAC7D,CAAC;GACH;GACA,MAAM,oBAAoB,YAAY;GACtC,MAAM,uBAAuB,YAC3B,mBAAmB,QAAQ,SAAS,CACtC;GACA,MAAM,0BAA0B,mBAAmB,OAAO;GAC1D,MAAM,YAAY,IAAI,IAAI;GAC1B,MAAM,MAAM,SAAS,mBAAmB;IACtC;IACA,aAAa,aAAa,UAAU,UAAU;IAC9C,UAAU,aAAa,UAAU,UAAU;IAC3C,kBAAkB,aAAa,UAAU,oBAAoB;IAC7D,iBACE,aAAa,UAAU,UAAU,mBAAmB;IACtD,YAAY,aAAa,UAAU,cAAc;IACjD,aAAa,YAAY,QAAQ,QAAQ;IACzC,UAAU,QAAQ;IAClB,mBAAmB,kBAAkB,WAAW,MAAM;IACtD,UAAU,WAAW,UAAU;IAC/B,kBAAkB,WAAW,UAAU;IACvC;IACA,yBAAyB,YACvB,gBAAgB,WAAW,UAAU,MAAM,CAC7C;IACA,oBAAoB;GACtB,CAAC;GACD,OAAO,OACL,SACA,MACA,KAAA,GACA,gBAAgB,UAAU;IACxB,OAAO,WAAW,UAAU,OAAO;IACnC,UAAU,WAAW,UAAU;IAC/B,kBAAkB,WAAW,UAAU;IACvC;GACF,CAAC,GACD,iBACF;EACF,CACF;CACF;CAEA,eAAe,kBACb,SACA,YACkC;EAClC,MAAM,WAA0C,CAAC;EACjD,KAAA,MAAW,SAAS,WAAW,UAAU,QACvC,IAAI;GACF,MAAM,cAAc,MAAM,WAAW,OAAO,SAAS,YAAY,KAAK;GACtE,IAAI,CAAC,YAAY,UAAU;IACzB,SAAS,KAAK;KACZ;KACA,QAAQ;KACR,GAAI,YAAY,SAAS,EAAE,QAAQ,YAAY,OAAO,IAAI,CAAC;IAC7D,CAAC;IACD;GACF;GACA,MAAM,UAAU,MAAM,WAAW,OAAO,MAAM,YAAY,KAAK;GAC/D,SAAS,KAAK;IACZ;IACA,QAAQ;IACR,GAAI,YAAY,QAChB,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,OAAO,IAClB,EAAE,UAAU,QAAQ,IACpB,CAAC;GACP,CAAC;EACH,SAAS,OAAO;GACd,SAAS,KAAK;IACZ;IACA,QAAQ;IACR,QAAQ,QAAQ,WAAW,OAAO,OAAO,KAAK;GAChD,CAAC;EACH;EAEF,OAAO,OAAO,SAAS,MAAM,KAAA,GAAW,gBAAgB,QAAQ,CAAC;CACnE;CAEA,eAAe,sBACb,SACA,OACA,WACkC;EAClC,MAAM,aAAa,YAAY,EAAE,CAAA,CAAE,SAAS,WAAW;EACvD,MAAM,uBAAuB,YAAY;GACvC,MAAM;GACN,SAAS,OAAO,sBAAsB,mBAAmB,OAAO;GAChE,QAAQ,OAAO,qBAAqB,QAAQ;EAC9C,CAAC;EACD,MAAM,iBAAiB,YAAY;GACjC,MAAM;GACN,aAAa,UAAU;GACvB,UAAU,UAAU;GACpB,kBAAkB,UAAU;GAC5B,iBAAiB,UAAU;GAC3B,YAAY,UAAU,cAAc;GACpC,UAAU,kBAAkB,QAAQ,QAAQ;GAC5C,UAAU,QAAQ;GAClB,gBAAgB,QAAQ;EAC1B,CAAC;EACD,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,KAAK,2BAA2B,yBAAyB,CAChE;EACA,KAAA,IAAS,OAAO,GAAG,QAAQ,UAAU,QAAQ,GAAG;GAC9C,MAAM,SAAS,MAAM,MAAM,mBAAmB,gBAAgB;IAC5D,oBAAoB;IACpB;IACA,YAAY,IAAI;GAClB,CAAC;GACD,IAAI,OAAO,uBAAuB,sBAChC,OAAO,OAAO,SAAS,OAAO,sBAAsB;GACtD,IAAI,OAAO,WAAW,aACpB,OAAO,aAAa,SAAS,OAAO,MAAM;GAC5C,IAAI,OAAO,eAAe,YAAY;IACpC,IAAI;IACJ,IAAI;KAIF,MAAM,WAAW,MAAM,QAAQ,2BAA2B,SAAS;KACnE,IACE,CAAC,YACD,SAAS,UAAU,gBAAgB,UAAU,eAC7C,SAAS,UAAU,aAAa,UAAU,aACzC,SAAS,UAAU,mBAAmB,UACrC,UAAU,oBACX,SAAS,oBAAoB,UAC5B,UAAU,qBACX,SAAS,cAAc,WAAW,UAAU,cAAc,SAC3D,CAAC,MAAM,QAAQ,SAAS,UAAU,YAAY,GAE9C,MAAM,IAAI,MACR,6EACF;KAEF,YAAY;IACd,SAAS,OAAO;KACd,MAAM,SAAS,QAAQ,WAAW,OAAO,OAAO;KAChD,IAAI,CAAC,QAAQ;MAGX,MAAM,MAAM,mBAAmB,gBAAgB,UAAU;MACzD,MAAM;KACR;KACA,MAAM,SAAS,OAAO,SAAS,OAAO,MAAM;KAC5C,IACE,CAAE,MAAM,MAAM,oBACZ,gBACA,YACA,MACF,GAEA,MAAM,IAAI,MAAM,gDAAgD;KAElE,OAAO;IACT;IACA,IAAI;IACJ,IAAI,kBAAkB;IACtB,IAAI;KAGF,MAAM,EAAE,aAAa,cAAc,GAAG,kBAAkB;KACxD,WAAW,MAAM,gBACf,SACA,EAAE,WAAW,cAAc,GAC3B,OACA,aACM;MACJ,kBAAkB;KACpB,CACF;IACF,SAAS,OAAO;KACd,MAAM,SAAS,QAAQ,WAAW,OAAO,OAAO;KAChD,IAAI,CAAC,QAAQ;MACX,IAAI,CAAC,iBACH,MAAM,MAAM,mBAAmB,gBAAgB,UAAU;MAI3D,MAAM;KACR;KACA,WAAW,OAAO,SAAS,OAAO,MAAM;IAC1C;IACA,IACE,CAAE,MAAM,MAAM,oBACZ,gBACA,YACA,QACF,GAEA,MAAM,IAAI,MAAM,gDAAgD;IAElE,OAAO;GACT;GACA,IAAI,OAAO,UAAU;IACnB,MAAM,IAAI,SAAe,YACvB,WAAW,SAAS,yBAAyB,CAC/C;IACA,MAAM,UAAU,MAAM,MAAM,eAAe,cAAc;IACzD,IAAI,SAAS,WAAW,aACtB,OAAO,aAAa,SAAS,QAAQ,MAAM;GAC/C;EACF;EACA,OAAO,OAAO,SAAS,OAAO,yBAAyB;CACzD;CAEA,eAAe,gBACb,SACA,SACA,OACA,iBACA,gBACkC;EAClC,MAAM,iBAAiB,QAAQ;EAC/B,IAAI,CAAC,gBAAgB,OAAO,OAAO,SAAS,OAAO,iBAAiB;EAGpE,MAAM,6BAA6B,OAAO,OAAO;GAC/C,aAAa,QAAQ,UAAU,UAAU;GACzC,UAAU,QAAQ,UAAU,UAAU;GACtC,iBAAiB,QAAQ,UAAU,UAAU,mBAAmB;GAChE,kBAAkB,QAAQ,UAAU,oBAAoB;GACxD,GAAI,QAAQ,UAAU,aAClB,EAAE,YAAY,QAAQ,UAAU,WAAW,IAC3C,CAAC;EACP,CAAC;EACD,OAAO,eACL;GACE,GAAG,QAAQ;GACX,QAAQ;GACR,eAAe;IACb,GAAG,QAAQ,UAAU;IACrB,WAAW,QAAQ,SAAS;IAC5B,UAAU,QAAQ;IAClB,WAAW,QAAQ;IACnB,gBAAgB,QAAQ;GAC1B;EACF,GACA,OAAO,QAAQ;GACb,MAAM,aAAa,MAAM,kBAAkB,SAAS,GAAG;GACvD,IAAI,QAAQ,YAAY,OAAO;GAC/B,IAAI;QAEA,mBAAmB,OAAO,MAAM,MAAM,sBACtC,WAAW,UAAU,aAAa,MAAM,YACxC,WAAW,UAAU,aAAa,QAAQ,oBAC1C,WAAW,UAAU,qBAAqB,MAAM,oBAChD,YAAY,mBAAmB,QAAQ,SAAS,CAAC,MAC/C,MAAM,wBACR,kBAAkB,WAAW,MAAM,MAAM,MAAM,qBAC/C,YAAY,gBAAgB,WAAW,UAAU,MAAM,CAAC,MACtD,MAAM,yBAER,OAAO,OAAO,SAAS,OAAO,eAAe;GAAA,OAEjD,IAAW,WAAW,OAAO,iBAAiB,YAC5C,OAAO,OAAO,SAAS,OAAO,uBAAuB;QACvD,IAAW,WAAW,UAAU,aAAa,QAAQ,kBACnD,OAAO,OAAO,SAAS,OAAO,gBAAgB;GAEhD,IAAI,WAAW,OAAO,cAAc,gBAAgB,iBAAiB;IACnE,IACE,CAAC,QAAQ,mBACT,CAAC,gBAAgB,QAAQ,mBAAmB,KAC5C,CAAC,QAAQ,4BACT,CAAC,gBAAgB,QAAQ,0BAA0B,GAEnD,OAAO,OAAO,SAAS,OAAO,wBAAwB;IAExD,MAAM,EAAE,mBAAmB,oBAAoB,GAAG,oBAChD;IACF,MAAM,mBAAmB;KACvB,SAAS;KACT,WAAW,QAAQ,uBAAuB;KAC1C,SAAS;KACT,WAAW;KACX,GAAI,QAAQ,EAAE,cAAc,MAAM,IAAI,CAAC;IACzC;IACA,MAAM,SAAS,MAAM,QAAQ,gBAAgB,QAAQ;KACnD;KACA,UAAU,QAAQ;KAClB,UAAU,QAAQ;KAClB,QAAQ,WAAW,UAAU;KAC7B,UAAU;MACR,GAAG;MACH,SAAS,gBACP,kBACA,QAAQ,0BACV;KACF;KACA,WACE,sBAAsB,SAAS,OAAO,0BAA0B;IACpE,CAAC;IACD,OAAO,OAAO,SAAS,MAAM,KAAA,GAAW;KACtC,UAAU,WAAW,UAAU,OAAO;KACtC,SAAS;KACT,QAAQ;KACR,GAAI,OAAO,WAAW,CAAC;KACvB,YAAY;KACZ,OAAO,OAAO;KAId,cAAc,QAAQ;IACxB,CAAC;GACH;GACA,iBAAiB;GACjB,OAAO,kBAAkB,SAAS,UAAU;EAC9C,CACF;CACF;CAEA,eAAe,MACb,OACA,SACkC;EAClC,MAAM,UAAU,gBAAgB,OAAO,OAAO;EAC9C,IAAI,SAAS,OAAO,OAAO,OAAO,OAAO,OAAO;EAChD,MAAM,UAAU,gBAAgB,KAAK;EACrC,MAAM,eAAe,sBAAsB,OAAO;EAClD,MAAM,oBAAoB,QAAQ;EAClC,MAAM,iBAAiB,QAAQ;EAC/B,IAAI,CAAC,gBAAgB,OAAO,OAAO,SAAS,OAAO,iBAAiB;EACpE,MAAM,cAAc,aAAa,UAAU,UAAU;EACrD,MAAM,WAAW,aAAa,UAAU,UAAU;EAClD,MAAM,mBAAmB,aAAa,UAAU,oBAAoB;EACpE,MAAM,kBACJ,aAAa,UAAU,UAAU,mBAAmB;EACtD,MAAM,aAAa,aAAa,UAAU,cAAc;EACxD,MAAM,0BAA0B,mBAAmB,OAAO;EAC1D,MAAM,mBAAmB,YAAY;GACnC;GACA;GACA;GACA;GACA;GACA,UAAU,kBAAkB,QAAQ,QAAQ;GAC5C,UAAU,QAAQ;GAClB;EACF,CAAC;EACD,MAAM,QAAQ,MAAM,MAAM,eAAe,gBAAgB;EACzD,IAAI,SAAS,MAAM,uBAAuB,yBACxC,OAAO,OAAO,SAAS,OAAO,sBAAsB;EAGtD,IAAI,OAAO,WAAW,aACpB,OAAO,aAAa,SAAS,MAAM,MAAM;EAE3C,IAAI;EACJ,IAAI,mBAAmB;GACrB,QAAQ,MAAM,MAAM,SAAS,iBAAiB;GAC9C,IAAI,CAAC,SAAS,MAAM,aAAa,IAAI,GACnC,OAAO,OAAO,SAAS,OAAO,iCAAiC;GAEjE,IACE,MAAM,gBAAgB,eACtB,MAAM,aAAa,YACnB,MAAM,qBAAqB,oBAC3B,MAAM,oBAAoB,mBAC1B,MAAM,eAAe,cACrB,MAAM,gBAAgB,YAAY,QAAQ,QAAQ,KAClD,MAAM,aAAa,QAAQ,YAC3B,MAAM,uBAAuB,yBAE7B,OAAO,OAAO,SAAS,OAAO,uBAAuB;EAEzD;EAKA,MAAM,sBAAsB,MAAM,eAChC;GACE,GAAG,aAAa;GAChB,QAAQ;GACR,eAAe,aAAa,UAAU;EACxC,GACA,OAAO,QAAQ;GACb,MAAM,UAAU,MAAM,QAAQ,eAAe,KAAK,QAAQ,QAAQ;GAClE,IACE,YAAY,QAAQ,WAAW,QAAQ,MACvC,YAAY,QAAQ,QAAQ,GAE5B,OAAO,KAAA;GACT,MAAM,SAAS,QAAQ,QAAQ,QAAQ;GACvC,MAAM,WAAW,QAAQ,WAAW,QAAQ,MACzC,EAAE,SAAS,OAAO,QAAQ,QAC7B;GACA,IAAI,CAAC,UAAU,CAAC,YAAY,OAAO,WAAW,OAAO,SAAS,IAC5D,OAAO,KAAA;GACT,IACE,OAAO,cAAc,iBACpB,CAAC,QAAQ,mBACR,CAAC,gBAAgB,QAAQ,mBAAmB,KAC5C,CAAC,QAAQ,4BACT,CAAC,gBAAgB,QAAQ,0BAA0B,IAErD,OAAO,OAAO,SAAS,OAAO,wBAAwB;EAG1D,CACF;EACA,IAAI,qBAAqB,OAAO;EAIhC,MAAM,aAAa,YAAY,EAAE,CAAA,CAAE,SAAS,WAAW;EACvD,MAAM,cAAc;GAClB,oBAAoB;GACpB;GACA,YAAY,IAAI;EAClB;EACA,IAAI;EACJ,IAAI,mBAAmB;GACrB,cAAc,MAAM,MAAM,kCACxB,mBACA,gBACA,kBACA,WACF;GACA,IAAI,CAAC,aAAa,OAAO,OAAO,SAAS,OAAO,uBAAuB;EACzE;EACA,MAAM,WAAW,KAAK,IACpB,GACA,KAAK,KAAK,2BAA2B,yBAAyB,CAChE;EACA,KAAA,IAAS,OAAO,GAAG,QAAQ,UAAU,QAAQ,GAAG;GAC9C,MAAM,SACJ,SAAS,KAAK,cACV,cACA,MAAM,MAAM,mBAAmB,kBAAkB,WAAW;GAClE,IAAI,OAAO,uBAAuB,yBAChC,OAAO,OAAO,SAAS,OAAO,sBAAsB;GACtD,IAAI,OAAO,WAAW,aACpB,OAAO,aAAa,SAAS,OAAO,MAAM;GAC5C,IAAI,OAAO,eAAe,YAAY;IACpC,IAAI;IACJ,IAAI;KACF,UAAU,MAAM,gBAAgB,SAAS,cAAc,OAAO,IAAI;IACpE,SAAS,OAAO;KACd,MAAM,MAAM,mBAAmB,kBAAkB,UAAU;KAC3D,MAAM;IACR;IAIA,IAAI,CAAC,QAAQ,MAAM,QAAQ,WAAW,yBAAyB;KAC7D,MAAM,MAAM,mBAAmB,kBAAkB,UAAU;KAC3D,OAAO;IACT;IAGA,IACE,CAAE,MAAM,MAAM,oBACZ,kBACA,YACA,OACF,GAEA,MAAM,IAAI,MAAM,2CAA2C;IAE7D,OAAO;GACT;GACA,IAAI,OAAO,UAAU;IACnB,MAAM,IAAI,SAAe,YACvB,WAAW,SAAS,yBAAyB,CAC/C;IACA,MAAM,UAAU,MAAM,MAAM,eAAe,gBAAgB;IAC3D,IAAI,SAAS,WAAW,aACtB,OAAO,aAAa,SAAS,QAAQ,MAAM;GAC/C;EACF;EACA,OAAO,OAAO,SAAS,OAAO,yBAAyB;CACzD;CAEA,eAAe,gBACb,UACkC;EAClC,IACE,CAAC,gBAAgB,QAAQ,0BAA0B,KACnD,CAAC,eACC,UACA,QAAQ,0BACV,GAEA,MAAM,IAAI,MAAM,sDAAsD;EAExE,MAAM,YAAY,SAAS;EAC3B,IACE,SAAS,YAAY,KACrB,CAAC,QAAQ,uBACT,SAAS,cAAc,QAAQ,uBAC/B,CAAC,aACD,CAAC,gBAAgB,UAAU,WAAW,KACrC,UAAU,aAAa,QAAQ,CAAC,gBAAgB,UAAU,QAAQ,KAClE,UAAU,oBAAoB,QAC7B,CAAC,gBAAgB,UAAU,eAAe,KAC3C,UAAU,qBAAqB,QAC9B,CAAC,gBAAgB,UAAU,gBAAgB,KAC5C,UAAU,eAAe,KAAA,KACxB,CAAC,gBAAgB,UAAU,UAAU,GAEvC,OAAO,OAAO,SAAS,SAAS,OAAO,iBAAiB;EAE1D,MAAM,UAAU,gBAAgB,SAAS,SAAS,OAAO;EACzD,IAAI,SAAS,OAAO,OAAO,SAAS,SAAS,OAAO,OAAO;EAC3D,OAAO,sBACL,gBAAgB,SAAS,OAAO,GAChC,SAAS,cACT,OAAO,OAAO,EAAE,GAAG,SAAS,UAAU,CAAC,CACzC;CACF;CAEA,OAAO;EAAE;EAAS;EAAO;CAAgB;AAC3C;;;;;;;;;;;ACjyCA,IAAM,2BAAW,IAAI,IAKnB;AA2BK,IAAM,4BAAN,cAAwC,WAAW;CAExD,WAA0B;CAG1B,OAAiC;EAC/B,SAAS;EACT,UAAU,CAAC;CACb;CAGA,MAAM,IACJ,OAAiC,KAAK,MACtC,SACkC;EAClC,MAAM,WAAW,MAAM;EACvB,MAAM,mBAAmB,6BAA6B,KAAK;EAC3D,IAAI,oBAAoB,CAAC,yBAAyB,gBAAgB,GAChE,MAAM,IAAI,MAAM,iDAAiD;EAInE,IAAI;EACJ,IAAI,kBAAkB;GACpB,MAAM,iBAAiB,iBAAiB,IAAI;GAC5C,IAAI,mBAAmB,MACrB,cAAc;QAChB,IACE,OAAO,mBAAmB,YAC1B,eAAe,SAAS,GAExB,cAAc;QAEd,MAAM,IAAI,MAAM,gDAAgD;EAEpE,OACE,cAAc,KAAK,YAAY,YAAY,KAAK;EAElD,IACE,MAAM,YAAY,KAClB,UAAU,YAAY,KACtB,OAAO,SAAS,cAAc,YAC9B,SAAS,UAAU,WAAW,KAC9B,SAAS,WAAW,aAAa,aAEjC,MAAM,IAAI,MAAM,8CAA8C;EAEhE,MAAM,UAAU,SAAS,IAAI,SAAS,SAAS;EAC/C,IAAI,CAAC,SACH,MAAM,IAAI,MACR,iDAAiD,SAAS,WAC5D;EAEF,MAAM,SAAS,MAAM,QAAQ,QAAQ;EACrC,IAAI,CAAC,OAAO,MAAM,OAAO,WAAW,2BAClC,MAAM,IAAI,MAAM,qDAAqD;EAEvE,OAAO;CACT;AACF;AAzDE,kBAAA,CADC,SAAS,EAAE,UAAU,KAAK,CAAC,CAAA,GADjB,0BAEX,WAAA,YAAA,CAAA;AAGA,kBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAJ5B,0BAKX,WAAA,QAAA,CAAA;AAMM,kBAAA,CADL,mBAAmB,CAAA,GAVT,0BAWL,WAAA,OAAA,CAAA;AAXK,4BAAN,kBAAA,CAPN,aAAa,EAAE,MAAM,WAAW,CAAC,GACjC,KAAK;CACJ,WAAW;CACX,KAAK;CACL,KAAK;CACL,KAAK;AACP,CAAC,CAAA,GACY,yBAAA;AAiEN,SAAS,2CACd,WACA,SAGY;CACZ,IAAI,CAAC,aAAa,UAAU,SAAS,KACnC,MAAM,IAAI,MACR,6DACF;CAEF,MAAM,WAAW,SAAS,IAAI,SAAS;CACvC,IAAI,YAAY,aAAa,SAC3B,MAAM,IAAI,MACR,mDAAmD,WACrD;CAEF,SAAS,IAAI,WAAW,OAAO;CAC/B,aAAa;EACX,IAAI,SAAS,IAAI,SAAS,MAAM,SAAS,SAAS,OAAO,SAAS;CACpE;AACF;AAEO,SAAS,qCACd,SACqD;CAKrD,OAAO;EACL,YALiB,2CACjB,QAAQ,WACR,QAAQ,OAGR;EACA,MAAM,QAAQ,KAAqC;GACjD,IAAI,IAAI,SAAS,cAAc,QAAQ,WACrC,MAAM,IAAI,MAAM,+CAA+C;GAEjE,MAAM,YAAY,MAAM,4BACtB,SACA,IAAI,QACN;GACA,IAAI,CAAC,UAAU,IACb,MAAM,IAAI,MAAM,2CAA2C;GAC7D,OAAO;IACL,OAAO,UAAU;IACjB,SAAS,EAAE,OAAO,UAAU,MAAM;GACpC;EACF;CACF;AACF;AAEA,eAAe,4BACb,SACA,UACkB;CAClB,MAAM,eAAe,qBAAqB,SAAS;CACnD,MAAM,OAAO,MAAM,kBAAkB,OAAO,EAAE,IAAI,QAAQ,GAAG,CAAC;CAC9D,MAAM,aACJ,eAAe,sBAAsB,yBAAyB,KAC9D,eAAe,SAAS,2BAA2B;CACrD,MAAM,aACJ,YAAY,iBACZ,YAAY,QACZ,0BAA0B;CAC5B,OAAO,KAAK,WACV;EACE,UAAU,SAAS,UAAU;EAC7B,OAAO,QAAQ,SAAS;EACxB;EACA,UAAU;EACV,QAAQ;EACR,MAAM;GAAE,SAAS;GAAG;EAAS;EAC7B,UAAU,QAAQ,YAAY;EAC9B,SAAS,QAAQ,WAAW;EAC5B,aAAa,QAAQ,eAAe;CACtC,GACA,EAAE,cAAc,QAAQ,aAAa,CACvC;AACF;;;ACxIO,SAAS,uBACd,UACiB;CACjB,MAAM,WAAW,SAAS;CAE1B,OAAO;EACL,IAAI,SAAS,WAAW,GAAG,SAAS,eAAc,GAAI,SAAS;EAC/D,MAAM,UAAU,QAAQ,SAAS;EACjC,YAAY,SAAS;EACrB,WAAW,SAAS;EACpB,YAAY,SAAS;EACrB,OAAO,UAAU;EACjB,aAAa,UAAU;EACvB,QAAQ,SAAS;EACjB,gBAAgB,SAAS;EACzB,aAAa,SAAS;EACtB,MAAM,UAAU;EAEhB,QAAQ,eAAe,SAAS,MAAM;CACxC;AACF;;;;;;;;;;;AC5EA,IAAM,cAAc;AACpB,IAAM,oBAAoB;AAE1B,IAAM,mBAAmB;CACvB,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAGO,IAAM,8BAAN,cAA0C,WAAW;CAE1D,YAAoB;CAGpB,SAAwC,iBAAiB;CAGzD,aAA4B;AAC9B;AAPE,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;CAAM,QAAQ;AAAK,CAAC,CAAA,GAD1C,4BAEX,WAAA,aAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAJ5B,4BAKX,WAAA,UAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAP5B,4BAQX,WAAA,cAAA,CAAA;AARW,8BAAN,gBAAA,CADN,KAAK;CAAE,WAAW;CAAoC,GAAG;AAAiB,CAAC,CAAA,GAC/D,2BAAA;AAeN,IAAM,oCAAN,cAAgD,WAAW;CAEhE,UAAkB;CAGlB,SAAmC;CAGnC,qBAA6B;CAG7B,YAA2B;CAG3B,aAA4B;CAG5B,SAAyC;CAGzC,WAA0D;AAC5D;AAnBE,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;CAAM,QAAQ;AAAK,CAAC,CAAA,GAD1C,kCAEX,WAAA,WAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAJ5B,kCAKX,WAAA,UAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAP5B,kCAQX,WAAA,sBAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAV5B,kCAWX,WAAA,aAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAb5B,kCAcX,WAAA,cAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAhB5B,kCAiBX,WAAA,UAAA,CAAA;AAGA,gBAAA,CADC,MAAM;CAAE,MAAM;CAAQ,UAAU;AAAK,CAAC,CAAA,GAnB5B,kCAoBX,WAAA,YAAA,CAAA;AApBW,oCAAN,gBAAA,CAJN,KAAK;CACJ,WAAW;CACX,GAAG;AACL,CAAC,CAAA,GACY,iCAAA;AAuCN,IAAM,wCAAN,cAAoD,MAAM;CAC/D,YAAY,OAAe;EACzB,MAAM,kDAAkD,OAAO;EAC/D,KAAK,OAAO;CACd;AACF;AAWO,IAAM,iCAAN,MAEP;CACmB;CACA;CACA;CAEjB,YAAY,SAAgD;EAC1D,KAAK,KAAK,QAAQ;EAClB,KAAK,MAAM,QAAQ,OAAO,KAAK;EAC/B,KAAK,oBAAoB,QAAQ;CACnC;CAEA,MAAM,SACJ,OACA,QACe;EACf,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,CAAC,CAAA,CAAE,YAAY;EACnD,MAAM,KAAK,GAAG,MACZ,eAAe,YAAW;;;4CAI1B,WAAW,GACX,gBAAgB,WAAW,KAC3B,WACA,WACA,WAAW,KAAK,GAChB,KAAK,UAAU,MAAM,CACvB;CACF;CAEA,MAAM,SACJ,OACoD;EAKpD,MAAM,OAAM,MAJQ,KAAK,GAAG,MAC1B,mCAAmC,YAAW,gCAC9C,WAAW,KAAK,CAClB,EAAA,CACkB,KAAK;EACvB,IAAI,CAAC,KAAK,OAAO,KAAA;EAEjB,OAAO;GACL,GAFa,YAAY,YAAY,IAAI,QAAQ,WAAW,CAEzD;GACH,GAAI,OAAO,IAAI,gBAAgB,WAC3B,EAAE,YAAY,IAAI,YAAY,IAC9B,CAAC;EACP;CACF;CAEA,MAAM,kBACJ,OACA,gBACkB;EAUlB,QAAO,MATe,KAAK,GAAG,MAC5B,UAAU,YAAW;;;+BAIrB,WAAW,cAAc,GACzB,IAAI,KAAK,KAAK,IAAI,CAAC,CAAA,CAAE,YAAY,GACjC,WAAW,KAAK,CAClB,EAAA,CACe,KAAK,WAAW;CACjC;CAEA,MAAM,kCACJ,OACA,gBACA,OACA,aACmD;EACnD,MAAM,cAAc,KAAK,GAAG;EAC5B,IAAI,CAAC,aACH,MAAM,IAAI,MACR,kEACF;EAEF,OAAQ,MAAM,YAAY,KAAK,KAAK,IAAI,OAAO,OAAO;GACpD,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,CAAC,CAAA,CAAE,YAAY;GAYnD,KAAI,MAXmB,GAAG,MACxB,UAAU,YAAW;;;;iCAKrB,WAAW,cAAc,GACzB,WACA,WAAW,KAAK,GAChB,WAAW,cAAc,CAC3B,EAAA,CACa,KAAK,WAAW,GAAG,OAAO,KAAA;GACvC,MAAM,GAAG,MACP,eAAe,kBAAiB;;;;4CAKhC,WAAW,GACX,sBAAsB,WAAW,KACjC,WACA,WACA,WAAW,KAAK,GAChB,YAAY,oBACZ,WAAW,YAAY,UAAU,GACjC,OAAO,YAAY,UAAU,CAC/B;GACA,MAAM,UAAU,MAAM,KAAK,wBACzB,OACA,YAAY,YACZ,EACF;GACA,IAAI,CAAC,SACH,MAAM,IAAI,sCAAsC,iBAAiB;GAEnE,OAAO;EACT,CAAC;CACH;CAEA,MAAM,eACJ,KACmD;EAMnD,MAAM,OAAM,MALQ,KAAK,GAAG,MAC1B;gBACU,kBAAiB,8BAC3B,WAAW,GAAG,CAChB,EAAA,CACkB,KAAK;EACvB,OAAO,MAAM,kBAAkB,GAAG,IAAI,KAAA;CACxC;CAEA,MAAM,mBACJ,KACA,aACuC;EACvC,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,CAAC,CAAA,CAAE,YAAY;EACnD,MAAM,KAAK,GAAG,MACZ,eAAe,kBAAiB;;;;0CAKhC,WAAW,GACX,sBAAsB,WAAW,KACjC,WACA,WACA,WAAW,GAAG,GACd,YAAY,oBACZ,WAAW,YAAY,UAAU,GACjC,OAAO,YAAY,UAAU,CAC/B;EACA,MAAM,UAAU,MAAM,KAAK,wBACzB,KACA,YAAY,UACd;EACA,IAAI,CAAC,SACH,MAAM,IAAI,sCAAsC,iBAAiB;EACnE,OAAO;CACT;CAEA,MAAM,oBACJ,KACA,YACA,QACkB;EAYlB,QAAO,MAXe,KAAK,GAAG,MAC5B,UAAU,kBAAiB;;;;6BAK3B,KAAK,UAAU,MAAM,GACrB,IAAI,KAAK,KAAK,IAAI,CAAC,CAAA,CAAE,YAAY,GACjC,WAAW,GAAG,GACd,WAAW,UAAU,CACvB,EAAA,CACe,KAAK,WAAW;CACjC;CAEA,MAAM,mBAAmB,KAAa,YAAsC;EAQ1E,QAAO,MAPe,KAAK,GAAG,MAC5B,eAAe,kBAAiB;;6BAGhC,WAAW,GAAG,GACd,WAAW,UAAU,CACvB,EAAA,CACe,KAAK,WAAW;CACjC;CAEA,MAAM,qBACJ,KACA,SACkB;EAClB,IACE,CAAC,QAAQ,sBACT,CAAC,OAAO,cAAc,QAAQ,UAAU,KACxC,QAAQ,aAAa,KACrB,CAAC,QAAQ,gBACT,QAAQ,aAAa,SAAS,OAC9B,CAAC,QAAQ,YACT,QAAQ,SAAS,SAAS,MAE1B,OAAO;EAET,IACE,CAAC,KAAK,qBACN,CAAE,MAAM,KAAK,kBAAkB,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC,GAE5D,OAAO;EAET,MAAM,WAAmD;GACvD,cAAc,QAAQ;GACtB,UAAU,QAAQ;GAClB,cAAc,KAAK,IAAI;EACzB;EAeA,QAAO,MAde,KAAK,GAAG,MAC5B,UAAU,kBAAiB;;;;;6BAM3B,KAAK,UAAU,QAAQ,MAAM,GAC7B,KAAK,UAAU,QAAQ,GACvB,IAAI,KAAK,SAAS,YAAY,CAAA,CAAE,YAAY,GAC5C,WAAW,GAAG,GACd,QAAQ,oBACR,OAAO,QAAQ,UAAU,CAC3B,EAAA,CACe,KAAK,WAAW;CACjC;CAEA,MAAc,wBACZ,KACA,YACA,KAAwB,KAAK,IACsB;EAMnD,MAAM,OAAM,MALQ,GAAG,MACrB;gBACU,kBAAiB,8BAC3B,WAAW,GAAG,CAChB,EAAA,CACkB,KAAK;EACvB,IAAI,CAAC,KAAK,OAAO,KAAA;EACjB,MAAM,SAAS,kBAAkB,GAAG;EACpC,IACE,OAAO,WAAW,cAClB,IAAI,eAAe,WAAW,UAAU,GAExC,OAAO;GAAE,GAAG;GAAQ;EAAW;EAEjC,OAAO;CACT;AACF;AAEO,SAAS,qCACd,SACgC;CAChC,OAAO,IAAI,+BAA+B,OAAO;AACnD;AAEA,SAAS,WAAW,OAAuB;CACzC,OAAO,WAAW,QAAQ,CAAA,CAAE,OAAO,KAAK,CAAA,CAAE,OAAO,KAAK;AACxD;AAEA,SAAS,YAAY,OAAgB,OAAwC;CAC3E,IAAI,SAAS;CACb,IAAI,OAAO,UAAU,UACnB,IAAI;EACF,SAAS,KAAK,MAAM,KAAK;CAC3B,QAAQ;EACN,MAAM,IAAI,sCAAsC,KAAK;CACvD;CAEF,IAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAC/D,MAAM,IAAI,sCAAsC,KAAK;CAEvD,OAAO;AACT;AAEA,SAAS,kBACP,KAC8B;CAC9B,MAAM,qBAAqB,IAAI;CAC/B,IAAI,OAAO,uBAAuB,UAChC,MAAM,IAAI,sCAAsC,iBAAiB;CAEnE,IAAI,IAAI,WAAW,YAAY;EAC7B,MAAM,aAAa,OAAO,IAAI,WAAW;EACzC,IACE,OAAO,IAAI,eAAe,YAC1B,CAAC,OAAO,cAAc,UAAU,GAEhC,MAAM,IAAI,sCAAsC,iBAAiB;EAEnE,OAAO;GACL,QAAQ;GACR;GACA,YAAY;GACZ;EACF;CACF;CACA,IAAI,IAAI,WAAW,aACjB,MAAM,IAAI,sCAAsC,iBAAiB;CAEnE,MAAM,SAAS,aAAa,YAAY,IAAI,QAAQ,iBAAiB,CAAC;CACtE,MAAM,WACJ,IAAI,YAAY,OACZ,KAAA,IACA,iBAAiB,YAAY,IAAI,UAAU,iBAAiB,CAAC;CACnE,OAAO;EACL,QAAQ;EACR;EACA;EACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;CACjC;AACF;AAEA,SAAS,YACP,OAC+B;CAW/B,IACE,CAAC,OAAO,cAAc,MAAM,SAAS,KACrC,CAAC,OAAO,cAAc,MAAM,QAAQ,KACpC;EAZA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAKA,CAAA,CAAW,MAAM,QAAQ,OAAO,MAAM,SAAS,QAAQ,KACvD,CAAC;EAAC;EAAY;EAAoB;EAAmB;CAAY,CAAA,CAAE,OAChE,QAAQ,MAAM,SAAS,QAAQ,OAAO,MAAM,SAAS,QACxD,GAEA,MAAM,IAAI,sCAAsC,WAAW;CAE7D,OAAO;AACT;AAEA,SAAS,aAAa,OAAyD;CAC7E,IACE,MAAM,YAAY,KAClB,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,aAAa,YAC1B,MAAM,UAAU,WAChB,OAAO,MAAM,OAAO,aACpB,CAAC,MAAM,YACP,OAAO,MAAM,aAAa,YAC1B,MAAM,QAAQ,MAAM,QAAQ,KAC3B,MAAM,WAAW,KAAA,KAAa,OAAO,MAAM,WAAW,UAEvD,MAAM,IAAI,sCAAsC,iBAAiB;CAEnE,OAAO;AACT;AAEA,SAAS,iBACP,OACwC;CACxC,IACE,OAAO,MAAM,iBAAiB,YAC9B,OAAO,MAAM,aAAa,YAC1B,CAAC,OAAO,cAAc,MAAM,YAAY,GAExC,MAAM,IAAI,sCAAsC,iBAAiB;CAEnE,OAAO;AACT;AAEA,SAAS,mBAAkD;CACzD,OAAO;EACL,WAAW;EACX,aAAa;EACb,UAAU;EACV,kBAAkB;EAClB,iBAAiB;EACjB,YAAY;EACZ,aAAa;EACb,UAAU;EACV,mBAAmB;EACnB,UAAU;EACV,kBAAkB;EAClB,sBAAsB;EACtB,yBAAyB;EACzB,oBAAoB;CACtB;AACF;;;AC/aA,IAAM,gCAA6C,IAAI,IAAI;CACzD,CAAC,mBAAmB,GAAG;CACvB,CAAC,aAAa,GAAG;CACjB,CAAC,UAAU,GAAG;CACd,CAAC,eAAe,GAAG;CACnB,CAAC,2BAA2B,GAAG;CAC/B,CAAC,kBAAkB,GAAG;CACtB,CAAC,kBAAkB,GAAG;CACtB,CAAC,iBAAiB,GAAG;CACrB,CAAC,yBAAyB,GAAG;CAC7B,CAAC,yBAAyB,GAAG;CAC7B,CAAC,yBAAyB,GAAG;CAC7B,CAAC,mCAAmC,GAAG;CACvC,CAAC,wBAAwB,GAAG;CAC5B,CAAC,2BAA2B,GAAG;CAC/B,CAAC,0BAA0B,GAAG;AAChC,CAAC;AAED,SAAS,gBAAgB,QAAyC;CAChE,IAAI,OAAO,IAAI,OAAO;CACtB,OAAO,cAAc,IAAI,OAAO,UAAU,EAAE,KAAK;AACnD;AAUA,SAAS,yBAAyB,OAAoC;CACpE,IAAI,EAAE,iBAAiB,QAAQ,OAAO,KAAA;CACtC,MAAM,SAAU,MAAuC;CACvD,OAAO,OAAO,WAAW,YAAY,UAAU,OAAO,SAAS,MAC3D,SACA,KAAA;AACN;AAEA,SAAS,aACP,MACA,QACU;CACV,OAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;EACxC;EACA,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH;AAOA,SAAS,6BACP,SACA,UACS;CACT,MAAM,WAAW,QAAQ,QAAQ,IAAI,gBAAgB;CACrD,IAAI,aAAa,MAAM,OAAO;CAC9B,MAAM,QAAQ,OAAO,QAAQ;CAC7B,OAAO,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC5C;AAOA,eAAe,gBACb,SACA,UAC4B;CAC5B,MAAM,SAAS,QAAQ;CACvB,IAAI,CAAC,QAAQ,OAAO;EAAE,IAAI;EAAM,MAAM,MAAM,QAAQ,KAAK;CAAE;CAC3D,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,SAAuB,CAAC;CAC9B,IAAI,QAAQ;CACZ,SAAS;EACP,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,OAAO,KAAK;EAC3B,QAAQ;GACN,OAAO;IAAE,IAAI;IAAO,QAAQ;GAAkB;EAChD;EACA,IAAI,KAAK,QAAQ,CAAC,KAAK,OAAO;EAC9B,SAAS,KAAK,MAAM;EACpB,IAAI,QAAQ,UAAU;GACpB,MAAM,OAAO,OAAO,CAAA,CAAE,YAAY,KAAA,CAAS;GAC3C,OAAO;IAAE,IAAI;IAAO,QAAQ;GAAoB;EAClD;EACA,OAAO,KAAK,KAAK,KAAK;CACxB;CACA,MAAM,SAAS,IAAI,WAAW,KAAK;CACnC,IAAI,SAAS;CACb,KAAA,MAAW,SAAS,QAAQ;EAC1B,OAAO,IAAI,OAAO,MAAM;EACxB,UAAU,MAAM;CAClB;CACA,OAAO;EAAE,IAAI;EAAM,MAAM,IAAI,YAAY,OAAO,CAAA,CAAE,OAAO,MAAM;CAAE;AACnE;AAEA,SAAS,UAAU,MAA0D;CAC3E,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,OAAO,UAAU,OAAO,WAAW,WAAW,SAAS,KAAA;CACzD,QAAQ;EACN;CACF;AACF;AAYO,SAAS,qCACd,SAC0B;CAC1B,MAAM,EAAE,SAAS,kBAAkB,gBAAgB;CACnD,MAAM,kBACJ,QAAQ,mBAAmB;CAE7B,eAAe,OACb,SACA,OACmB;EACnB,IAAI,6BAA6B,SAAS,eAAe,GACvD,OAAO,aAAa,EAAE,OAAO,oBAAoB,GAAG,GAAG;EAEzD,IAAI;EACJ,IAAI;GACF,YAAY,MAAM,iBAAiB,OAAO;EAC5C,SAAS,OAAO;GACd,cAAc,OAAO,OAAO;GAC5B,OAAO,aAAa,EAAE,OAAO,eAAe,GAAG,GAAG;EACpD;EACA,MAAM,aAAa,MAAM,gBAAgB,SAAS,eAAe;EACjE,IAAI,CAAC,WAAW,IACd,OAAO,aACL,EAAE,OAAO,WAAW,OAAO,GAC3B,WAAW,WAAW,sBAAsB,MAAM,GACpD;EAEF,MAAM,OAAO,UAAU,WAAW,IAAI;EACtC,IAAI,CAAC,MACH,OAAO,aAAa,EAAE,OAAO,kBAAkB,GAAG,GAAG;EAEvD,IAAI;EACJ,IAAI;GACF,SACE,UAAU,YACN,MAAM,QAAQ,QAAQ,MAAM,EAAE,UAAU,CAAC,IACzC,MAAM,QAAQ,MAAM,MAAM,EAAE,UAAU,CAAC;EAC/C,SAAS,OAAO;GACd,MAAM,SAAS,yBAAyB,KAAK;GAC7C,IAAI,WAAW,KAAA,GAAW,MAAM;GAChC,OAAO,aACL;IACE,SAAS;IACT,WAAW,KAAK;IAChB,UAAU,KAAK;IACf,UAAU,KAAK;IACf;IACA,IAAI;IACJ,QAAQ;GACV,GACA,MACF;EACF;EACA,OAAO,aAAa,QAAQ,gBAAgB,MAAM,CAAC;CACrD;CAEA,OAAO;EACL,UAAU,YAAY,OAAO,SAAS,SAAS;EAC/C,QAAQ,YAAY,OAAO,SAAS,OAAO;CAC7C;AACF;AAgBA,eAAsB,uBACpB,cACA,UAC6B;CAC7B,MAAM,WAAW,MAAM,SAAS,OAAO,YAAY;CACnD,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,SAA6B,CAAC;CACpC,KAAA,MAAW,SAAS,UAAU;EAC5B,MAAM,MAAM,GAAG,OAAO,MAAK,GAAI,OAAO,KAAK;EAC3C,IAAI,KAAK,IAAI,GAAG,GAAG;EACnB,KAAK,IAAI,GAAG;EACZ,OAAO,KAAK,KAAK;CACnB;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"server.js","names":["result"],"sources":["../src/server/api-routes.ts","../src/server/config-loader.ts","../src/server/data-surface-actions.ts","../src/server/jobs-data-surface-action-queue.ts","../src/server/serialization.ts","../src/server/sql-data-surface-action-state.ts","../src/server/sveltekit-data-surface-routes.ts"],"sourcesContent":["/**\n * Server-side API route resolution for SMRT agents\n *\n * Reads agent package manifests and builds a route map from resource\n * paths (e.g., 'performers', 'video-contents') to SmrtObject class\n * names and allowed CRUD actions. The catch-all API handler uses this\n * to resolve incoming requests.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport type { PackageManifest } from './manifest-utils.js';\n\n/**\n * Info about a single API route (one SmrtObject with api.include)\n */\nexport interface AgentAPIRouteInfo {\n /** SmrtObject class name (e.g., 'Performer') */\n className: string;\n /** Allowed CRUD actions (e.g., ['list', 'get', 'create', 'update', 'delete']) */\n allowedActions: string[];\n /** Package that owns this resource */\n packageName?: string;\n}\n\n/**\n * Result of resolving a URL path against the route map\n */\nexport interface ResolvedAPIRoute {\n /** The matched route info */\n route: AgentAPIRouteInfo;\n /** Resource ID if path includes one (e.g., 'performers/abc-123') */\n id?: string;\n /** Custom action name if path includes one (e.g., 'performers/abc-123/generate-image') */\n action?: string;\n}\n\n/**\n * Build a route map from loaded package manifests.\n *\n * Iterates all objects in each manifest, and for any object with a\n * `decoratorConfig.api.include` array, registers a route. The route\n * path is derived from `decoratorConfig.api.path` if set, otherwise\n * from the table name with underscores converted to hyphens.\n *\n * @param manifests - Array of parsed package manifest JSON objects\n * @returns Map of resource path -> route info\n *\n * @example\n * ```typescript\n * const manifests = [histrioManifest, praecoManifest];\n * const routes = buildRouteMap(manifests);\n * // routes.get('performers') => { className: 'Performer', allowedActions: ['list', 'get', 'create', 'update', 'delete'] }\n * // routes.get('video-contents') => { className: 'VideoShot', allowedActions: ['list', 'get', 'create', 'update'] }\n * ```\n */\nexport function buildRouteMap(\n manifests: PackageManifest[],\n): Map<string, AgentAPIRouteInfo> {\n const routes = new Map<string, AgentAPIRouteInfo>();\n\n for (const manifest of manifests) {\n const packageName = (manifest as Record<string, unknown>).packageName as\n | string\n | undefined;\n\n for (const obj of Object.values(manifest.objects)) {\n const config = obj.decoratorConfig as Record<string, unknown> | undefined;\n if (!config) continue;\n\n const api = config.api as\n | { include?: string[]; path?: string }\n | undefined;\n if (!api?.include || api.include.length === 0) continue;\n\n // Derive the URL path: explicit api.path, or table name with _ -> -\n const tableName = config.tableName as string | undefined;\n const path =\n api.path || (tableName ? tableName.replace(/_/g, '-') : null);\n if (!path) continue;\n\n routes.set(path, {\n className: obj.className,\n allowedActions: api.include,\n packageName,\n });\n }\n }\n\n return routes;\n}\n\n/**\n * Resolve a URL resource path against a route map.\n *\n * Handles three URL patterns:\n * - `performers` → list/create (no id)\n * - `performers/abc-123` → get/update/delete (with id)\n * - `performers/abc-123/generate-image` → custom action\n *\n * @param urlPath - The resource portion of the URL (after `/api/agents/{agentId}/`)\n * @param routes - Route map from {@link buildRouteMap}\n * @returns Resolved route with optional id/action, or null if no match\n */\nexport function resolveAPIRoute(\n urlPath: string,\n routes: Map<string, AgentAPIRouteInfo>,\n): ResolvedAPIRoute | null {\n // Normalize: strip leading/trailing slashes\n const normalized = urlPath.replace(/^\\/+|\\/+$/g, '');\n if (!normalized) return null;\n\n const segments = normalized.split('/');\n\n // Try 1-segment: \"performers\"\n if (segments.length === 1) {\n const route = routes.get(segments[0]);\n if (route) return { route };\n return null;\n }\n\n // Try 2-segment: \"performers/{id}\"\n if (segments.length === 2) {\n const route = routes.get(segments[0]);\n if (route) return { route, id: segments[1] };\n return null;\n }\n\n // Try 3-segment: \"performers/{id}/{action}\"\n if (segments.length === 3) {\n const route = routes.get(segments[0]);\n if (route) return { route, id: segments[1], action: segments[2] };\n return null;\n }\n\n return null;\n}\n","/**\n * Server-side agent config loading utilities\n *\n * Loads slot configurations from the agent_configs table for a set of agents.\n * Agent-specific table loading (e.g., praeco_sources) stays in the host app.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { AgentConfig } from '../config.js';\n\n/**\n * Load slot configs for multiple agents from the agent_configs table.\n *\n * Returns a nested map: agentId -> slotId -> configData.\n * Agent-specific tables (e.g., praeco_sources, praeco_reports)\n * are NOT loaded here — those stay in the host application.\n *\n * @param agents - Array of agent identifiers (id + agentClass)\n * @param dbOptions - Database options for SmrtCollection.create()\n * @returns Map of agentId -> slotId -> config data\n */\nexport async function loadSlotConfigs(\n agents: Array<{ id: string; agentClass: string }>,\n dbOptions: SmrtClassOptions,\n): Promise<Record<string, Record<string, unknown>>> {\n if (agents.length === 0) {\n return {};\n }\n\n try {\n const configsByAgent = await AgentConfig.forAgents(\n agents.map((agent) => agent.id),\n dbOptions,\n );\n\n const configs: Record<string, Record<string, unknown>> = {};\n for (const [agentId, slotConfigs] of configsByAgent) {\n const agentConfig: Record<string, unknown> = {};\n for (const [slotId, configData] of slotConfigs) {\n agentConfig[slotId] = configData;\n }\n if (Object.keys(agentConfig).length > 0) {\n configs[agentId] = agentConfig;\n }\n }\n\n return configs;\n } catch (error) {\n if (isMissingAgentConfigTableError(error)) {\n return {};\n }\n throw error;\n }\n}\n\nfunction isMissingAgentConfigTableError(error: unknown): boolean {\n const message = String((error as Error)?.message || error || '');\n\n return (\n message.includes(\"Run 'smrt db:migrate'\") ||\n /no such table[:\\s]+agent_configs/i.test(message) ||\n /relation .*agent_configs.*does not exist/i.test(message) ||\n /table .*agent_configs.*doesn'?t exist/i.test(message)\n );\n}\n","/**\n * Principal-bound preview/apply orchestration for data-surface actions.\n *\n * Browser state is treated only as an input hint. Every preview and apply is\n * executed under the bound principal, resolves the surface and selection\n * afresh, and delegates durable work only after authorization and eligibility\n * checks have passed.\n */\nimport { createHash, randomBytes } from 'node:crypto';\nimport {\n createHmacDurableJobPayloadSigner,\n type DurableJobPayloadIntegrity,\n} from '@happyvertical/smrt-jobs';\nimport type {\n DataSurfaceActionDescriptor,\n DataSurfaceActionResult,\n DataSurfaceActionRowOutcome,\n DataSurfaceActionWireRequest,\n DataSurfaceDescriptor,\n DataSurfaceIdentity,\n DataSurfaceJsonObject,\n DataSurfaceJsonValue,\n DataSurfaceRowId,\n DataSurfaceSelectionReference,\n} from '@happyvertical/smrt-types';\nimport {\n type ExecuteAsPrincipalOptions,\n executeAsPrincipal,\n type PrincipalRun,\n} from '../execute-as-principal.js';\n\nexport type DataSurfaceConfirmationPolicy = 'required' | 'none';\nexport type DataSurfaceActionExecution = 'foreground' | 'background';\n\nexport interface DataSurfaceActionEligibility {\n eligible: boolean;\n reason?: string;\n}\n\nexport type DataSurfaceActionPayloadValidation =\n | { valid: true }\n | { valid: false; reason?: string };\n\nexport interface ResolvedDataSurfaceSelection {\n /** Fresh server-side revision of the selected surface/query. */\n revision: number;\n /** Canonical fingerprint of the frozen query represented by the selection. */\n queryFingerprint: string;\n /** Authoritatively resolved row ids. Browser-provided ids are only hints. */\n rowIds: DataSurfaceRowId[];\n}\n\nexport interface DataSurfaceActionInvocation {\n run: PrincipalRun;\n request: DataSurfaceServerActionRequest;\n descriptor: DataSurfaceDescriptor;\n action: DataSurfaceServerActionDefinition;\n selection: ResolvedDataSurfaceSelection;\n}\n\nexport interface DataSurfaceServerActionDefinition {\n descriptor: DataSurfaceActionDescriptor;\n /** Serializable declaration for transport/schema generators; null means no input. */\n inputSchema: DataSurfaceJsonObject | null;\n /** Runtime enforcement for the declared schema; absence is never permissive. */\n validatePayload(\n payload: DataSurfaceJsonValue | undefined,\n ):\n | DataSurfaceActionPayloadValidation\n | Promise<DataSurfaceActionPayloadValidation>;\n /** Explicit for every action, including sensitive/public/destructive ones. */\n confirmation: DataSurfaceConfirmationPolicy;\n execution: DataSurfaceActionExecution;\n /** Fail-closed persona capability checked by PrincipalRun. */\n tool: string;\n /** Explicit RBAC catalog gate, enforced independently of callback convention. */\n operation: {\n id: string;\n collection: Parameters<PrincipalRun['assertOperation']>[0];\n action: string;\n };\n /** Fresh permission/domain authorization check, run for preview and apply. */\n authorize(\n invocation: DataSurfaceActionInvocation,\n ): boolean | Promise<boolean>;\n /** Fresh per-row domain precondition check, repeated at apply time. */\n eligible(\n invocation: DataSurfaceActionInvocation,\n rowId: DataSurfaceRowId,\n ): DataSurfaceActionEligibility | Promise<DataSurfaceActionEligibility>;\n /** Foreground mutation. Background definitions are run by the injected queue. */\n apply(\n invocation: DataSurfaceActionInvocation,\n rowId: DataSurfaceRowId,\n ):\n | undefined\n | DataSurfaceJsonValue\n | Promise<undefined | DataSurfaceJsonValue>;\n}\n\nexport interface ResolvedDataSurfaceActions {\n descriptor: DataSurfaceDescriptor;\n /** Current server-side revision, never trusted from the browser. */\n revision: number;\n actions: Record<string, DataSurfaceServerActionDefinition>;\n}\n\nexport interface DataSurfaceServerActionRequest\n extends DataSurfaceActionWireRequest {}\n\nexport interface DataSurfaceActionContext {\n principal: ExecuteAsPrincipalOptions;\n}\n\nexport interface DataSurfaceDeferredPrincipalReference {\n runAsUserId: string;\n tenantId: string | null;\n actsAsProfileId: string | null;\n onBehalfOfUserId: string | null;\n agentClass?: string;\n}\n\n/** Serializable, versioned payload persisted by a durable background queue. */\nexport interface DataSurfaceBackgroundActionEnvelope {\n version: 1;\n handlerId: string;\n request: DataSurfaceServerActionRequest;\n principal: DataSurfaceDeferredPrincipalReference;\n previewToken?: DataSurfacePreviewTokenRecord;\n /** Server-authenticated binding over every other persisted envelope field. */\n binding: DurableJobPayloadIntegrity;\n}\n\nexport interface DataSurfaceBackgroundActionJob {\n idempotencyKey: string;\n identity: DataSurfaceIdentity;\n actionId: string;\n rowIds: DataSurfaceRowId[];\n envelope: DataSurfaceBackgroundActionEnvelope;\n /**\n * The queue must call this task to perform the work. It re-enters the bound\n * principal and repeats descriptor, authorization, selection, and eligibility\n * checks before any mutation.\n */\n run: () => Promise<DataSurfaceActionResult>;\n}\n\nexport interface DataSurfaceBackgroundQueue {\n enqueue(\n job: DataSurfaceBackgroundActionJob,\n ): Promise<{ jobId: string; details?: DataSurfaceJsonObject }>;\n}\n\nexport interface DataSurfacePreviewTokenRecord {\n expiresAt: number;\n actorUserId: string;\n tenantId: string | null;\n onBehalfOfUserId: string | null;\n actsAsProfileId: string | null;\n agentClass: string | null;\n identityKey: string;\n actionId: string;\n actionFingerprint: string;\n revision: number;\n queryFingerprint: string;\n selectionFingerprint: string;\n resolvedRowsFingerprint: string;\n requestFingerprint: string;\n consumedBy?: string;\n}\n\nexport type DataSurfaceIdempotencyRecord =\n | {\n status: 'reserved';\n requestFingerprint: string;\n ownerToken: string;\n reservedAt: number;\n }\n | {\n status: 'completed';\n requestFingerprint: string;\n result: DataSurfaceActionResult;\n recovery?: DataSurfaceIdempotencyRecoveryEvidence;\n };\n\nexport interface DataSurfaceIdempotencyRecoveryEvidence {\n authorizedBy: string;\n evidence: string;\n reconciledAt: number;\n}\n\nexport interface DataSurfaceIdempotencyReservation {\n requestFingerprint: string;\n ownerToken: string;\n reservedAt: number;\n}\n\nexport interface DataSurfaceActionStateStore {\n putToken(\n token: string,\n record: DataSurfacePreviewTokenRecord,\n ): Promise<void> | void;\n getToken(\n token: string,\n ):\n | Promise<DataSurfacePreviewTokenRecord | undefined>\n | DataSurfacePreviewTokenRecord\n | undefined;\n markTokenConsumed(\n token: string,\n idempotencyKey: string,\n ): Promise<boolean> | boolean;\n /** Atomically consume a preview token and create/read its apply reservation. */\n consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ):\n | Promise<DataSurfaceIdempotencyRecord | undefined>\n | DataSurfaceIdempotencyRecord\n | undefined;\n getIdempotency(\n key: string,\n ):\n | Promise<DataSurfaceIdempotencyRecord | undefined>\n | DataSurfaceIdempotencyRecord\n | undefined;\n /** Atomically create a durable reservation or return the existing record. */\n reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord> | DataSurfaceIdempotencyRecord;\n completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): Promise<boolean> | boolean;\n releaseIdempotency(\n key: string,\n ownerToken: string,\n ): Promise<boolean> | boolean;\n}\n\n/** Explicit single-process/testing store; production callers inject shared state. */\nexport class InMemoryDataSurfaceActionStateStore\n implements DataSurfaceActionStateStore\n{\n private readonly tokens = new Map<string, DataSurfacePreviewTokenRecord>();\n private readonly idempotency = new Map<\n string,\n DataSurfaceIdempotencyRecord\n >();\n\n putToken(token: string, record: DataSurfacePreviewTokenRecord): void {\n this.tokens.set(token, record);\n }\n\n getToken(token: string): DataSurfacePreviewTokenRecord | undefined {\n return this.tokens.get(token);\n }\n\n markTokenConsumed(token: string, idempotencyKey: string): boolean {\n const record = this.tokens.get(token);\n if (!record) return false;\n if (record.consumedBy && record.consumedBy !== idempotencyKey) return false;\n record.consumedBy = idempotencyKey;\n return true;\n }\n\n consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): DataSurfaceIdempotencyRecord | undefined {\n const tokenRecord = this.tokens.get(token);\n if (\n !tokenRecord ||\n (tokenRecord.consumedBy && tokenRecord.consumedBy !== idempotencyKey)\n ) {\n return undefined;\n }\n const existing = this.idempotency.get(scope);\n if (!existing) {\n this.idempotency.set(scope, { status: 'reserved', ...reservation });\n }\n tokenRecord.consumedBy = idempotencyKey;\n return this.idempotency.get(scope);\n }\n\n getIdempotency(key: string): DataSurfaceIdempotencyRecord | undefined {\n return this.idempotency.get(key);\n }\n\n reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): DataSurfaceIdempotencyRecord {\n const existing = this.idempotency.get(key);\n if (existing) return existing;\n const record: DataSurfaceIdempotencyRecord = {\n status: 'reserved',\n ...reservation,\n };\n this.idempotency.set(key, record);\n return record;\n }\n\n completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): boolean {\n const existing = this.idempotency.get(key);\n if (existing?.status !== 'reserved' || existing.ownerToken !== ownerToken)\n return false;\n this.idempotency.set(key, {\n status: 'completed',\n requestFingerprint: existing.requestFingerprint,\n result,\n });\n return true;\n }\n\n releaseIdempotency(key: string, ownerToken: string): boolean {\n const existing = this.idempotency.get(key);\n if (existing?.status !== 'reserved' || existing.ownerToken !== ownerToken)\n return false;\n return this.idempotency.delete(key);\n }\n}\n\nexport interface DataSurfaceActionAdapterOptions {\n resolveSurface(\n run: PrincipalRun,\n identity: DataSurfaceIdentity,\n ): Promise<ResolvedDataSurfaceActions>;\n resolveSelection(\n invocation: Omit<DataSurfaceActionInvocation, 'selection'>,\n selection: DataSurfaceSelectionReference,\n ): Promise<ResolvedDataSurfaceSelection>;\n backgroundQueue?: DataSurfaceBackgroundQueue;\n /** Stable worker registration key required by durable background queues. */\n backgroundHandlerId?: string;\n /** Secret used only in-process to authenticate durable background envelopes. */\n deferredEnvelopeSigningKey?: string | Uint8Array;\n /** Required durable, shared backend in production; memory storage is opt-in. */\n state: DataSurfaceActionStateStore;\n tokenTtlMs?: number;\n now?: () => number;\n createToken?: () => string;\n runAsPrincipal?: typeof executeAsPrincipal;\n /**\n * Re-resolve the complete current binding immediately before deferred work.\n * Background execution fails closed when this seam is absent or returns a\n * binding for a different principal.\n */\n resolveDeferredPrincipal?(\n reference: Readonly<{\n runAsUserId: string;\n tenantId: string | null;\n actsAsProfileId: string | null;\n onBehalfOfUserId: string | null;\n agentClass?: string;\n }>,\n ): ExecuteAsPrincipalOptions | Promise<ExecuteAsPrincipalOptions>;\n idempotencyPollIntervalMs?: number;\n idempotencyWaitTimeoutMs?: number;\n /** Domain-specific request input that must participate in confirmation/idempotency. */\n requestFingerprintExtension?(\n request: DataSurfaceServerActionRequest,\n ): DataSurfaceJsonValue | undefined;\n /** Maps terminal domain failures; return undefined to preserve queue retries. */\n mapError?(\n error: unknown,\n request: DataSurfaceServerActionRequest,\n ): string | undefined;\n}\n\nexport interface DataSurfaceActionAdapter {\n preview(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult>;\n apply(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult>;\n /** Worker entry point; re-resolves current authority before any mutation. */\n executeDeferred(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult>;\n}\n\nconst DEFAULT_TOKEN_TTL_MS = 5 * 60 * 1_000;\nconst MAX_IDENTIFIER_LENGTH = 256;\nconst MAX_JSON_DEPTH = 16;\nconst MAX_JSON_ITEMS = 1_000;\nconst FORBIDDEN_JSON_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction isBoundedJsonValue(\n value: unknown,\n depth = 0,\n seen = new Set<object>(),\n): value is DataSurfaceJsonValue {\n if (value === null) return true;\n if (['string', 'boolean'].includes(typeof value)) return true;\n if (typeof value === 'number') return Number.isFinite(value);\n if (typeof value !== 'object' || depth >= MAX_JSON_DEPTH || seen.has(value))\n return false;\n seen.add(value);\n if (Array.isArray(value)) {\n if (value.length > MAX_JSON_ITEMS) return false;\n return value.every((item) => isBoundedJsonValue(item, depth + 1, seen));\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) return false;\n const entries = Object.entries(value);\n if (entries.length > MAX_JSON_ITEMS) return false;\n return entries.every(\n ([key, item]) =>\n !FORBIDDEN_JSON_KEYS.has(key) &&\n isBoundedJsonValue(item, depth + 1, seen),\n );\n}\n\n/** Validates untrusted extension values before they enter canonical hashing. */\nexport function isBoundedDataSurfaceJsonValue(\n value: unknown,\n): value is DataSurfaceJsonValue {\n return isBoundedJsonValue(value);\n}\n\nfunction validIdentifier(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_IDENTIFIER_LENGTH\n );\n}\n\nfunction validSelection(\n selection: unknown,\n): selection is DataSurfaceSelectionReference {\n if (!selection || typeof selection !== 'object') return false;\n const candidate = selection as Record<string, unknown>;\n if (candidate.scope === 'current-page') return true;\n if (candidate.scope === 'all-matching')\n return validIdentifier(candidate.queryFingerprint);\n if (candidate.scope !== 'explicit-ids' || !Array.isArray(candidate.rowIds))\n return false;\n if (candidate.rowIds.length > MAX_JSON_ITEMS) return false;\n return candidate.rowIds.every(\n (rowId) =>\n (typeof rowId === 'string' && rowId.length > 0) ||\n (typeof rowId === 'number' && Number.isFinite(rowId)),\n );\n}\n\nfunction stable(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;\n return `{${Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`)\n .join(',')}}`;\n}\n\nfunction fingerprint(value: unknown): string {\n return createHash('sha256').update(stable(value)).digest('hex');\n}\n\nfunction envelopeBinding(\n envelope: Omit<DataSurfaceBackgroundActionEnvelope, 'binding'>,\n key: string | Uint8Array,\n): DurableJobPayloadIntegrity {\n return createHmacDurableJobPayloadSigner({\n keyId: 'data-surface-envelope-v1',\n key,\n }).sign(envelope);\n}\n\nfunction validSigningKey(key: string | Uint8Array | undefined): boolean {\n return (\n key !== undefined &&\n (typeof key === 'string' ? Buffer.byteLength(key) : key.byteLength) >= 32\n );\n}\n\nfunction bindingMatches(\n envelope: DataSurfaceBackgroundActionEnvelope,\n key: string | Uint8Array,\n): boolean {\n const { binding, ...unsigned } = envelope;\n return createHmacDurableJobPayloadSigner({\n keyId: 'data-surface-envelope-v1',\n key,\n }).verify(unsigned, binding);\n}\n\nfunction identityKey(identity: DataSurfaceIdentity): string {\n return stable(canonicalIdentity(identity));\n}\n\nfunction canonicalIdentity(identity: DataSurfaceIdentity): DataSurfaceIdentity {\n return {\n kind: identity.kind,\n surfaceId: identity.surfaceId,\n ...(identity.subject\n ? {\n subject: {\n type: identity.subject.type,\n id: identity.subject.id,\n },\n }\n : {}),\n };\n}\n\nfunction rowIdKey(rowId: DataSurfaceRowId): string {\n return `${typeof rowId}:${String(rowId)}`;\n}\n\nfunction compareRowIds(\n left: DataSurfaceRowId,\n right: DataSurfaceRowId,\n): number {\n if (typeof left !== typeof right) return typeof left === 'number' ? -1 : 1;\n if (typeof left === 'number' && typeof right === 'number')\n return left - right;\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction canonicalRowIds(\n rowIds: readonly DataSurfaceRowId[],\n): DataSurfaceRowId[] {\n const ids = new Map<string, DataSurfaceRowId>();\n for (const rowId of rowIds) ids.set(rowIdKey(rowId), rowId);\n return [...ids.values()].sort(compareRowIds);\n}\n\nfunction canonicalSelection(\n selection: DataSurfaceSelectionReference,\n): DataSurfaceSelectionReference {\n if (selection.scope !== 'explicit-ids') return selection;\n return { scope: selection.scope, rowIds: canonicalRowIds(selection.rowIds) };\n}\n\nfunction requestFingerprint(\n request: DataSurfaceServerActionRequest,\n extension?: DataSurfaceJsonValue,\n): string {\n return fingerprint({\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n selection: canonicalSelection(request.selection),\n payload: request.payload,\n expectedRevision: request.expectedRevision,\n ...(extension === undefined ? {} : { extension }),\n });\n}\n\nfunction deepFreeze<T>(value: T, seen = new WeakSet<object>()): T {\n if (!value || typeof value !== 'object' || seen.has(value)) return value;\n seen.add(value);\n for (const nested of Object.values(value)) deepFreeze(nested, seen);\n Object.freeze(value);\n return value;\n}\n\nfunction snapshotRequest(\n request: DataSurfaceServerActionRequest,\n): DataSurfaceServerActionRequest {\n return deepFreeze(structuredClone(request));\n}\n\nfunction snapshotActionContext(\n context: DataSurfaceActionContext,\n): DataSurfaceActionContext {\n const principal = {\n ...context.principal.principal,\n ...(context.principal.principal.allowedTools\n ? { allowedTools: [...context.principal.principal.allowedTools] }\n : {}),\n };\n if (principal.allowedTools) Object.freeze(principal.allowedTools);\n Object.freeze(principal);\n const permissions = context.principal.permissions\n ? [...context.principal.permissions]\n : undefined;\n if (permissions) Object.freeze(permissions);\n const auditMetadata = context.principal.auditMetadata\n ? deepFreeze(structuredClone(context.principal.auditMetadata))\n : undefined;\n const principalOptions: ExecuteAsPrincipalOptions = {\n ...context.principal,\n principal,\n ...(permissions ? { permissions } : {}),\n ...(auditMetadata ? { auditMetadata } : {}),\n };\n Object.freeze(principalOptions);\n return Object.freeze({ principal: principalOptions });\n}\n\nfunction actionFingerprint(action: DataSurfaceServerActionDefinition): string {\n return fingerprint({\n descriptor: action.descriptor,\n inputSchema: action.inputSchema,\n confirmation: action.confirmation,\n execution: action.execution,\n tool: action.tool,\n operationId: action.operation.id,\n operationCollection: action.operation.collection,\n operationAction: action.operation.action,\n });\n}\n\nfunction result(\n request: DataSurfaceServerActionRequest,\n ok: boolean,\n reason?: string,\n details?: DataSurfaceJsonObject,\n confirmationToken?: string,\n): DataSurfaceActionResult {\n return {\n version: 1,\n requestId: request.requestId,\n identity: request.identity,\n actionId: request.actionId,\n phase: request.phase,\n ok,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n ...(confirmationToken ? { confirmationToken } : {}),\n };\n}\n\nfunction replayResult(\n request: DataSurfaceServerActionRequest,\n stored: DataSurfaceActionResult,\n): DataSurfaceActionResult {\n // Idempotency keys identify one logical execution, but each transport retry\n // has its own correlation id. Preserve the stored outcome while binding the\n // replay envelope to the request that is receiving it.\n return { ...stored, requestId: request.requestId };\n}\n\nfunction outcomesDetails(\n outcomes: DataSurfaceActionRowOutcome[],\n extra: DataSurfaceJsonObject = {},\n): DataSurfaceJsonObject {\n const accepted = outcomes.filter(\n ({ status }) => status === 'accepted',\n ).length;\n const skipped = outcomes.filter(({ status }) => status === 'skipped').length;\n const failed = outcomes.filter(({ status }) => status === 'failed').length;\n return {\n accepted,\n skipped,\n failed,\n outcomes: outcomes.map(({ metadata, ...outcome }) => ({\n ...(metadata ?? {}),\n ...outcome,\n })),\n ...extra,\n };\n}\n\nfunction validateRequest(\n request: DataSurfaceServerActionRequest,\n phase: 'preview' | 'apply',\n): string | undefined {\n if (!request || typeof request !== 'object') return 'invalid_request';\n if (request.version !== 1 || request.phase !== phase)\n return 'invalid_request';\n if (\n !validIdentifier(request.requestId) ||\n !validIdentifier(request.actionId) ||\n !validIdentifier(request.identity?.surfaceId) ||\n !['table', 'list', 'report', 'custom'].includes(request.identity?.kind) ||\n !validSelection(request.selection) ||\n (request.payload !== undefined && !isBoundedJsonValue(request.payload))\n )\n return 'invalid_request';\n if (\n !Number.isSafeInteger(request.expectedRevision) ||\n request.expectedRevision < 0\n )\n return 'invalid_request';\n if (\n phase === 'apply' &&\n (!validIdentifier(request.idempotencyKey) ||\n (request.confirmationToken !== undefined &&\n !validIdentifier(request.confirmationToken)))\n )\n return 'invalid_request';\n return undefined;\n}\n\n/** Create a transport-neutral, principal-bound data-surface action adapter. */\nexport function createDataSurfaceActionAdapter(\n options: DataSurfaceActionAdapterOptions,\n): DataSurfaceActionAdapter {\n const state = options.state;\n const now = options.now ?? Date.now;\n const createToken =\n options.createToken ?? (() => randomBytes(32).toString('base64url'));\n const tokenTtlMs = options.tokenTtlMs ?? DEFAULT_TOKEN_TTL_MS;\n const runAsPrincipal = options.runAsPrincipal ?? executeAsPrincipal;\n const idempotencyPollIntervalMs = Math.max(\n 1,\n options.idempotencyPollIntervalMs ?? 10,\n );\n const idempotencyWaitTimeoutMs = Math.max(\n 0,\n options.idempotencyWaitTimeoutMs ?? 5_000,\n );\n const fingerprintRequest = (request: DataSurfaceServerActionRequest) =>\n requestFingerprint(request, options.requestFingerprintExtension?.(request));\n\n async function resolveInvocation(\n request: DataSurfaceServerActionRequest,\n run: PrincipalRun,\n ): Promise<DataSurfaceActionInvocation | DataSurfaceActionResult> {\n const surface = await options.resolveSurface(run, request.identity);\n if (\n identityKey(surface.descriptor.identity) !== identityKey(request.identity)\n ) {\n return result(request, false, 'not_found');\n }\n const action = surface.actions[request.actionId];\n const declared = surface.descriptor.actions.find(\n ({ id }) => id === request.actionId,\n );\n if (\n !action ||\n !declared ||\n action.descriptor.id !== declared.id ||\n Boolean(declared.requiresConfirmation) !==\n (action.confirmation === 'required')\n ) {\n return result(request, false, 'unsupported');\n }\n if (\n !action.tool ||\n !validIdentifier(action.operation?.id) ||\n !validIdentifier(action.operation?.action)\n )\n return result(request, false, 'denied');\n run.assertToolAllowed(action.tool);\n await run.assertOperation(\n action.operation.collection,\n action.operation.action,\n );\n const payloadValidation = await action.validatePayload(request.payload);\n if (!payloadValidation.valid)\n return result(\n request,\n false,\n payloadValidation.reason ?? 'invalid_payload',\n );\n if (\n !declared.selectionScopes.includes(request.selection.scope) ||\n !action.descriptor.selectionScopes.includes(request.selection.scope)\n ) {\n return result(request, false, 'selection_not_supported');\n }\n const base = {\n run,\n request,\n descriptor: surface.descriptor,\n action,\n };\n const resolvedSelection = await options.resolveSelection(\n base,\n canonicalSelection(request.selection),\n );\n const selection = {\n ...resolvedSelection,\n rowIds: canonicalRowIds(resolvedSelection.rowIds),\n };\n const invocation = { ...base, selection };\n if (!(await action.authorize(invocation))) {\n return result(request, false, 'denied');\n }\n if (selection.rowIds.length > surface.descriptor.limits.maxSelectionSize) {\n return result(request, false, 'limit_exceeded');\n }\n return invocation;\n }\n\n async function preview(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult> {\n const invalid = validateRequest(request, 'preview');\n if (invalid) return result(request, false, invalid);\n const boundContext = snapshotActionContext(context);\n return runAsPrincipal(\n {\n ...boundContext.principal,\n action: 'data_surface.action.preview',\n auditMetadata: {\n ...boundContext.principal.auditMetadata,\n surfaceId: request.identity.surfaceId,\n actionId: request.actionId,\n requestId: request.requestId,\n },\n },\n async (run) => {\n const invocation = await resolveInvocation(request, run);\n if ('ok' in invocation) return invocation;\n if (invocation.selection.revision !== request.expectedRevision) {\n return result(request, false, 'stale_revision');\n }\n const outcomes: DataSurfaceActionRowOutcome[] = [];\n for (const rowId of invocation.selection.rowIds) {\n const eligibility = await invocation.action.eligible(\n invocation,\n rowId,\n );\n outcomes.push({\n rowId,\n status: eligibility.eligible ? 'accepted' : 'skipped',\n ...(eligibility.reason ? { reason: eligibility.reason } : {}),\n });\n }\n const confirmationToken = createToken();\n const selectionFingerprint = fingerprint(\n canonicalSelection(request.selection),\n );\n const requestFingerprintValue = fingerprintRequest(request);\n const expiresAt = now() + tokenTtlMs;\n await state.putToken(confirmationToken, {\n expiresAt,\n actorUserId: boundContext.principal.principal.runAsUserId,\n tenantId: boundContext.principal.principal.tenantId,\n onBehalfOfUserId: boundContext.principal.onBehalfOfUserId ?? null,\n actsAsProfileId:\n boundContext.principal.principal.actsAsProfileId ?? null,\n agentClass: boundContext.principal.agentClass ?? null,\n identityKey: identityKey(request.identity),\n actionId: request.actionId,\n actionFingerprint: actionFingerprint(invocation.action),\n revision: invocation.selection.revision,\n queryFingerprint: invocation.selection.queryFingerprint,\n selectionFingerprint,\n resolvedRowsFingerprint: fingerprint(\n canonicalRowIds(invocation.selection.rowIds),\n ),\n requestFingerprint: requestFingerprintValue,\n });\n return result(\n request,\n true,\n undefined,\n outcomesDetails(outcomes, {\n count: invocation.selection.rowIds.length,\n revision: invocation.selection.revision,\n queryFingerprint: invocation.selection.queryFingerprint,\n expiresAt,\n }),\n confirmationToken,\n );\n },\n );\n }\n\n async function executeForeground(\n request: DataSurfaceServerActionRequest,\n invocation: DataSurfaceActionInvocation,\n ): Promise<DataSurfaceActionResult> {\n const outcomes: DataSurfaceActionRowOutcome[] = [];\n for (const rowId of invocation.selection.rowIds) {\n try {\n const eligibility = await invocation.action.eligible(invocation, rowId);\n if (!eligibility.eligible) {\n outcomes.push({\n rowId,\n status: 'skipped',\n ...(eligibility.reason ? { reason: eligibility.reason } : {}),\n });\n continue;\n }\n const applied = await invocation.action.apply(invocation, rowId);\n outcomes.push({\n rowId,\n status: 'accepted',\n ...(applied !== null &&\n typeof applied === 'object' &&\n !Array.isArray(applied)\n ? { metadata: applied }\n : {}),\n });\n } catch (error) {\n outcomes.push({\n rowId,\n status: 'failed',\n reason: options.mapError?.(error, request) ?? 'execution_failed',\n });\n }\n }\n return result(request, true, undefined, outcomesDetails(outcomes));\n }\n\n async function executeBackgroundOnce(\n request: DataSurfaceServerActionRequest,\n token: DataSurfacePreviewTokenRecord | undefined,\n reference: Readonly<DataSurfaceDeferredPrincipalReference>,\n ): Promise<DataSurfaceActionResult> {\n const ownerToken = randomBytes(16).toString('base64url');\n const executionFingerprint = fingerprint({\n kind: 'background-execution',\n request: token?.requestFingerprint ?? fingerprintRequest(request),\n action: token?.actionFingerprint ?? request.actionId,\n });\n const executionScope = fingerprint({\n kind: 'background-execution',\n actorUserId: reference.runAsUserId,\n tenantId: reference.tenantId,\n onBehalfOfUserId: reference.onBehalfOfUserId,\n actsAsProfileId: reference.actsAsProfileId,\n agentClass: reference.agentClass ?? null,\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n idempotencyKey: request.idempotencyKey,\n });\n const maxPolls = Math.max(\n 1,\n Math.ceil(idempotencyWaitTimeoutMs / idempotencyPollIntervalMs),\n );\n for (let poll = 0; poll <= maxPolls; poll += 1) {\n const winner = await state.reserveIdempotency(executionScope, {\n requestFingerprint: executionFingerprint,\n ownerToken,\n reservedAt: now(),\n });\n if (winner.requestFingerprint !== executionFingerprint)\n return result(request, false, 'idempotency_conflict');\n if (winner.status === 'completed')\n return replayResult(request, winner.result);\n if (winner.ownerToken === ownerToken) {\n let refreshed: ExecuteAsPrincipalOptions;\n try {\n // A queued job may run long after the request that created it. The\n // complete persona binding (including the TenantAgent-capped tool\n // allow-list) must therefore be resolved again at execution time.\n const resolved = await options.resolveDeferredPrincipal?.(reference);\n if (\n !resolved ||\n resolved.principal.runAsUserId !== reference.runAsUserId ||\n resolved.principal.tenantId !== reference.tenantId ||\n (resolved.principal.actsAsProfileId ?? null) !==\n reference.actsAsProfileId ||\n (resolved.onBehalfOfUserId ?? null) !==\n reference.onBehalfOfUserId ||\n (resolved.agentClass ?? null) !== (reference.agentClass ?? null) ||\n !Array.isArray(resolved.principal.allowedTools)\n ) {\n throw new Error(\n 'Deferred data-surface action principal binding could not be resolved safely',\n );\n }\n refreshed = resolved;\n } catch (error) {\n const reason = options.mapError?.(error, request);\n if (!reason) {\n // No side effect has started, so a later queue attempt may safely\n // acquire a fresh reservation and re-check current authority.\n await state.releaseIdempotency(executionScope, ownerToken);\n throw error;\n }\n const denied = result(request, false, reason);\n if (\n !(await state.completeIdempotency(\n executionScope,\n ownerToken,\n denied,\n ))\n ) {\n throw new Error('Lost background action idempotency reservation');\n }\n return denied;\n }\n let executed: DataSurfaceActionResult;\n let mutationStarted = false;\n try {\n // Permission snapshots are never carried across the queue boundary;\n // executeAsPrincipal resolves current RBAC/membership immediately.\n const { permissions: _permissions, ...livePrincipal } = refreshed;\n executed = await authorizedApply(\n request,\n { principal: livePrincipal },\n token,\n false,\n () => {\n mutationStarted = true;\n },\n );\n } catch (error) {\n const reason = options.mapError?.(error, request);\n if (!reason) {\n if (!mutationStarted) {\n await state.releaseIdempotency(executionScope, ownerToken);\n }\n // Once mutation starts, effects are unknown; retain the reservation\n // until evidence-based reconciliation rather than retrying blindly.\n throw error;\n }\n executed = result(request, false, reason);\n }\n if (\n !(await state.completeIdempotency(\n executionScope,\n ownerToken,\n executed,\n ))\n ) {\n throw new Error('Lost background action idempotency reservation');\n }\n return executed;\n }\n if (poll < maxPolls) {\n await new Promise<void>((resolve) =>\n setTimeout(resolve, idempotencyPollIntervalMs),\n );\n const current = await state.getIdempotency(executionScope);\n if (current?.status === 'completed')\n return replayResult(request, current.result);\n }\n }\n return result(request, false, 'idempotency_in_progress');\n }\n\n async function authorizedApply(\n request: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n token: DataSurfacePreviewTokenRecord | undefined,\n allowBackground: boolean,\n beforeMutation?: () => void,\n ): Promise<DataSurfaceActionResult> {\n const idempotencyKey = request.idempotencyKey;\n if (!idempotencyKey) return result(request, false, 'invalid_request');\n // Capture the immutable binding before any asynchronous authorization.\n // The host may reuse or mutate its request context after enqueue returns.\n const deferredPrincipalReference = Object.freeze({\n runAsUserId: context.principal.principal.runAsUserId,\n tenantId: context.principal.principal.tenantId,\n actsAsProfileId: context.principal.principal.actsAsProfileId ?? null,\n onBehalfOfUserId: context.principal.onBehalfOfUserId ?? null,\n ...(context.principal.agentClass\n ? { agentClass: context.principal.agentClass }\n : {}),\n });\n return runAsPrincipal(\n {\n ...context.principal,\n action: 'data_surface.action.apply',\n auditMetadata: {\n ...context.principal.auditMetadata,\n surfaceId: request.identity.surfaceId,\n actionId: request.actionId,\n requestId: request.requestId,\n idempotencyKey: request.idempotencyKey,\n },\n },\n async (run) => {\n const invocation = await resolveInvocation(request, run);\n if ('ok' in invocation) return invocation;\n if (token) {\n if (\n fingerprintRequest(request) !== token.requestFingerprint ||\n invocation.selection.revision !== token.revision ||\n invocation.selection.revision !== request.expectedRevision ||\n invocation.selection.queryFingerprint !== token.queryFingerprint ||\n fingerprint(canonicalSelection(request.selection)) !==\n token.selectionFingerprint ||\n actionFingerprint(invocation.action) !== token.actionFingerprint ||\n fingerprint(canonicalRowIds(invocation.selection.rowIds)) !==\n token.resolvedRowsFingerprint\n ) {\n return result(request, false, 'stale_preview');\n }\n } else if (invocation.action.confirmation === 'required') {\n return result(request, false, 'confirmation_required');\n } else if (invocation.selection.revision !== request.expectedRevision) {\n return result(request, false, 'stale_revision');\n }\n if (invocation.action.execution === 'background' && allowBackground) {\n if (\n !options.backgroundQueue ||\n !validIdentifier(options.backgroundHandlerId) ||\n !options.resolveDeferredPrincipal ||\n !validSigningKey(options.deferredEnvelopeSigningKey)\n ) {\n return result(request, false, 'background_unavailable');\n }\n const { confirmationToken: _confirmationToken, ...deferredRequest } =\n request;\n const unsignedEnvelope = {\n version: 1,\n handlerId: options.backgroundHandlerId ?? '',\n request: deferredRequest,\n principal: deferredPrincipalReference,\n ...(token ? { previewToken: token } : {}),\n } as const;\n const queued = await options.backgroundQueue.enqueue({\n idempotencyKey,\n identity: request.identity,\n actionId: request.actionId,\n rowIds: invocation.selection.rowIds,\n envelope: {\n ...unsignedEnvelope,\n binding: envelopeBinding(\n unsignedEnvelope,\n options.deferredEnvelopeSigningKey as string | Uint8Array,\n ),\n },\n run: () =>\n executeBackgroundOnce(request, token, deferredPrincipalReference),\n });\n return result(request, true, undefined, {\n accepted: invocation.selection.rowIds.length,\n skipped: 0,\n failed: 0,\n ...(queued.details ?? {}),\n background: true,\n jobId: queued.jobId,\n // A replayed apply has a new transport request id, while the\n // already-queued job still returns the original execution result.\n // Preserve that correlation id across the replay envelope.\n jobRequestId: request.requestId,\n });\n }\n beforeMutation?.();\n return executeForeground(request, invocation);\n },\n );\n }\n\n async function apply(\n input: DataSurfaceServerActionRequest,\n context: DataSurfaceActionContext,\n ): Promise<DataSurfaceActionResult> {\n const invalid = validateRequest(input, 'apply');\n if (invalid) return result(input, false, invalid);\n const request = snapshotRequest(input);\n const boundContext = snapshotActionContext(context);\n const confirmationToken = request.confirmationToken;\n const idempotencyKey = request.idempotencyKey;\n if (!idempotencyKey) return result(request, false, 'invalid_request');\n const actorUserId = boundContext.principal.principal.runAsUserId;\n const tenantId = boundContext.principal.principal.tenantId;\n const onBehalfOfUserId = boundContext.principal.onBehalfOfUserId ?? null;\n const actsAsProfileId =\n boundContext.principal.principal.actsAsProfileId ?? null;\n const agentClass = boundContext.principal.agentClass ?? null;\n const requestFingerprintValue = fingerprintRequest(request);\n const idempotencyScope = fingerprint({\n actorUserId,\n tenantId,\n onBehalfOfUserId,\n actsAsProfileId,\n agentClass,\n identity: canonicalIdentity(request.identity),\n actionId: request.actionId,\n idempotencyKey,\n });\n const prior = await state.getIdempotency(idempotencyScope);\n if (prior && prior.requestFingerprint !== requestFingerprintValue)\n return result(request, false, 'idempotency_conflict');\n // A completed durable result is safe to replay from its actor/tenant-bound\n // idempotency scope even when the one-time confirmation has expired.\n if (prior?.status === 'completed')\n return replayResult(request, prior.result);\n\n let token: DataSurfacePreviewTokenRecord | undefined;\n if (confirmationToken) {\n token = await state.getToken(confirmationToken);\n if (!token || token.expiresAt <= now()) {\n return result(request, false, 'invalid_or_expired_confirmation');\n }\n if (\n token.actorUserId !== actorUserId ||\n token.tenantId !== tenantId ||\n token.onBehalfOfUserId !== onBehalfOfUserId ||\n token.actsAsProfileId !== actsAsProfileId ||\n token.agentClass !== agentClass ||\n token.identityKey !== identityKey(request.identity) ||\n token.actionId !== request.actionId ||\n token.requestFingerprint !== requestFingerprintValue\n ) {\n return result(request, false, 'confirmation_mismatch');\n }\n }\n // Resolve and validate deferred execution before consuming a one-time\n // confirmation or reserving idempotency. Execution re-authorizes below;\n // this preflight only prevents broken worker configuration from burning a\n // retryable preview.\n const backgroundPreflight = await runAsPrincipal(\n {\n ...boundContext.principal,\n action: 'data_surface.action.apply',\n auditMetadata: boundContext.principal.auditMetadata,\n },\n async (run) => {\n const surface = await options.resolveSurface(run, request.identity);\n if (\n identityKey(surface.descriptor.identity) !==\n identityKey(request.identity)\n )\n return undefined;\n const action = surface.actions[request.actionId];\n const declared = surface.descriptor.actions.find(\n ({ id }) => id === request.actionId,\n );\n if (!action || !declared || action.descriptor.id !== declared.id)\n return undefined;\n if (\n action.execution === 'background' &&\n (!options.backgroundQueue ||\n !validIdentifier(options.backgroundHandlerId) ||\n !options.resolveDeferredPrincipal ||\n !validSigningKey(options.deferredEnvelopeSigningKey))\n ) {\n return result(request, false, 'background_unavailable');\n }\n return undefined;\n },\n );\n if (backgroundPreflight) return backgroundPreflight;\n // Ownership is an internal compare-and-set nonce. Keep it independent of\n // the injectable preview-token factory, which tests or callers may make\n // deterministic without weakening concurrent winner selection.\n const ownerToken = randomBytes(16).toString('base64url');\n const reservation = {\n requestFingerprint: requestFingerprintValue,\n ownerToken,\n reservedAt: now(),\n };\n let firstWinner: DataSurfaceIdempotencyRecord | undefined;\n if (confirmationToken) {\n firstWinner = await state.consumeTokenAndReserveIdempotency(\n confirmationToken,\n idempotencyKey,\n idempotencyScope,\n reservation,\n );\n if (!firstWinner) return result(request, false, 'confirmation_replayed');\n }\n const maxPolls = Math.max(\n 1,\n Math.ceil(idempotencyWaitTimeoutMs / idempotencyPollIntervalMs),\n );\n for (let poll = 0; poll <= maxPolls; poll += 1) {\n const winner =\n poll === 0 && firstWinner\n ? firstWinner\n : await state.reserveIdempotency(idempotencyScope, reservation);\n if (winner.requestFingerprint !== requestFingerprintValue)\n return result(request, false, 'idempotency_conflict');\n if (winner.status === 'completed')\n return replayResult(request, winner.result);\n if (winner.ownerToken === ownerToken) {\n let applied: DataSurfaceActionResult;\n try {\n applied = await authorizedApply(request, boundContext, token, true);\n } catch (error) {\n await state.releaseIdempotency(idempotencyScope, ownerToken);\n throw error;\n }\n // A confirmation-required request without a token is a recoverable\n // precondition failure. Do not consume its idempotency key: the caller\n // may preview and retry with the same key.\n if (!applied.ok && applied.reason === 'confirmation_required') {\n await state.releaseIdempotency(idempotencyScope, ownerToken);\n return applied;\n }\n // Once execution returns, never release on a persistence failure: a\n // durable reservation is safer than allowing duplicate side effects.\n if (\n !(await state.completeIdempotency(\n idempotencyScope,\n ownerToken,\n applied,\n ))\n ) {\n throw new Error('Lost data-surface idempotency reservation');\n }\n return applied;\n }\n if (poll < maxPolls) {\n await new Promise<void>((resolve) =>\n setTimeout(resolve, idempotencyPollIntervalMs),\n );\n const current = await state.getIdempotency(idempotencyScope);\n if (current?.status === 'completed')\n return replayResult(request, current.result);\n }\n }\n return result(request, false, 'idempotency_in_progress');\n }\n\n async function executeDeferred(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult> {\n if (\n !validSigningKey(options.deferredEnvelopeSigningKey) ||\n !bindingMatches(\n envelope,\n options.deferredEnvelopeSigningKey as string | Uint8Array,\n )\n ) {\n throw new Error('Invalid durable data-surface action envelope binding');\n }\n const principal = envelope.principal;\n if (\n envelope.version !== 1 ||\n !options.backgroundHandlerId ||\n envelope.handlerId !== options.backgroundHandlerId ||\n !principal ||\n !validIdentifier(principal.runAsUserId) ||\n (principal.tenantId !== null && !validIdentifier(principal.tenantId)) ||\n (principal.actsAsProfileId !== null &&\n !validIdentifier(principal.actsAsProfileId)) ||\n (principal.onBehalfOfUserId !== null &&\n !validIdentifier(principal.onBehalfOfUserId)) ||\n (principal.agentClass !== undefined &&\n !validIdentifier(principal.agentClass))\n ) {\n return result(envelope.request, false, 'invalid_request');\n }\n const invalid = validateRequest(envelope.request, 'apply');\n if (invalid) return result(envelope.request, false, invalid);\n return executeBackgroundOnce(\n snapshotRequest(envelope.request),\n envelope.previewToken,\n Object.freeze({ ...envelope.principal }),\n );\n }\n\n return { preview, apply, executeDeferred };\n}\n","import {\n field,\n ObjectRegistry,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n backgroundEligible,\n getActiveJobExecutionContext,\n isRunnerExecutionContext,\n type JobExecutionContext,\n type SmrtJob,\n SmrtJobCollection,\n} from '@happyvertical/smrt-jobs';\nimport {\n getTenantId,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport type {\n DataSurfaceActionResult,\n DataSurfaceJsonObject,\n} from '@happyvertical/smrt-types';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type {\n DataSurfaceBackgroundActionEnvelope,\n DataSurfaceBackgroundActionJob,\n DataSurfaceBackgroundQueue,\n} from './data-surface-actions.js';\n\nconst handlers = new Map<\n string,\n (\n envelope: DataSurfaceBackgroundActionEnvelope,\n ) => Promise<DataSurfaceActionResult>\n>();\n\nexport interface DataSurfaceActionJobArgs {\n version: 1;\n envelope: DataSurfaceBackgroundActionEnvelope;\n}\n\nexport interface JobsDataSurfaceBackgroundQueueOptions {\n db: DatabaseInterface;\n handlerId: string;\n execute(\n envelope: DataSurfaceBackgroundActionEnvelope,\n ): Promise<DataSurfaceActionResult>;\n queue?: string;\n priority?: number;\n timeout?: number;\n maxAttempts?: number;\n tenantJobCap?: number;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_data_surface_action_tasks',\n api: false,\n cli: false,\n mcp: false,\n})\nexport class SmrtDataSurfaceActionTask extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'json', required: true })\n args: DataSurfaceActionJobArgs = {\n version: 1,\n envelope: {} as DataSurfaceBackgroundActionEnvelope,\n };\n\n @backgroundEligible()\n async run(\n args: DataSurfaceActionJobArgs = this.args,\n context?: JobExecutionContext,\n ): Promise<DataSurfaceActionResult> {\n const envelope = args?.envelope;\n const executionContext = getActiveJobExecutionContext() ?? context;\n if (executionContext && !isRunnerExecutionContext(executionContext)) {\n throw new Error('Invalid durable data-surface action job context');\n }\n // A runner context owns scope even when the queued job is global. Persisted\n // instance configuration must never replace that explicit null.\n let jobTenantId: string | null;\n if (executionContext) {\n const runnerTenantId = executionContext.job.tenantId;\n if (runnerTenantId === null) {\n jobTenantId = null;\n } else if (\n typeof runnerTenantId === 'string' &&\n runnerTenantId.length > 0\n ) {\n jobTenantId = runnerTenantId;\n } else {\n throw new Error('Invalid durable data-surface action job tenant');\n }\n } else {\n jobTenantId = this.tenantId ?? getTenantId() ?? null;\n }\n if (\n args?.version !== 1 ||\n envelope?.version !== 1 ||\n typeof envelope.handlerId !== 'string' ||\n envelope.handlerId.length === 0 ||\n envelope.principal?.tenantId !== jobTenantId\n ) {\n throw new Error('Invalid durable data-surface action envelope');\n }\n const handler = handlers.get(envelope.handlerId);\n if (!handler) {\n throw new Error(\n `No data-surface action handler registered for ${envelope.handlerId}`,\n );\n }\n const result = await handler(envelope);\n if (!result.ok && result.reason === 'idempotency_in_progress') {\n throw new Error('Data-surface action outcome requires reconciliation');\n }\n return result;\n }\n}\n\n/**\n * Register the host handler used by workers after process restart.\n * The returned disposer only removes the same registration.\n */\nexport function registerDataSurfaceBackgroundActionHandler(\n handlerId: string,\n execute: (\n envelope: DataSurfaceBackgroundActionEnvelope,\n ) => Promise<DataSurfaceActionResult>,\n): () => void {\n if (!handlerId || handlerId.length > 256) {\n throw new Error(\n 'Data-surface action handlerId must contain 1-256 characters',\n );\n }\n const existing = handlers.get(handlerId);\n if (existing && existing !== execute) {\n throw new Error(\n `Data-surface action handler already registered: ${handlerId}`,\n );\n }\n handlers.set(handlerId, execute);\n return () => {\n if (handlers.get(handlerId) === execute) handlers.delete(handlerId);\n };\n}\n\nexport function createJobsDataSurfaceBackgroundQueue(\n options: JobsDataSurfaceBackgroundQueueOptions,\n): DataSurfaceBackgroundQueue & { unregister(): void } {\n const unregister = registerDataSurfaceBackgroundActionHandler(\n options.handlerId,\n options.execute,\n );\n return {\n unregister,\n async enqueue(job: DataSurfaceBackgroundActionJob) {\n if (job.envelope.handlerId !== options.handlerId) {\n throw new Error('Data-surface action envelope handler mismatch');\n }\n const persisted = await enqueueDataSurfaceActionJob(\n options,\n job.envelope,\n );\n if (!persisted.id)\n throw new Error('Durable data-surface action job has no ID');\n return {\n jobId: persisted.id,\n details: { queue: persisted.queue } as DataSurfaceJsonObject,\n };\n },\n };\n}\n\nasync function enqueueDataSurfaceActionJob(\n options: JobsDataSurfaceBackgroundQueueOptions,\n envelope: DataSurfaceBackgroundActionEnvelope,\n): Promise<SmrtJob> {\n await ObjectRegistry.ensureManifestLoaded('SmrtJob');\n const jobs = await SmrtJobCollection.create({ db: options.db });\n const registered =\n ObjectRegistry.getClassByConstructor(SmrtDataSurfaceActionTask) ??\n ObjectRegistry.getClass('SmrtDataSurfaceActionTask');\n const objectType =\n registered?.qualifiedName ??\n registered?.name ??\n SmrtDataSurfaceActionTask.name;\n return jobs.enqueueJob(\n {\n tenantId: envelope.principal.tenantId,\n queue: options.queue ?? 'data-surface-actions',\n objectType,\n objectId: null,\n method: 'run',\n args: { version: 1, envelope },\n priority: options.priority ?? 70,\n timeout: options.timeout ?? 300_000,\n maxAttempts: options.maxAttempts ?? 3,\n },\n { tenantJobCap: options.tenantJobCap },\n );\n}\n","/**\n * Serialization utilities for resolved agents\n *\n * Converts ResolvedAgentAvailability (database + manifest data) into\n * a JSON-safe shape suitable for passing to client components.\n *\n * @module @happyvertical/smrt-agents/server\n */\n\nimport { sanitizeConfig } from '@happyvertical/smrt-config';\nimport type { ResolvedAgentAvailability } from '../tenant-agent.js';\nimport type { AgentAdminRoute, AgentUISlots } from '../ui.js';\n\n/**\n * Serialized agent data for passing to client components.\n *\n * Includes manifest-derived fields (icon, permissions, slots)\n * alongside resolution metadata (source, sourceTenantId).\n */\nexport interface SerializedAgent {\n /** Agent instance ID, or a synthetic key if no instance exists */\n id: string;\n /** Human-readable name from manifest */\n name?: string;\n /** Human-readable agent class name (e.g., 'Praeco') */\n agentClass: string;\n /** Canonical agent type (qualified name when available) */\n agentType: string;\n /** STI type discriminator (same as agentType) */\n _meta_type?: string;\n /** UI slot definitions from manifest */\n slots?: AgentUISlots;\n /** Admin route declarations from manifest */\n adminRoutes?: AgentAdminRoute[];\n /** How this agent was resolved for the tenant */\n source?: 'explicit' | 'inherited';\n /** Which tenant the binding came from */\n sourceTenantId?: string;\n /** Merged permissions from manifest + tenant overrides */\n permissions?: Record<string, boolean>;\n /** Agent icon from manifest */\n icon?: string;\n /**\n * Tenant-level config overrides, **secret-sanitized** for client transport.\n *\n * SECURITY (#1553, follow-up to #1552): the raw `TenantAgent.config` is the\n * tenant's own override blob and is `@field({ sensitive: true })` (stripped\n * from the generated CRUD api/mcp surfaces). This hand-written admin\n * serialization runs it through `sanitizeConfig()` from\n * `@happyvertical/smrt-config` before it leaves the server, so secret-shaped\n * keys (apiKey/token/password/…) are dropped and secret-shaped values\n * (`sk-…`, `AKIA…`, `Bearer …`, URL credentials, PEM blocks) are masked —\n * non-secret config still reaches the authorized admin UI for display.\n *\n * This is **display-only**: do not edit-round-trip it back to the server\n * (a masked value would overwrite the real secret). Best practice remains to\n * reference secrets by id via `@happyvertical/smrt-secrets` so only an opaque\n * handle is ever stored in tenant config.\n */\n config?: Record<string, unknown>;\n}\n\n/**\n * Convert a ResolvedAgentAvailability to a serializable shape for the UI.\n *\n * @param resolved - Output from TenantAgentCollection.resolveForTenant()\n * @returns Serialized agent data safe for JSON transport\n */\nexport function serializeResolvedAgent(\n resolved: ResolvedAgentAvailability,\n): SerializedAgent {\n const manifest = resolved.manifest;\n\n return {\n id: resolved.agentId || `${resolved.sourceTenantId}:${resolved.agentType}`,\n name: manifest?.name || resolved.agentClass,\n agentClass: resolved.agentClass,\n agentType: resolved.agentType,\n _meta_type: resolved.agentType,\n slots: manifest?.uiSlots as AgentUISlots | undefined,\n adminRoutes: manifest?.adminRoutes as AgentAdminRoute[] | undefined,\n source: resolved.source,\n sourceTenantId: resolved.sourceTenantId,\n permissions: resolved.permissions,\n icon: manifest?.icon,\n // Secret-sanitize before the blob crosses into the client payload (#1553).\n config: sanitizeConfig(resolved.config) as SerializedAgent['config'],\n };\n}\n","import { createHash, randomUUID } from 'node:crypto';\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { DataSurfaceActionResult } from '@happyvertical/smrt-types';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type {\n DataSurfaceActionStateStore,\n DataSurfaceIdempotencyRecord,\n DataSurfaceIdempotencyRecoveryEvidence,\n DataSurfaceIdempotencyReservation,\n DataSurfacePreviewTokenRecord,\n} from './data-surface-actions.js';\n\nconst TOKEN_TABLE = '_smrt_data_surface_action_tokens';\nconst IDEMPOTENCY_TABLE = '_smrt_data_surface_action_idempotency';\n\nconst INTERNAL_SURFACE = {\n api: false,\n cli: false,\n mcp: false,\n} as const;\n\n@smrt({ tableName: '_smrt_data_surface_action_tokens', ...INTERNAL_SURFACE })\nexport class DataSurfaceActionTokenState extends SmrtObject {\n @field({ type: 'text', required: true, unique: true })\n tokenHash: string = '';\n\n @field({ type: 'json', required: true })\n record: DataSurfacePreviewTokenRecord = emptyTokenRecord();\n\n @field({ type: 'text', nullable: true })\n consumedBy: string | null = null;\n}\n\n@smrt({\n tableName: '_smrt_data_surface_action_idempotency',\n ...INTERNAL_SURFACE,\n})\nexport class DataSurfaceActionIdempotencyState extends SmrtObject {\n @field({ type: 'text', required: true, unique: true })\n keyHash: string = '';\n\n @field({ type: 'text', required: true })\n status: 'reserved' | 'completed' = 'reserved';\n\n @field({ type: 'text', required: true })\n requestFingerprint: string = '';\n\n @field({ type: 'text', nullable: true })\n ownerHash: string | null = null;\n\n @field({ type: 'text', nullable: true })\n reservedAt: string | null = null;\n\n @field({ type: 'json', nullable: true })\n result: DataSurfaceActionResult | null = null;\n\n @field({ type: 'json', nullable: true })\n recovery: DataSurfaceIdempotencyRecoveryEvidence | null = null;\n}\n\nexport interface DataSurfaceIdempotencyRecoveryRequest {\n requestFingerprint: string;\n reservedAt: number;\n result: DataSurfaceActionResult;\n authorizedBy: string;\n evidence: string;\n}\n\nexport interface SqlDataSurfaceActionStateStoreOptions {\n db: DatabaseInterface;\n now?: () => number;\n authorizeRecovery?: (\n request: Readonly<DataSurfaceIdempotencyRecoveryRequest>,\n ) => boolean | Promise<boolean>;\n}\n\nexport class DataSurfaceActionStateCorruptionError extends Error {\n constructor(table: string) {\n super(`Malformed durable data-surface action state in ${table}`);\n this.name = 'DataSurfaceActionStateCorruptionError';\n }\n}\n\n/**\n * SQL-backed action state shared by server processes.\n *\n * Preview tokens and owner nonces are stored only as hashes. Runtime schema\n * creation remains the application's normal SMRT migration responsibility.\n * An orphaned reservation is never expired or released automatically: a host\n * may only reconcile it to a concrete terminal result after its live authority\n * callback accepts immutable evidence for the exact reservation timestamp.\n */\nexport class SqlDataSurfaceActionStateStore\n implements DataSurfaceActionStateStore\n{\n private readonly db: DatabaseInterface;\n private readonly now: () => number;\n private readonly authorizeRecovery?: SqlDataSurfaceActionStateStoreOptions['authorizeRecovery'];\n\n constructor(options: SqlDataSurfaceActionStateStoreOptions) {\n this.db = options.db;\n this.now = options.now ?? Date.now;\n this.authorizeRecovery = options.authorizeRecovery;\n }\n\n async putToken(\n token: string,\n record: DataSurfacePreviewTokenRecord,\n ): Promise<void> {\n const timestamp = new Date(this.now()).toISOString();\n await this.db.query(\n `INSERT INTO ${TOKEN_TABLE}\n (id, slug, context, created_at, updated_at, token_hash, record, consumed_by)\n VALUES (?, ?, '', ?, ?, ?, ?, NULL)\n ON CONFLICT(token_hash) DO NOTHING`,\n randomUUID(),\n `action-token-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(token),\n JSON.stringify(record),\n );\n }\n\n async getToken(\n token: string,\n ): Promise<DataSurfacePreviewTokenRecord | undefined> {\n const found = await this.db.query(\n `SELECT record, consumed_by FROM ${TOKEN_TABLE} WHERE token_hash = ? LIMIT 1`,\n secretHash(token),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n if (!row) return undefined;\n const record = tokenRecord(parseObject(row.record, TOKEN_TABLE));\n return {\n ...record,\n ...(typeof row.consumed_by === 'string'\n ? { consumedBy: row.consumed_by }\n : {}),\n };\n }\n\n async markTokenConsumed(\n token: string,\n idempotencyKey: string,\n ): Promise<boolean> {\n const updated = await this.db.query(\n `UPDATE ${TOKEN_TABLE}\n SET consumed_by = ?, updated_at = ?\n WHERE token_hash = ? AND consumed_by IS NULL\n RETURNING token_hash`,\n secretHash(idempotencyKey),\n new Date(this.now()).toISOString(),\n secretHash(token),\n );\n return updated.rows.length === 1;\n }\n\n async consumeTokenAndReserveIdempotency(\n token: string,\n idempotencyKey: string,\n scope: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const transaction = this.db.transaction;\n if (!transaction) {\n throw new Error(\n 'Durable data-surface action state requires database transactions',\n );\n }\n return (await transaction.call(this.db, async (tx) => {\n const timestamp = new Date(this.now()).toISOString();\n const consumed = await tx.query(\n `UPDATE ${TOKEN_TABLE}\n SET consumed_by = ?, updated_at = ?\n WHERE token_hash = ?\n AND (consumed_by IS NULL OR consumed_by = ?)\n RETURNING token_hash`,\n secretHash(idempotencyKey),\n timestamp,\n secretHash(token),\n secretHash(idempotencyKey),\n );\n if (consumed.rows.length !== 1) return undefined;\n await tx.query(\n `INSERT INTO ${IDEMPOTENCY_TABLE}\n (id, slug, context, created_at, updated_at, key_hash, status,\n request_fingerprint, owner_hash, reserved_at, result, recovery)\n VALUES (?, ?, '', ?, ?, ?, 'reserved', ?, ?, ?, NULL, NULL)\n ON CONFLICT(key_hash) DO NOTHING`,\n randomUUID(),\n `action-idempotency-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(scope),\n reservation.requestFingerprint,\n secretHash(reservation.ownerToken),\n String(reservation.reservedAt),\n );\n const current = await this.getIdempotencyWithOwner(\n scope,\n reservation.ownerToken,\n tx,\n );\n if (!current) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return current;\n })) as DataSurfaceIdempotencyRecord | undefined;\n }\n\n async getIdempotency(\n key: string,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const found = await this.db.query(\n `SELECT status, request_fingerprint, owner_hash, reserved_at, result, recovery\n FROM ${IDEMPOTENCY_TABLE} WHERE key_hash = ? LIMIT 1`,\n secretHash(key),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n return row ? idempotencyRecord(row) : undefined;\n }\n\n async reserveIdempotency(\n key: string,\n reservation: DataSurfaceIdempotencyReservation,\n ): Promise<DataSurfaceIdempotencyRecord> {\n const timestamp = new Date(this.now()).toISOString();\n await this.db.query(\n `INSERT INTO ${IDEMPOTENCY_TABLE}\n (id, slug, context, created_at, updated_at, key_hash, status,\n request_fingerprint, owner_hash, reserved_at, result, recovery)\n VALUES (?, ?, '', ?, ?, ?, 'reserved', ?, ?, ?, NULL, NULL)\n ON CONFLICT(key_hash) DO NOTHING`,\n randomUUID(),\n `action-idempotency-${randomUUID()}`,\n timestamp,\n timestamp,\n secretHash(key),\n reservation.requestFingerprint,\n secretHash(reservation.ownerToken),\n String(reservation.reservedAt),\n );\n const current = await this.getIdempotencyWithOwner(\n key,\n reservation.ownerToken,\n );\n if (!current)\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n return current;\n }\n\n async completeIdempotency(\n key: string,\n ownerToken: string,\n result: DataSurfaceActionResult,\n ): Promise<boolean> {\n const updated = await this.db.query(\n `UPDATE ${IDEMPOTENCY_TABLE}\n SET status = 'completed', result = ?, owner_hash = NULL,\n reserved_at = NULL, updated_at = ?\n WHERE key_hash = ? AND status = 'reserved' AND owner_hash = ?\n RETURNING key_hash`,\n JSON.stringify(result),\n new Date(this.now()).toISOString(),\n secretHash(key),\n secretHash(ownerToken),\n );\n return updated.rows.length === 1;\n }\n\n async releaseIdempotency(key: string, ownerToken: string): Promise<boolean> {\n const removed = await this.db.query(\n `DELETE FROM ${IDEMPOTENCY_TABLE}\n WHERE key_hash = ? AND status = 'reserved' AND owner_hash = ?\n RETURNING key_hash`,\n secretHash(key),\n secretHash(ownerToken),\n );\n return removed.rows.length === 1;\n }\n\n async reconcileIdempotency(\n key: string,\n request: DataSurfaceIdempotencyRecoveryRequest,\n ): Promise<boolean> {\n if (\n !request.requestFingerprint ||\n !Number.isSafeInteger(request.reservedAt) ||\n request.reservedAt < 0 ||\n !request.authorizedBy ||\n request.authorizedBy.length > 256 ||\n !request.evidence ||\n request.evidence.length > 2_048\n ) {\n return false;\n }\n if (\n !this.authorizeRecovery ||\n !(await this.authorizeRecovery(Object.freeze({ ...request })))\n ) {\n return false;\n }\n const recovery: DataSurfaceIdempotencyRecoveryEvidence = {\n authorizedBy: request.authorizedBy,\n evidence: request.evidence,\n reconciledAt: this.now(),\n };\n const updated = await this.db.query(\n `UPDATE ${IDEMPOTENCY_TABLE}\n SET status = 'completed', result = ?, recovery = ?, owner_hash = NULL,\n reserved_at = NULL, updated_at = ?\n WHERE key_hash = ? AND status = 'reserved'\n AND request_fingerprint = ? AND reserved_at = ?\n RETURNING key_hash`,\n JSON.stringify(request.result),\n JSON.stringify(recovery),\n new Date(recovery.reconciledAt).toISOString(),\n secretHash(key),\n request.requestFingerprint,\n String(request.reservedAt),\n );\n return updated.rows.length === 1;\n }\n\n private async getIdempotencyWithOwner(\n key: string,\n ownerToken: string,\n db: DatabaseInterface = this.db,\n ): Promise<DataSurfaceIdempotencyRecord | undefined> {\n const found = await db.query(\n `SELECT status, request_fingerprint, owner_hash, reserved_at, result, recovery\n FROM ${IDEMPOTENCY_TABLE} WHERE key_hash = ? LIMIT 1`,\n secretHash(key),\n );\n const row = found.rows[0] as Record<string, unknown> | undefined;\n if (!row) return undefined;\n const record = idempotencyRecord(row);\n if (\n record.status === 'reserved' &&\n row.owner_hash === secretHash(ownerToken)\n ) {\n return { ...record, ownerToken };\n }\n return record;\n }\n}\n\nexport function createSqlDataSurfaceActionStateStore(\n options: SqlDataSurfaceActionStateStoreOptions,\n): SqlDataSurfaceActionStateStore {\n return new SqlDataSurfaceActionStateStore(options);\n}\n\nfunction secretHash(value: string): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction parseObject(value: unknown, table: string): Record<string, unknown> {\n let parsed = value;\n if (typeof value === 'string') {\n try {\n parsed = JSON.parse(value) as unknown;\n } catch {\n throw new DataSurfaceActionStateCorruptionError(table);\n }\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new DataSurfaceActionStateCorruptionError(table);\n }\n return parsed as Record<string, unknown>;\n}\n\nfunction idempotencyRecord(\n row: Record<string, unknown>,\n): DataSurfaceIdempotencyRecord {\n const requestFingerprint = row.request_fingerprint;\n if (typeof requestFingerprint !== 'string') {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n if (row.status === 'reserved') {\n const reservedAt = Number(row.reserved_at);\n if (\n typeof row.owner_hash !== 'string' ||\n !Number.isSafeInteger(reservedAt)\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return {\n status: 'reserved',\n requestFingerprint,\n ownerToken: '',\n reservedAt,\n };\n }\n if (row.status !== 'completed') {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n const result = actionResult(parseObject(row.result, IDEMPOTENCY_TABLE));\n const recovery =\n row.recovery == null\n ? undefined\n : recoveryEvidence(parseObject(row.recovery, IDEMPOTENCY_TABLE));\n return {\n status: 'completed',\n requestFingerprint,\n result,\n ...(recovery ? { recovery } : {}),\n };\n}\n\nfunction tokenRecord(\n value: Record<string, unknown>,\n): DataSurfacePreviewTokenRecord {\n const stringKeys = [\n 'actorUserId',\n 'identityKey',\n 'actionId',\n 'actionFingerprint',\n 'queryFingerprint',\n 'selectionFingerprint',\n 'resolvedRowsFingerprint',\n 'requestFingerprint',\n ] as const;\n if (\n !Number.isSafeInteger(value.expiresAt) ||\n !Number.isSafeInteger(value.revision) ||\n stringKeys.some((key) => typeof value[key] !== 'string') ||\n !['tenantId', 'onBehalfOfUserId', 'actsAsProfileId', 'agentClass'].every(\n (key) => value[key] === null || typeof value[key] === 'string',\n )\n ) {\n throw new DataSurfaceActionStateCorruptionError(TOKEN_TABLE);\n }\n return value as unknown as DataSurfacePreviewTokenRecord;\n}\n\nfunction actionResult(value: Record<string, unknown>): DataSurfaceActionResult {\n if (\n value.version !== 1 ||\n typeof value.requestId !== 'string' ||\n typeof value.actionId !== 'string' ||\n value.phase !== 'apply' ||\n typeof value.ok !== 'boolean' ||\n !value.identity ||\n typeof value.identity !== 'object' ||\n Array.isArray(value.identity) ||\n (value.reason !== undefined && typeof value.reason !== 'string')\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return value as unknown as DataSurfaceActionResult;\n}\n\nfunction recoveryEvidence(\n value: Record<string, unknown>,\n): DataSurfaceIdempotencyRecoveryEvidence {\n if (\n typeof value.authorizedBy !== 'string' ||\n typeof value.evidence !== 'string' ||\n !Number.isSafeInteger(value.reconciledAt)\n ) {\n throw new DataSurfaceActionStateCorruptionError(IDEMPOTENCY_TABLE);\n }\n return value as unknown as DataSurfaceIdempotencyRecoveryEvidence;\n}\n\nfunction emptyTokenRecord(): DataSurfacePreviewTokenRecord {\n return {\n expiresAt: 0,\n actorUserId: '',\n tenantId: null,\n onBehalfOfUserId: null,\n actsAsProfileId: null,\n agentClass: null,\n identityKey: '',\n actionId: '',\n actionFingerprint: '',\n revision: 0,\n queryFingerprint: '',\n selectionFingerprint: '',\n resolvedRowsFingerprint: '',\n requestFingerprint: '',\n };\n}\n","/**\n * SvelteKit `+server.ts` wiring for the data-surface action adapter (#2907).\n *\n * `createDataSurfaceActionAdapter()` and `createJobsDataSurfaceBackgroundQueue()`\n * already carry every principal, idempotency, and durable-queue rule. This\n * module only adapts a plain Web `Request`/`Response` pair (which is what a\n * SvelteKit `RequestHandler` receives/returns) to that adapter, so an app\n * never hand-rolls principal resolution, body parsing, or refusal-to-status\n * mapping per route. It has no `@sveltejs/kit` import and no SvelteKit\n * dependency: any framework whose route handler receives a standard\n * `Request` and returns a `Response` can use it the same way.\n *\n * Keep this module — and everything reachable from\n * `@happyvertical/smrt-agents/server` — free of Svelte imports. This entry is\n * the manifest `importPath` of three objects, so it lands in a consumer's\n * generated `.smrt/register.js`, which the `smrt` CLI loads with a plain\n * `import()` for `db:migrate`. `@happyvertical/smrt-ui/data` is a component\n * barrel that re-exports `.svelte` files and is not loadable that way; the\n * Svelte-free `/data-surface` entry carries the shared protocol limits\n * (#2924).\n */\n\nimport type {\n DataSurfaceActionResult,\n DataSurfaceRowId,\n} from '@happyvertical/smrt-types';\nimport { DATA_SURFACE_MAX_REQUEST_BYTES } from '@happyvertical/smrt-ui/data-surface';\nimport type { ExecuteAsPrincipalOptions } from '../execute-as-principal.js';\nimport type {\n DataSurfaceActionAdapter,\n DataSurfaceServerActionRequest,\n} from './data-surface-actions.js';\n\n/** Resolves the bound principal for one incoming request. Throw to refuse. */\nexport type DataSurfaceRoutePrincipalResolver = (\n request: Request,\n) => ExecuteAsPrincipalOptions | Promise<ExecuteAsPrincipalOptions>;\n\nexport interface DataSurfaceRouteHandlerOptions {\n adapter: DataSurfaceActionAdapter;\n resolvePrincipal: DataSurfaceRoutePrincipalResolver;\n /** Called when `resolvePrincipal` throws; defaults to logging nothing. */\n onAuthError?(error: unknown, request: Request): void;\n /** Overrides the shared `DATA_SURFACE_MAX_REQUEST_BYTES` transport ceiling. */\n maxRequestBytes?: number;\n}\n\nexport interface DataSurfaceRouteHandlers {\n /** Wire as `export const POST: RequestHandler = ({ request }) => handlers.preview(request)`. */\n preview(request: Request): Promise<Response>;\n apply(request: Request): Promise<Response>;\n}\n\n/**\n * Refusal reasons that keep the confirm/apply loop alive for the caller\n * (bad or stale client state) map to 4xx; anything the adapter did not\n * recognize as a terminal client mistake maps to 422 so it is distinguishable\n * from a malformed request (400) or a hard authorization denial (403). A\n * `Map` (not a plain object) so an attacker-chosen reason like `constructor`\n * or `toString` can never resolve to an inherited function instead of\n * `undefined`.\n */\nconst REASON_STATUS: ReadonlyMap<string, number> = new Map([\n ['invalid_request', 400],\n ['not_found', 404],\n ['denied', 403],\n ['unsupported', 404],\n ['selection_not_supported', 400],\n ['limit_exceeded', 413],\n ['stale_revision', 409],\n ['stale_preview', 409],\n ['confirmation_required', 409],\n ['confirmation_mismatch', 409],\n ['confirmation_replayed', 409],\n ['invalid_or_expired_confirmation', 409],\n ['idempotency_conflict', 409],\n ['idempotency_in_progress', 202],\n ['background_unavailable', 503],\n]);\n\nfunction statusForResult(result: DataSurfaceActionResult): number {\n if (result.ok) return 200;\n return REASON_STATUS.get(result.reason ?? '') ?? 422;\n}\n\n/**\n * An error surfaced by `PrincipalRun.assertToolAllowed()` /\n * `assertOperation()` (e.g. `PrincipalToolNotAllowedError`,\n * `OperationPermissionError`) carries an explicit HTTP `status` — both are\n * already authored as authorization refusals, not unexpected failures.\n * Anything else is rethrown so it still surfaces as a real 500, not a\n * silently swallowed bug.\n */\nfunction authorizationErrorStatus(error: unknown): number | undefined {\n if (!(error instanceof Error)) return undefined;\n const status = (error as Error & { status?: unknown }).status;\n return typeof status === 'number' && status >= 400 && status < 500\n ? status\n : undefined;\n}\n\nfunction jsonResponse(\n body: DataSurfaceActionResult | { error: string },\n status: number,\n): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n\ntype BoundedBodyResult =\n | { ok: true; text: string }\n | { ok: false; reason: 'payload_too_large' | 'invalid_request' };\n\n/** Cheap, buffer-free rejection of a declared-oversized body via its header. */\nfunction declaredContentLengthExceeds(\n request: Request,\n maxBytes: number,\n): boolean {\n const declared = request.headers.get('content-length');\n if (declared === null) return false;\n const bytes = Number(declared);\n return !Number.isFinite(bytes) || bytes > maxBytes;\n}\n\n/**\n * Read the request body without ever buffering more than `maxBytes`: a\n * chunked or otherwise mis-declared body is capped as it streams in, not\n * only via the (spoofable) `content-length` header.\n */\nasync function readBoundedBody(\n request: Request,\n maxBytes: number,\n): Promise<BoundedBodyResult> {\n const stream = request.body;\n if (!stream) return { ok: true, text: await request.text() };\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n let step: { done: boolean; value?: Uint8Array };\n try {\n step = await reader.read();\n } catch {\n return { ok: false, reason: 'invalid_request' };\n }\n if (step.done || !step.value) break;\n total += step.value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => undefined);\n return { ok: false, reason: 'payload_too_large' };\n }\n chunks.push(step.value);\n }\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return { ok: true, text: new TextDecoder('utf-8').decode(merged) };\n}\n\nfunction parseJson(text: string): DataSurfaceServerActionRequest | undefined {\n try {\n const parsed = JSON.parse(text);\n return parsed && typeof parsed === 'object' ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build the preview/apply `Request -> Response` pair for one action surface.\n * Principal/tenant resolution, idempotency, and durable queueing are entirely\n * owned by `options.adapter` (from `createDataSurfaceActionAdapter`); this\n * helper only bridges transport. Ordered deliberately: the request-byte cap\n * is enforced from the `content-length` header before anything else runs\n * (no buffering), then the principal is resolved (refusing before the body\n * is ever parsed), then the body is read under the same cap as it streams,\n * and only a resolved, in-budget request reaches the adapter.\n */\nexport function createDataSurfaceActionRouteHandlers(\n options: DataSurfaceRouteHandlerOptions,\n): DataSurfaceRouteHandlers {\n const { adapter, resolvePrincipal, onAuthError } = options;\n const maxRequestBytes =\n options.maxRequestBytes ?? DATA_SURFACE_MAX_REQUEST_BYTES;\n\n async function handle(\n request: Request,\n phase: 'preview' | 'apply',\n ): Promise<Response> {\n if (declaredContentLengthExceeds(request, maxRequestBytes)) {\n return jsonResponse({ error: 'payload_too_large' }, 413);\n }\n let principal: ExecuteAsPrincipalOptions;\n try {\n principal = await resolvePrincipal(request);\n } catch (error) {\n onAuthError?.(error, request);\n return jsonResponse({ error: 'unauthorized' }, 401);\n }\n const bodyResult = await readBoundedBody(request, maxRequestBytes);\n if (!bodyResult.ok) {\n return jsonResponse(\n { error: bodyResult.reason },\n bodyResult.reason === 'payload_too_large' ? 413 : 400,\n );\n }\n const body = parseJson(bodyResult.text);\n if (!body) {\n return jsonResponse({ error: 'invalid_request' }, 400);\n }\n let result: DataSurfaceActionResult;\n try {\n result =\n phase === 'preview'\n ? await adapter.preview(body, { principal })\n : await adapter.apply(body, { principal });\n } catch (error) {\n const status = authorizationErrorStatus(error);\n if (status === undefined) throw error;\n return jsonResponse(\n {\n version: 1,\n requestId: body.requestId,\n identity: body.identity,\n actionId: body.actionId,\n phase,\n ok: false,\n reason: 'denied',\n },\n status,\n );\n }\n return jsonResponse(result, statusForResult(result));\n }\n\n return {\n preview: (request) => handle(request, 'preview'),\n apply: (request) => handle(request, 'apply'),\n };\n}\n\n/**\n * Helper for the bulk-scope shape (e.g. \"apply this look to every reference\n * photo in the current selection\"): resolves an `explicit-ids` selection to\n * the full set of related row ids server-side, so the browser only ever\n * supplies the anchor id(s) and the framework — not the app — is the source\n * of truth for which rows a single idempotency key covers.\n */\nexport interface DataSurfaceBulkSelectionResolver {\n /** Expand the browser-supplied anchor ids into the full row set to act on. */\n expand(\n anchorRowIds: DataSurfaceRowId[],\n ): Promise<DataSurfaceRowId[]> | DataSurfaceRowId[];\n}\n\nexport async function resolveBulkExplicitIds(\n anchorRowIds: DataSurfaceRowId[],\n resolver: DataSurfaceBulkSelectionResolver,\n): Promise<DataSurfaceRowId[]> {\n const expanded = await resolver.expand(anchorRowIds);\n const seen = new Set<string>();\n const unique: DataSurfaceRowId[] = [];\n for (const rowId of expanded) {\n const key = `${typeof rowId}:${String(rowId)}`;\n if (seen.has(key)) continue;\n seen.add(key);\n unique.push(rowId);\n }\n return unique;\n}\n"],"mappings":"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"}
@@ -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.51.6",
6
+ "packageVersion": "0.51.8",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "ef8298ad46b5ec48a43f0fbd8101eaa8d912c10a30abdc644f8e2c90467ada5b",
11
- "packageJson": "20e59b4caca047e212d293b724471656e5812776373bd59458767f4cef300903",
12
- "agents": "c162ba431f70ab17ed30f00f367358c7207a6072ee11ab0243d08bb35eb4800a"
10
+ "manifest": "043c68fbfa79e62bb7a33640556d3914b5a0f797227ee633ad11863aa73af538",
11
+ "packageJson": "617f2546b13e0e727839654ac1432a914ea75d463d2eed03356c614a965879e9",
12
+ "agents": "a75384d96e59f01d9e14ecf01df8314393d8178e0f24935ce7a32cb54569ab76"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -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 with no snapshotting. It publishes `(user_id, tenant_id, permissions[])` onto the DB session (Postgres RLS then bounds every query per-`(table, action)` and per-tenant) and hands `fn` a `PrincipalRun` whose `assertToolAllowed()` / `assertOperation()` enforce the persona tool ceiling and the RLS-off catalog gate. Effective authority = **bound-user RBAC ∩ agent-class ceiling ∩ persona `allowedTools`**. Actions audit as on-behalf-of the originating user via a `PrincipalAuditSink`.\n\n## 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## 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; callers must keep `maxPageLimit` <= the collection's own row-limit cap (#2905, follow-up #2912) |\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## 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 with no snapshotting. It publishes `(user_id, tenant_id, permissions[])` onto the DB session (Postgres RLS then bounds every query per-`(table, action)` and per-tenant) and hands `fn` a `PrincipalRun` whose `assertToolAllowed()` / `assertOperation()` enforce the persona tool ceiling and the RLS-off catalog gate. Effective authority = **bound-user RBAC ∩ agent-class ceiling ∩ persona `allowedTools`**. Actions audit as on-behalf-of the originating user via a `PrincipalAuditSink`.\n\n## 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; callers must keep `maxPageLimit` <= the collection's own row-limit cap (#2905, follow-up #2912) |\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-agents",
3
- "version": "0.51.6",
3
+ "version": "0.51.8",
4
4
  "smrtJsdoc": "strict",
5
5
  "type": "module",
6
6
  "smrtRawPrimitives": "strict",
@@ -59,22 +59,22 @@
59
59
  "dependencies": {
60
60
  "@happyvertical/ai": "^0.89.11",
61
61
  "@happyvertical/files": "^0.89.11",
62
- "@happyvertical/smrt-config": "0.51.6",
63
- "@happyvertical/smrt-core": "0.51.6",
64
- "@happyvertical/smrt-jobs": "0.51.6",
65
- "@happyvertical/smrt-playbooks": "0.51.6",
66
- "@happyvertical/smrt-reports": "0.51.6",
67
- "@happyvertical/smrt-secrets": "0.51.6",
68
- "@happyvertical/smrt-tenancy": "0.51.6",
69
- "@happyvertical/smrt-types": "0.51.6",
70
- "@happyvertical/smrt-ui": "0.51.6",
71
- "@happyvertical/smrt-users": "0.51.6",
62
+ "@happyvertical/smrt-config": "0.51.8",
63
+ "@happyvertical/smrt-core": "0.51.8",
64
+ "@happyvertical/smrt-jobs": "0.51.8",
65
+ "@happyvertical/smrt-playbooks": "0.51.8",
66
+ "@happyvertical/smrt-reports": "0.51.8",
67
+ "@happyvertical/smrt-secrets": "0.51.8",
68
+ "@happyvertical/smrt-tenancy": "0.51.8",
69
+ "@happyvertical/smrt-types": "0.51.8",
70
+ "@happyvertical/smrt-ui": "0.51.8",
71
+ "@happyvertical/smrt-users": "0.51.8",
72
72
  "@happyvertical/sql": "^0.89.11",
73
73
  "@happyvertical/utils": "^0.89.11"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@happyvertical/logger": "^0.89.11",
77
- "@happyvertical/smrt-vitest": "0.51.6",
77
+ "@happyvertical/smrt-vitest": "0.51.8",
78
78
  "@happyvertical/sql": "^0.89.11",
79
79
  "@sentry/node": "^10.63.0",
80
80
  "@sveltejs/package": "^2.5.8",