@happyvertical/smrt-playbooks 0.51.39 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
15
15
|
};
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/__smrt-register__.ts
|
|
18
|
-
ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-playbooks\",\"packageVersion\":\"0.
|
|
18
|
+
ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-playbooks\",\"packageVersion\":\"0.52.0\",\"objects\":{\"@happyvertical/smrt-playbooks:PlaybookOverrideCollection\":{\"name\":\"playbookoverridecollection\",\"className\":\"PlaybookOverrideCollection\",\"qualifiedName\":\"@happyvertical/smrt-playbooks:PlaybookOverrideCollection\",\"collection\":\"playbookoverrides\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/playbooks/src/collections/PlaybookOverrideCollection.ts\",\"packageName\":\"@happyvertical/smrt-playbooks\",\"fields\":{},\"methods\":{\"getAppOverride\":{\"name\":\"getAppOverride\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"string\"]}],\"returnType\":\"Promise<PlaybookOverride | null>\",\"isStatic\":false,\"isPublic\":true},\"getTenantOverride\":{\"name\":\"getTenantOverride\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantId\",\"type\":\"string\",\"optional\":false},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"string\"]}],\"returnType\":\"Promise<PlaybookOverride | null>\",\"isStatic\":false,\"isPublic\":true},\"getResolutionLayers\":{\"name\":\"getResolutionLayers\",\"async\":true,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantId\",\"type\":\"string | null\",\"optional\":true,\"unionBranches\":[{\"type\":\"string\"},{\"type\":\"null\"}]},{\"name\":\"options\",\"type\":\"object\",\"optional\":true,\"memberTypes\":[\"string\"]}],\"returnType\":\"Promise<object>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_playbook_overrides\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"PlaybookOverride\",\"exportName\":\"PlaybookOverrideCollection\",\"collectionExportName\":\"PlaybookOverrideCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_playbook_overrides\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_playbook_overrides\\\" (\\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\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID,\\n \\\"title\\\" TEXT,\\n \\\"description\\\" TEXT,\\n \\\"planes\\\" TEXT,\\n \\\"on_step_failure\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"metadata\\\" 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\"},\"key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"notNull\":false,\"unique\":false},\"title\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"description\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"planes\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"on_step_failure\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"metadata\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_playbook_overrides_key_context_idx\",\"columns\":[\"key\",\"context\"],\"unique\":true},{\"name\":\"_smrt_playbook_overrides_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"_smrt_playbook_overrides_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"0da8c622\"},\"importPath\":\"@happyvertical/smrt-playbooks\"},\"@happyvertical/smrt-playbooks:PlaybookOverride\":{\"name\":\"playbookoverride\",\"className\":\"PlaybookOverride\",\"qualifiedName\":\"@happyvertical/smrt-playbooks:PlaybookOverride\",\"collection\":\"playbookoverrides\",\"filePath\":\"/home/runner/work/smrt/smrt/packages/playbooks/src/models/PlaybookOverride.ts\",\"packageName\":\"@happyvertical/smrt-playbooks\",\"fields\":{\"key\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true}},\"title\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"description\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"planes\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"onStepFailure\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"enabled\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{\"nullable\":true}},\"metadata\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{\"getMetadata\":{\"name\":\"getMetadata\",\"async\":false,\"parameters\":[],\"returnType\":\"PlaybookMetadata\",\"isStatic\":false,\"isPublic\":true},\"setMetadata\":{\"name\":\"setMetadata\",\"async\":false,\"parameters\":[{\"name\":\"metadata\",\"type\":\"PlaybookMetadata | null\",\"optional\":false,\"unionBranches\":[{\"type\":\"PlaybookMetadata\"},{\"type\":\"null\"}]}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getPlanes\":{\"name\":\"getPlanes\",\"async\":false,\"parameters\":[],\"returnType\":\"any\",\"isStatic\":false,\"isPublic\":true},\"setPlanes\":{\"name\":\"setPlanes\",\"async\":false,\"parameters\":[{\"name\":\"planes\",\"type\":\"any\",\"optional\":false,\"typeUnresolved\":true}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"toPlaybookLayer\":{\"name\":\"toPlaybookLayer\",\"async\":false,\"parameters\":[],\"returnType\":\"PlaybookLayer\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<this>\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_playbook_overrides\",\"conflictColumns\":[\"key\",\"context\"],\"api\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"]},\"cli\":{\"include\":[\"list\",\"get\",\"create\",\"update\",\"delete\"],\"exclude\":[\"getMetadata\",\"setMetadata\",\"getPlanes\",\"setPlanes\",\"toPlaybookLayer\"]},\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"PlaybookOverride\",\"collectionExportName\":\"PlaybookOverrideCollection\",\"validationRules\":[{\"field\":\"key\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_playbook_overrides\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_playbook_overrides\\\" (\\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\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID,\\n \\\"title\\\" TEXT,\\n \\\"description\\\" TEXT,\\n \\\"planes\\\" TEXT,\\n \\\"on_step_failure\\\" TEXT,\\n \\\"enabled\\\" BOOLEAN,\\n \\\"metadata\\\" 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\"},\"key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"notNull\":false,\"unique\":false},\"title\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"description\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"planes\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"on_step_failure\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"enabled\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"metadata\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_playbook_overrides_key_context_idx\",\"columns\":[\"key\",\"context\"],\"unique\":true},{\"name\":\"_smrt_playbook_overrides_slug_context_idx\",\"columns\":[\"slug\",\"context\"]},{\"name\":\"_smrt_playbook_overrides_created_at_idx\",\"columns\":[\"created_at\"]}],\"version\":\"0da8c622\"},\"importPath\":\"@happyvertical/smrt-playbooks\"}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-tenancy\"]}"));
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/cache.ts
|
|
21
21
|
var PLAYBOOK_CACHE_TTL_MS = 3e4;
|
|
@@ -542,7 +542,7 @@ var PlaybookOverride = class extends SmrtObject {
|
|
|
542
542
|
};
|
|
543
543
|
__decorateClass([field({ required: true })], PlaybookOverride.prototype, "key", 2);
|
|
544
544
|
__decorateClass([field({
|
|
545
|
-
|
|
545
|
+
sqlType: "UUID",
|
|
546
546
|
nullable: true
|
|
547
547
|
})], PlaybookOverride.prototype, "tenantId", 2);
|
|
548
548
|
__decorateClass([field({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["layer"],"sources":["../src/__smrt-register__.ts","../src/cache.ts","../src/utils.ts","../src/playbook-registry.ts","../src/models/PlaybookOverride.ts","../src/collections/PlaybookOverrideCollection.ts","../src/playbook-resolver.ts","../src/preflight-cache.ts","../src/preflight.ts","../src/rest-preflight.ts","../src/index.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt()\n * decorator in the package fires. Fixes issue #1132: in consumer runtimes\n * (tsx, SvelteKit SSR, plain `vite dev`) the decorator's synchronous manifest\n * lookup previously missed because no step populated the global manifest cache\n * — classes got registered with zero fields and `save()` / `toJSON()`\n * silently dropped every declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates\n * manifests via a different path. Only needs to succeed in the published dist\n * output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { DatabaseInterface } from '@happyvertical/sql';\nimport type { PlaybookCacheValue } from './types.js';\n\nconst PLAYBOOK_CACHE_TTL_MS = 30_000;\n\ntype CacheEntry = {\n expiresAt: number;\n value: PlaybookCacheValue;\n};\n\nconst playbookCache = new Map<string, CacheEntry>();\n/** Monotonic per-`(db, key)` invalidation counter; see getPlaybookCacheGeneration. */\nconst cacheGenerations = new Map<string, number>();\nconst dbInstanceIds = new WeakMap<object, string>();\nlet nextDbId = 1;\n\nfunction getDbNamespace(db: unknown): string {\n if (!db) {\n return 'no-db';\n }\n\n if (typeof db === 'string') {\n return `db:${db}`;\n }\n\n if (typeof db === 'object') {\n const dbObject = db as Record<string, unknown>;\n if (typeof dbObject.query === 'function') {\n if (!dbInstanceIds.has(dbObject)) {\n dbInstanceIds.set(dbObject, `db-instance:${nextDbId++}`);\n }\n const namespace = dbInstanceIds.get(dbObject);\n if (namespace) {\n return namespace;\n }\n\n return 'db-instance:unknown';\n }\n\n try {\n return `db-config:${JSON.stringify(dbObject)}`;\n } catch {\n return 'db-config:opaque';\n }\n }\n\n return 'db:unknown';\n}\n\n/**\n * The cache namespace a database handle resolves to.\n *\n * Exported so the preflight cache (#2590) partitions by the same database\n * identity this cache does, rather than growing a second, divergent notion of\n * \"which database is this\".\n *\n * @internal\n */\nexport function playbookCacheNamespace(db: unknown): string {\n return getDbNamespace(db);\n}\n\nfunction buildCacheKey(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): string {\n return `${getDbNamespace(db)}::${key}::${tenantId ?? 'app'}`;\n}\n\nfunction buildGenerationKey(\n key: string,\n db: DatabaseInterface | unknown,\n): string {\n return `${getDbNamespace(db)}::${key}`;\n}\n\nfunction bumpGeneration(key: string, db: DatabaseInterface | unknown): void {\n const generationKey = buildGenerationKey(key, db);\n cacheGenerations.set(\n generationKey,\n (cacheGenerations.get(generationKey) ?? 0) + 1,\n );\n}\n\nexport function getPlaybookCacheTtlMs(): number {\n return PLAYBOOK_CACHE_TTL_MS;\n}\n\nexport function getCachedPlaybookBase(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): PlaybookCacheValue | null {\n const cacheKey = buildCacheKey(key, tenantId, db);\n const cached = playbookCache.get(cacheKey);\n\n if (!cached) {\n return null;\n }\n\n if (cached.expiresAt <= Date.now()) {\n playbookCache.delete(cacheKey);\n return null;\n }\n\n return cached.value;\n}\n\n/**\n * Reads the current invalidation generation for a key.\n *\n * A resolution captures this *before* its asynchronous layer loads and hands\n * it back to {@link setCachedPlaybookBase}. Any write that lands while those\n * loads are in flight bumps the generation, so the in-flight resolution — which\n * read the pre-write layers — is refused the cache write instead of\n * repopulating the key it just invalidated. Without this, the acceptance rule\n * \"a stale entry is never served after a write\" held only until a read raced a\n * write, and then failed for the full TTL.\n *\n * Tracked per `(db, key)` rather than per `(db, key, tenantId)`: an app-level\n * row is inherited by every tenant, so a write to any scope of a key must\n * invalidate every scope of it.\n */\nexport function getPlaybookCacheGeneration(\n key: string,\n db: DatabaseInterface | unknown,\n): number {\n return cacheGenerations.get(buildGenerationKey(key, db)) ?? 0;\n}\n\nexport function setCachedPlaybookBase(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n value: PlaybookCacheValue,\n loadedAtGeneration: number,\n): void {\n if (getPlaybookCacheGeneration(key, db) !== loadedAtGeneration) {\n // A write landed while this resolution was loading. Its value is already\n // stale, so drop it rather than poison the key for the whole TTL.\n return;\n }\n\n playbookCache.set(buildCacheKey(key, tenantId, db), {\n expiresAt: Date.now() + PLAYBOOK_CACHE_TTL_MS,\n value,\n });\n}\n\n/**\n * Invalidates a cached resolution. An app-level write (tenantId null) clears\n * every tenant's entry for that key, because each tenant inherits from it.\n */\nexport function invalidatePlaybookCache(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): void {\n const dbNamespace = getDbNamespace(db);\n bumpGeneration(key, db);\n\n if (tenantId !== null && tenantId !== undefined) {\n playbookCache.delete(buildCacheKey(key, tenantId, db));\n return;\n }\n\n const keyPrefix = `${dbNamespace}::${key}::`;\n for (const cacheKey of playbookCache.keys()) {\n if (cacheKey.startsWith(keyPrefix)) {\n playbookCache.delete(cacheKey);\n }\n }\n}\n\nexport function clearPlaybookCache(): void {\n playbookCache.clear();\n // Generations deliberately survive: resetting them to zero would let a\n // resolution that started before the clear write its stale value back.\n for (const generationKey of cacheGenerations.keys()) {\n cacheGenerations.set(\n generationKey,\n (cacheGenerations.get(generationKey) ?? 0) + 1,\n );\n }\n}\n","import type {\n CapabilityClassification,\n CapabilityDeclaration,\n} from '@happyvertical/smrt-types';\nimport {\n PLAYBOOK_PLANES,\n type PlaybookConfigOverrideInput,\n type PlaybookDefinition,\n type PlaybookDefinitionInput,\n type PlaybookEditableConfig,\n type PlaybookFailurePolicy,\n type PlaybookLayer,\n type PlaybookMetadata,\n type PlaybookPlane,\n type PlaybookStep,\n} from './types.js';\n\n/**\n * Every field defaults to non-editable, matching `normalizeEditableConfig` in\n * `@happyvertical/smrt-prompts`. `steps` is not in this table at all — it is\n * structurally non-editable rather than defaulted false.\n */\nconst DEFAULT_EDITABLE: PlaybookEditableConfig = {\n title: false,\n description: false,\n planes: false,\n onStepFailure: false,\n enabled: false,\n metadata: false,\n};\n\n/**\n * Fail-closed capability classification, per epic #2585 invariant 3. Applied\n * whenever the host cannot tell us how a referenced operation is classified.\n */\nexport const FAIL_CLOSED_CLASSIFICATION: CapabilityClassification = {\n effect: 'destructive',\n idempotent: false,\n openWorld: true,\n};\n\nconst FAILURE_POLICIES = new Set<PlaybookFailurePolicy>(['abort', 'continue']);\n\n/**\n * `<package>:<Class>` — the same qualified form STI discriminators and\n * `@crossPackageRef` use. Both halves are required and neither may be padded.\n */\nconst QUALIFIED_MODEL_PATTERN = /^\\S+:\\S+$/;\n\n/** Frozen so a definition's default plane list can never be mutated in place. */\nconst BROWSER_ONLY_PLANES: readonly PlaybookPlane[] = Object.freeze([\n 'browser',\n]);\n\nexport function isPlainObject(\n value: unknown,\n): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function normalizeEditableConfig(\n editable?: Partial<PlaybookEditableConfig>,\n): PlaybookEditableConfig {\n const flag = (name: keyof PlaybookEditableConfig): boolean => {\n const supplied = editable?.[name];\n if (supplied === undefined) {\n return DEFAULT_EDITABLE[name];\n }\n\n // Only a real `true` unlocks a field. A truthy non-boolean such as\n // `'false'` would otherwise read as an explicit opt-in and open a stored\n // override the definition never meant to allow.\n if (typeof supplied !== 'boolean') {\n throw new Error(\n `Playbook editable.${name} must be a boolean, received ${typeof supplied} \"${String(supplied)}\"`,\n );\n }\n\n return supplied;\n };\n\n return {\n title: flag('title'),\n description: flag('description'),\n planes: flag('planes'),\n onStepFailure: flag('onStepFailure'),\n enabled: flag('enabled'),\n metadata: flag('metadata'),\n };\n}\n\n/**\n * Applies a partial capability declaration over the fail-closed default.\n *\n * A playbook step never classifies itself: this only fills the gaps left by a\n * declaration that the host inherited from the referenced operation.\n */\nexport function applyCapabilityDeclaration(\n declaration?: CapabilityDeclaration | null,\n): CapabilityClassification {\n if (!isPlainObject(declaration)) {\n return { ...FAIL_CLOSED_CLASSIFICATION };\n }\n\n return {\n effect: declaration.effect ?? FAIL_CLOSED_CLASSIFICATION.effect,\n idempotent: declaration.idempotent ?? FAIL_CLOSED_CLASSIFICATION.idempotent,\n openWorld: declaration.openWorld ?? FAIL_CLOSED_CLASSIFICATION.openWorld,\n };\n}\n\nexport function isCapabilityDeclarationComplete(\n declaration?: CapabilityDeclaration | null,\n): boolean {\n return (\n isPlainObject(declaration) &&\n declaration.effect !== undefined &&\n declaration.idempotent !== undefined &&\n declaration.openWorld !== undefined\n );\n}\n\nexport function normalizePlanes(\n planes: readonly PlaybookPlane[] | null | undefined,\n context: string,\n): readonly PlaybookPlane[] | undefined {\n if (planes === undefined) {\n return undefined;\n }\n\n if (planes === null) {\n return undefined;\n }\n\n if (!Array.isArray(planes)) {\n throw new Error(`${context} planes must be an array`);\n }\n\n const normalized: PlaybookPlane[] = [];\n for (const plane of planes) {\n if (!PLAYBOOK_PLANES.includes(plane)) {\n throw new Error(\n `${context} declares unknown plane \"${String(plane)}\"; expected one of ${PLAYBOOK_PLANES.join(', ')}`,\n );\n }\n if (!normalized.includes(plane)) {\n normalized.push(plane);\n }\n }\n\n if (normalized.length === 0) {\n throw new Error(`${context} must declare at least one plane`);\n }\n\n // Stable order keeps stored/serialized planes comparable.\n return PLAYBOOK_PLANES.filter((plane) => normalized.includes(plane));\n}\n\nfunction normalizeFailurePolicy(\n value: unknown,\n context: string,\n): PlaybookFailurePolicy {\n if (!FAILURE_POLICIES.has(value as PlaybookFailurePolicy)) {\n throw new Error(\n `${context} onStepFailure must be \"abort\" or \"continue\", received \"${String(value)}\"`,\n );\n }\n\n return value as PlaybookFailurePolicy;\n}\n\n/**\n * Enablement is a gate, so it is validated rather than coerced. Truthiness\n * would silently read a config or runtime `\"false\"` as enabled — the one\n * coercion failure here that fails open.\n */\nfunction normalizeEnabled(value: unknown, context: string): boolean {\n if (typeof value !== 'boolean') {\n throw new Error(\n `${context} enabled must be a boolean, received ${typeof value} \"${String(value)}\"`,\n );\n }\n\n return value;\n}\n\n/**\n * Validated, not coerced, for the same reason as {@link normalizeEnabled}: a\n * stringly-typed `\"false\"` would turn a required step into a skippable one.\n */\nfunction normalizeStepOptional(value: unknown, context: string): boolean {\n if (typeof value !== 'boolean') {\n throw new Error(\n `${context} optional must be a boolean, received ${typeof value} \"${String(value)}\"`,\n );\n }\n\n return value;\n}\n\nfunction normalizeOptionalText(\n value: unknown,\n context: string,\n field: string,\n): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value !== 'string') {\n throw new Error(`${context} ${field} must be a string`);\n }\n\n return value;\n}\n\n/**\n * Validates and freezes the declared step list.\n *\n * Rejects nested playbooks at definition time (epic #2585): a package playbook\n * referencing a tenant-overridden playbook is the description-behavior\n * mismatch one level removed.\n */\nexport function normalizeSteps(\n steps: readonly PlaybookStep[] | undefined,\n context: string,\n): readonly PlaybookStep[] {\n if (!Array.isArray(steps) || steps.length === 0) {\n throw new Error(`${context} requires at least one step`);\n }\n\n return Object.freeze(\n steps.map((rawStep, index) => {\n const stepContext = `${context} step ${index}`;\n\n if (!isPlainObject(rawStep)) {\n throw new Error(`${stepContext} must be an object`);\n }\n\n const kind = rawStep.kind;\n\n if (kind === 'playbook' || 'playbook' in rawStep) {\n throw new Error(\n `${stepContext} references another playbook; nested playbooks are not supported`,\n );\n }\n\n if (kind === 'operation') {\n const model = rawStep.model;\n const action = rawStep.action;\n\n if (typeof model !== 'string' || model.trim() === '') {\n throw new Error(`${stepContext} requires a qualified model name`);\n }\n\n // Both halves must be present and unpadded: ':', '@pkg:', and ':Order'\n // all contain a separator but identify no model, and would resolve\n // into a plan that no classifier or executor lookup can ever match.\n if (!QUALIFIED_MODEL_PATTERN.test(model)) {\n throw new Error(\n `${stepContext} model \"${model}\" must be a qualified pair such as \"@happyvertical/smrt-commerce:Order\"`,\n );\n }\n\n if (typeof action !== 'string' || action.trim() === '') {\n throw new Error(`${stepContext} requires an action name`);\n }\n\n return Object.freeze({\n kind: 'operation' as const,\n model,\n action,\n ...(normalizeOptionalText(rawStep.label, stepContext, 'label') !==\n undefined\n ? { label: rawStep.label as string }\n : {}),\n ...(normalizeOptionalText(\n rawStep.description,\n stepContext,\n 'description',\n ) !== undefined\n ? { description: rawStep.description as string }\n : {}),\n ...(rawStep.optional === undefined\n ? {}\n : {\n optional: normalizeStepOptional(rawStep.optional, stepContext),\n }),\n }) satisfies PlaybookStep;\n }\n\n if (kind === 'intent') {\n const id = rawStep.id;\n\n if (typeof id !== 'string' || id.trim() === '') {\n throw new Error(`${stepContext} requires an intent id`);\n }\n\n return Object.freeze({\n kind: 'intent' as const,\n id,\n ...(normalizeOptionalText(rawStep.label, stepContext, 'label') !==\n undefined\n ? { label: rawStep.label as string }\n : {}),\n ...(normalizeOptionalText(\n rawStep.description,\n stepContext,\n 'description',\n ) !== undefined\n ? { description: rawStep.description as string }\n : {}),\n ...(rawStep.optional === undefined\n ? {}\n : {\n optional: normalizeStepOptional(rawStep.optional, stepContext),\n }),\n }) satisfies PlaybookStep;\n }\n\n throw new Error(\n `${stepContext} has unknown kind \"${String(kind)}\"; expected \"operation\" or \"intent\"`,\n );\n }),\n );\n}\n\nexport function hasIntentStep(steps: readonly PlaybookStep[]): boolean {\n return steps.some((step) => step.kind === 'intent');\n}\n\n/**\n * Default plane validity. Operation-only playbooks are valid on both planes;\n * anything containing a view intent is browser-valid only until the author\n * explicitly declares server validity through the #2446 command/ack bridge.\n */\nexport function defaultPlanesForSteps(\n steps: readonly PlaybookStep[],\n): readonly PlaybookPlane[] {\n return hasIntentStep(steps) ? BROWSER_ONLY_PLANES : PLAYBOOK_PLANES;\n}\n\nexport function normalizePlaybookDefinitionInput(\n input: PlaybookDefinitionInput,\n): PlaybookDefinition {\n if (!input || typeof input.key !== 'string' || input.key.trim() === '') {\n throw new Error('Playbook definitions require a non-empty key');\n }\n\n const context = `Playbook \"${input.key}\"`;\n\n if (typeof input.title !== 'string' || input.title.trim() === '') {\n throw new Error(`${context} requires a title`);\n }\n\n if (\n typeof input.description !== 'string' ||\n input.description.trim() === ''\n ) {\n throw new Error(`${context} requires a description`);\n }\n\n if ('steps' in ((input.editable ?? {}) as Record<string, unknown>)) {\n throw new Error(\n `${context} cannot mark steps editable; playbook step lists are never editable`,\n );\n }\n\n const steps = normalizeSteps(input.steps, context);\n const declaredPlanes = normalizePlanes(input.planes, context);\n\n // Freeze the nested values too, not just the outer object: `planes` and\n // `editable` are the declared fail-closed policy, and a shallow freeze would\n // still let a caller push 'server' onto a browser-only definition or flip an\n // `editable` flag on the object the registry hands back.\n return Object.freeze({\n key: input.key,\n title: input.title,\n description: input.description,\n steps,\n planes: Object.freeze(declaredPlanes ?? defaultPlanesForSteps(steps)),\n onStepFailure:\n input.onStepFailure === undefined\n ? 'abort'\n : normalizeFailurePolicy(input.onStepFailure, context),\n enabled:\n input.enabled === undefined\n ? true\n : normalizeEnabled(input.enabled, context),\n metadata: Object.freeze(\n input.metadata ? sanitizeMetadata(input.metadata) : {},\n ),\n editable: Object.freeze(normalizeEditableConfig(input.editable)),\n });\n}\n\nexport function sanitizeMetadata(\n metadata: PlaybookMetadata | null | undefined,\n): PlaybookMetadata {\n if (!isPlainObject(metadata)) {\n return {};\n }\n\n // Copy and deep-freeze rather than aliasing the caller's object: metadata\n // ends up in the registered definition and the shared cache entry, and a\n // shallow freeze would leave nested values mutable through the caller's\n // original reference.\n return deepFreezeJsonObject(metadata);\n}\n\nfunction deepFreezeJsonValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return Object.freeze(value.map(deepFreezeJsonValue));\n }\n\n if (isPlainObject(value)) {\n return deepFreezeJsonObject(value);\n }\n\n return value;\n}\n\nfunction deepFreezeJsonObject(\n source: Record<string, unknown>,\n): PlaybookMetadata {\n const copied: PlaybookMetadata = {};\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n continue;\n }\n copied[key] = deepFreezeJsonValue(value);\n }\n\n return Object.freeze(copied);\n}\n\nexport function parseMetadata(\n raw: string | null | undefined,\n): PlaybookMetadata {\n if (!raw) {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return isPlainObject(parsed) ? sanitizeMetadata(parsed) : {};\n } catch {\n return {};\n }\n}\n\nexport function serializeMetadata(\n metadata: PlaybookMetadata | null | undefined,\n): string | null {\n if (metadata === null || metadata === undefined) {\n return null;\n }\n\n return JSON.stringify(sanitizeMetadata(metadata));\n}\n\nexport function parsePlanes(\n raw: string | null | undefined,\n): readonly PlaybookPlane[] | null {\n if (!raw) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n return null;\n }\n return (\n normalizePlanes(parsed as PlaybookPlane[], 'Stored override') ?? null\n );\n } catch {\n return null;\n }\n}\n\nexport function serializePlanes(\n planes: readonly PlaybookPlane[] | null | undefined,\n): string | null {\n if (planes === null || planes === undefined) {\n return null;\n }\n\n return JSON.stringify(normalizePlanes(planes, 'Stored override') ?? []);\n}\n\n/**\n * Normalizes an override layer from config, storage, or a runtime call.\n *\n * A `steps` key is rejected rather than dropped: \"a tenant override cannot\n * alter steps through any path\" is enforced loudly at every entry point.\n */\nexport function normalizePlaybookLayer(\n input?: PlaybookConfigOverrideInput | null,\n context = 'Playbook override',\n): PlaybookLayer {\n if (!isPlainObject(input)) {\n return {};\n }\n\n if ('steps' in input) {\n throw new Error(\n `${context} cannot set steps; playbook step lists are never editable`,\n );\n }\n\n const layer: PlaybookLayer = {};\n\n if (input.title !== undefined) {\n layer.title =\n input.title === null\n ? null\n : (normalizeOptionalText(input.title, context, 'title') ?? null);\n }\n\n if (input.description !== undefined) {\n layer.description =\n input.description === null\n ? null\n : (normalizeOptionalText(input.description, context, 'description') ??\n null);\n }\n\n if (input.planes !== undefined) {\n layer.planes =\n input.planes === null\n ? null\n : (normalizePlanes(input.planes, context) ?? null);\n }\n\n if (input.onStepFailure !== undefined) {\n layer.onStepFailure =\n input.onStepFailure === null\n ? null\n : normalizeFailurePolicy(input.onStepFailure, context);\n }\n\n if (input.enabled !== undefined) {\n layer.enabled =\n input.enabled === null ? null : normalizeEnabled(input.enabled, context);\n }\n\n if (input.metadata !== undefined) {\n layer.metadata =\n input.metadata === null ? null : sanitizeMetadata(input.metadata);\n }\n\n return layer;\n}\n\nexport interface MergedPlaybookLayers {\n title: string;\n description: string;\n planes: readonly PlaybookPlane[];\n onStepFailure: PlaybookFailurePolicy;\n enabled: boolean;\n metadata: PlaybookMetadata;\n}\n\n/**\n * Merges layers low → high, field by field.\n *\n * Enablement is one-directional: once any layer disables a playbook, no\n * higher layer can enable it again. A tenant may narrow, never widen.\n */\nexport function mergePlaybookLayers(\n base: PlaybookDefinition,\n ...layers: Array<PlaybookLayer | null | undefined>\n): MergedPlaybookLayers {\n let title = base.title;\n let description = base.description;\n let planes = base.planes;\n let onStepFailure = base.onStepFailure;\n let enabled = base.enabled;\n let metadata: PlaybookMetadata = { ...base.metadata };\n\n for (const layer of layers) {\n if (!layer) {\n continue;\n }\n\n if (layer.title !== undefined && layer.title !== null) {\n title = layer.title;\n }\n\n if (layer.description !== undefined && layer.description !== null) {\n description = layer.description;\n }\n\n if (layer.planes !== undefined && layer.planes !== null) {\n // Plane validity only ever narrows: an override cannot claim a plane the\n // lower layers never declared.\n planes = layer.planes.filter((plane) => planes.includes(plane));\n }\n\n if (layer.onStepFailure !== undefined && layer.onStepFailure !== null) {\n onStepFailure = layer.onStepFailure;\n }\n\n if (layer.enabled !== undefined && layer.enabled !== null) {\n enabled = enabled && layer.enabled;\n }\n\n if (layer.metadata !== undefined && layer.metadata !== null) {\n metadata = { ...metadata, ...layer.metadata };\n }\n }\n\n // The merged result is both cached and handed to callers as part of the\n // plan, so the policy values must be immutable: a caller pushing onto a\n // narrowed `planes` array would otherwise corrupt the cached entry and widen\n // the declared-plane gate for every resolution until the TTL expires.\n return Object.freeze({\n title,\n description,\n planes: Object.freeze(planes),\n onStepFailure,\n enabled,\n metadata: Object.freeze(metadata),\n });\n}\n","import type { PlaybookDefinition, PlaybookDefinitionInput } from './types.js';\nimport { normalizePlaybookDefinitionInput } from './utils.js';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtPlaybookRegistry: Map<string, PlaybookDefinition> | undefined;\n}\n\nfunction getRegistry(): Map<string, PlaybookDefinition> {\n if (!globalThis.__smrtPlaybookRegistry) {\n globalThis.__smrtPlaybookRegistry = new Map<string, PlaybookDefinition>();\n }\n\n return globalThis.__smrtPlaybookRegistry;\n}\n\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item)).join(',')}]`;\n }\n\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>).sort(\n ([left], [right]) => left.localeCompare(right),\n );\n return `{${entries\n .map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`)\n .join(',')}}`;\n }\n\n return JSON.stringify(value);\n}\n\n/**\n * Global process registry of code-default playbooks, keyed by namespaced key.\n *\n * Held on `globalThis` so it survives HMR, mirroring `PromptRegistry`.\n */\nexport const PlaybookRegistry = {\n register(input: PlaybookDefinitionInput): PlaybookDefinition {\n const definition = normalizePlaybookDefinitionInput(input);\n const registry = getRegistry();\n const existing = registry.get(definition.key);\n\n if (existing) {\n const existingSignature = stableStringify(existing);\n const incomingSignature = stableStringify(definition);\n\n if (existingSignature !== incomingSignature) {\n throw new Error(\n `Playbook \"${definition.key}\" is already registered with a different definition`,\n );\n }\n\n return existing;\n }\n\n registry.set(definition.key, definition);\n return definition;\n },\n\n get(key: string): PlaybookDefinition | undefined {\n return getRegistry().get(key);\n },\n\n has(key: string): boolean {\n return getRegistry().has(key);\n },\n\n getAll(): PlaybookDefinition[] {\n return Array.from(getRegistry().values());\n },\n\n clear(): void {\n getRegistry().clear();\n },\n};\n\n/**\n * Registers a code-default playbook. Packages call this at import time so a\n * bundled playbook resolves without any application registration.\n */\nexport function definePlaybook(\n input: PlaybookDefinitionInput,\n): PlaybookDefinition {\n return PlaybookRegistry.register(input);\n}\n","import { getPackageConfig } from '@happyvertical/smrt-config';\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { invalidatePlaybookCache } from '../cache.js';\nimport { PlaybookRegistry } from '../playbook-registry.js';\nimport type {\n PlaybookConfigOverrideInput,\n PlaybookFailurePolicy,\n PlaybookLayer,\n PlaybookMetadata,\n PlaybookOverrideOptions as PlaybookOverrideFieldOptions,\n PlaybookPackageConfig,\n PlaybookPlane,\n} from '../types.js';\nimport {\n mergePlaybookLayers,\n normalizePlaybookLayer,\n parseMetadata,\n parsePlanes,\n serializeMetadata,\n serializePlanes,\n} from '../utils.js';\n\nexport interface PlaybookOverrideOptions\n extends SmrtObjectOptions,\n PlaybookOverrideFieldOptions {}\n\ntype PlaybookOverrideIdentity = {\n key: string;\n tenantId: string | null;\n};\n\ntype PlaybookTransactionHandle = DatabaseInterface & {\n commit: () => Promise<void>;\n rollback: () => Promise<void>;\n};\n\nfunction getPlaybookConfig(): PlaybookPackageConfig {\n return getPackageConfig<PlaybookPackageConfig>('playbooks', {\n playbooks: {},\n });\n}\n\n/**\n * Stored app-level (`tenantId = null`) and tenant-level playbook overrides.\n *\n * There is deliberately **no `steps` column**. `steps` is structurally\n * non-editable: an override layer has nowhere to put a step list, and\n * assigning one is rejected in `save()` rather than silently dropped. That is\n * the description-behavior guarantee from epic #2585 — an agent announcing\n * \"checking out your cart\" must not be following a rewritten script.\n */\n@smrt({\n tableName: '_smrt_playbook_overrides',\n conflictColumns: ['key', 'context'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n exclude: [\n 'getMetadata',\n 'setMetadata',\n 'getPlanes',\n 'setPlanes',\n 'toPlaybookLayer',\n ],\n },\n mcp: { include: [] },\n})\nexport class PlaybookOverride extends SmrtObject {\n @field({ required: true })\n key: string = '';\n\n @field({ type: 'text', nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'text', nullable: true })\n title: string | null = null;\n\n @field({ type: 'text', nullable: true })\n description: string | null = null;\n\n /** JSON array of plane names; null inherits the lower layer. */\n @field({ type: 'text', nullable: true })\n planes: string | null = null;\n\n @field({ type: 'text', nullable: true })\n onStepFailure: string | null = null;\n\n /** Tri-state: null inherits, false disables. True can never widen. */\n @field({ type: 'boolean', nullable: true })\n enabled: boolean | null = null;\n\n /** JSON object; stored as a string with guarded get/set helpers. */\n @field({ type: 'text', nullable: true })\n metadata: string | null = null;\n\n constructor(options: PlaybookOverrideOptions = {}) {\n super(options);\n\n // `SmrtCollection.create()` spreads its caller's option bag straight into\n // the constructor, so this is the entry point a `steps` key actually\n // reaches. Reject it loudly instead of letting it be silently dropped:\n // step lists are never editable through any path.\n if ((options as Record<string, unknown>).steps !== undefined) {\n throw new Error(\n 'Playbook step lists are never editable; PlaybookOverride cannot carry steps',\n );\n }\n\n if (options.key !== undefined) this.key = options.key;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.onStepFailure !== undefined)\n this.onStepFailure = options.onStepFailure;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.planes !== undefined) {\n this.planes =\n typeof options.planes === 'string' || options.planes === null\n ? options.planes\n : serializePlanes(options.planes);\n }\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string' || options.metadata === null\n ? options.metadata\n : serializeMetadata(options.metadata);\n }\n }\n\n getMetadata(): PlaybookMetadata {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: PlaybookMetadata | null): void {\n this.metadata = serializeMetadata(metadata);\n }\n\n getPlanes(): readonly PlaybookPlane[] | null {\n return parsePlanes(this.planes);\n }\n\n setPlanes(planes: readonly PlaybookPlane[] | null): void {\n this.planes = serializePlanes(planes);\n }\n\n toPlaybookLayer(): PlaybookLayer {\n return {\n title: this.title,\n description: this.description,\n planes: this.getPlanes(),\n onStepFailure: (this.onStepFailure ??\n null) as PlaybookFailurePolicy | null,\n // The database boundary is where coercion is legitimate: SQLite hydrates\n // a boolean column as 0/1. Everywhere above this, enablement is\n // validated rather than coerced.\n enabled:\n this.enabled === null || this.enabled === undefined\n ? null\n : Boolean(this.enabled),\n metadata: this.metadata === null ? null : this.getMetadata(),\n };\n }\n\n override async save(): Promise<this> {\n const previousIdentity = await this.getPersistedIdentity();\n this.normalizeForPersistence();\n await this.validatePlaybookOverride();\n // `context` is the conflictColumn-friendly scope: '__app__' for a nullable\n // tenant, tenantId otherwise. Mirrors PromptOverride / LanguageOverride so\n // app-level rows stay unique on PostgreSQL, SQLite, and DuckDB alike,\n // where multiple NULLs would otherwise all satisfy a unique index.\n this.context = this.tenantId ?? '__app__';\n\n const identityChanged =\n !!previousIdentity &&\n (previousIdentity.key !== this.key ||\n previousIdentity.tenantId !== this.tenantId);\n\n const result =\n identityChanged && previousIdentity\n ? await this.saveAfterIdentityChange()\n : await super.save();\n\n if (identityChanged && previousIdentity) {\n invalidatePlaybookCache(\n previousIdentity.key,\n previousIdentity.tenantId,\n this.db,\n );\n }\n invalidatePlaybookCache(this.key, this.tenantId, this.db);\n return result;\n }\n\n private async saveAfterIdentityChange(): Promise<this> {\n if (typeof this.db.beginTransaction === 'function') {\n return this.saveAfterIdentityChangeInTransaction();\n }\n\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n private async saveAfterIdentityChangeInTransaction(): Promise<this> {\n const originalDb = this._db;\n const originalOptionsDb = this.options.db;\n const tx = (await this.db.beginTransaction?.()) as\n | PlaybookTransactionHandle\n | undefined;\n\n if (!tx) {\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n try {\n this._db = tx;\n this.options.db = tx;\n await super.delete();\n const result = await super.save();\n await tx.commit();\n return result;\n } catch (error) {\n try {\n await tx.rollback();\n } catch {\n // Preserve the original save error; rollback failures are secondary.\n }\n throw error;\n } finally {\n this._db = originalDb;\n this.options.db = originalOptionsDb;\n }\n }\n\n private async saveAfterIdentityChangeWithDeferredDelete(): Promise<this> {\n const previousId = this.id;\n if (!previousId) {\n return super.save();\n }\n\n const replacementId = crypto.randomUUID();\n let replacementSaved = false;\n this.id = replacementId;\n\n try {\n const result = await super.save();\n replacementSaved = true;\n await this.db.delete(this.tableName, { id: previousId });\n return result;\n } catch (error) {\n if (replacementSaved) {\n try {\n await this.db.delete(this.tableName, { id: replacementId });\n } catch {\n // Best effort cleanup keeps the original row as the source of truth.\n }\n }\n\n this.id = previousId;\n throw error;\n }\n }\n\n override async delete(): Promise<void> {\n const key = this.key;\n const tenantId = this.tenantId;\n await super.delete();\n invalidatePlaybookCache(key, tenantId, this.db);\n }\n\n private async validatePlaybookOverride(): Promise<void> {\n if (!this.key || this.key.trim() === '') {\n throw new Error('PlaybookOverride.key is required');\n }\n\n // Structural, not merely defaulted-false: there is no `steps` column, and\n // a caller assigning one through the untyped option bag is rejected here\n // rather than having the value silently dropped by persistence.\n const assignedSteps = (this as unknown as Record<string, unknown>).steps;\n if (assignedSteps !== undefined) {\n throw new Error(\n `Playbook \"${this.key}\" step lists are never editable; PlaybookOverride cannot carry steps`,\n );\n }\n\n const definition = PlaybookRegistry.get(this.key);\n if (!definition) {\n throw new Error(`Unknown playbook key \"${this.key}\"`);\n }\n\n const editable = definition.editable;\n\n if (this.title !== null && !editable.title) {\n throw new Error(`Playbook \"${this.key}\" does not allow title overrides`);\n }\n\n if (this.description !== null && !editable.description) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow description overrides`,\n );\n }\n\n if (this.planes !== null && !editable.planes) {\n throw new Error(`Playbook \"${this.key}\" does not allow planes overrides`);\n }\n\n if (this.onStepFailure !== null && !editable.onStepFailure) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow onStepFailure overrides`,\n );\n }\n\n if (this.enabled !== null && !editable.enabled) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow enablement overrides`,\n );\n }\n\n if (this.metadata !== null && !editable.metadata) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow metadata overrides`,\n );\n }\n\n if (this.metadata !== null) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(this.metadata);\n } catch (error) {\n throw new Error(\n `Playbook \"${this.key}\" has invalid metadata JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(\n `Playbook \"${this.key}\" metadata must be a JSON object`,\n );\n }\n }\n\n if (this.planes !== null) {\n const parsedPlanes = parsePlanes(this.planes);\n if (!parsedPlanes || parsedPlanes.length === 0) {\n throw new Error(\n `Playbook \"${this.key}\" planes must be a non-empty JSON array of plane names`,\n );\n }\n }\n\n if (\n this.onStepFailure !== null &&\n this.onStepFailure !== 'abort' &&\n this.onStepFailure !== 'continue'\n ) {\n throw new Error(\n `Playbook \"${this.key}\" onStepFailure must be \"abort\" or \"continue\"`,\n );\n }\n\n const lowerLayers = await this.getLowerPrecedenceLayers();\n const currentLayer = this.toPlaybookLayer();\n const lowerMerged = mergePlaybookLayers(definition, ...lowerLayers);\n\n // Enablement is one-directional: a layer may disable, never re-enable\n // something a lower layer disabled.\n if (this.enabled === true && !lowerMerged.enabled) {\n throw new Error(\n `Playbook \"${this.key}\" is disabled by a lower layer and cannot be re-enabled by an override`,\n );\n }\n\n // Plane validity narrows only; an override cannot claim a plane the lower\n // layers never declared.\n if (currentLayer.planes) {\n const widened = currentLayer.planes.filter(\n (plane) => !lowerMerged.planes.includes(plane),\n );\n if (widened.length > 0) {\n throw new Error(\n `Playbook \"${this.key}\" cannot add plane(s) ${widened.join(', ')} that no lower layer declares`,\n );\n }\n }\n }\n\n private async getLowerPrecedenceLayers(): Promise<PlaybookLayer[]> {\n const config = getPlaybookConfig();\n const layers: PlaybookLayer[] = [\n normalizePlaybookLayer(\n config.playbooks?.[this.key] as PlaybookConfigOverrideInput | undefined,\n `Playbook \"${this.key}\" config override`,\n ),\n ];\n\n const { PlaybookOverrideCollection } = await import(\n '../collections/PlaybookOverrideCollection.js'\n );\n const collection = await PlaybookOverrideCollection.create({\n db: this.options.db ?? this.options.persistence,\n });\n\n if (this.tenantId) {\n const appOverride = await collection.getAppOverride(this.key, {\n excludeId: this.id ?? undefined,\n });\n if (appOverride) {\n layers.push(appOverride.toPlaybookLayer());\n }\n }\n\n return layers;\n }\n\n private normalizeForPersistence(): void {\n const rawMetadata = this.metadata as unknown;\n if (rawMetadata === undefined) {\n this.metadata = null;\n } else if (\n rawMetadata !== null &&\n typeof rawMetadata === 'object' &&\n !Array.isArray(rawMetadata)\n ) {\n this.metadata = serializeMetadata(rawMetadata as PlaybookMetadata);\n } else if (rawMetadata !== null && typeof rawMetadata !== 'string') {\n throw new Error(\n `Playbook \"${this.key}\" metadata must be a JSON object or JSON string`,\n );\n }\n\n const rawPlanes = this.planes as unknown;\n if (rawPlanes === undefined) {\n this.planes = null;\n } else if (Array.isArray(rawPlanes)) {\n this.planes = serializePlanes(rawPlanes as PlaybookPlane[]);\n } else if (rawPlanes !== null && typeof rawPlanes !== 'string') {\n throw new Error(\n `Playbook \"${this.key}\" planes must be an array or JSON string`,\n );\n }\n\n // Enablement is a gate, so it is validated before it is ever persisted.\n // A stringly-typed `'false'` arriving through the untyped collection API\n // would otherwise hydrate as a truthy string and resolve as enabled.\n if (this.enabled === undefined) {\n this.enabled = null;\n } else if (this.enabled !== null && typeof this.enabled !== 'boolean') {\n throw new Error(\n `Playbook \"${this.key}\" enabled must be a boolean or null, received ${typeof this.enabled} \"${String(this.enabled)}\"`,\n );\n }\n }\n\n private async getPersistedIdentity(): Promise<PlaybookOverrideIdentity | null> {\n if (!this.id) {\n return null;\n }\n\n const existing = await this.db.get(this.tableName, { id: this.id });\n if (!existing) {\n return null;\n }\n\n const row = existing as Record<string, unknown>;\n return {\n key: String(row.key ?? this.key),\n tenantId:\n row.tenantId !== undefined\n ? (row.tenantId as string | null)\n : ((row.tenant_id as string | null | undefined) ?? null),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { PlaybookOverride } from '../models/PlaybookOverride.js';\n\nexport class PlaybookOverrideCollection extends SmrtCollection<PlaybookOverride> {\n static readonly _itemClass = PlaybookOverride;\n\n private excludeOverrideId(\n items: PlaybookOverride[],\n excludeId?: string,\n ): PlaybookOverride[] {\n return items.filter((item) => (excludeId ? item.id !== excludeId : true));\n }\n\n async getAppOverride(\n key: string,\n options: { excludeId?: string } = {},\n ): Promise<PlaybookOverride | null> {\n const items = await this.list({ where: { key, tenantId: null } });\n return this.excludeOverrideId(items, options.excludeId)[0] ?? null;\n }\n\n async getTenantOverride(\n key: string,\n tenantId: string,\n options: { excludeId?: string } = {},\n ): Promise<PlaybookOverride | null> {\n const items = await this.list({ where: { key, tenantId } });\n return this.excludeOverrideId(items, options.excludeId)[0] ?? null;\n }\n\n async getResolutionLayers(\n key: string,\n tenantId?: string | null,\n options: { excludeId?: string } = {},\n ): Promise<{\n app: PlaybookOverride | null;\n tenant: PlaybookOverride | null;\n }> {\n const [app, tenant] = await Promise.all([\n this.getAppOverride(key, options),\n tenantId != null\n ? this.getTenantOverride(key, tenantId, options)\n : Promise.resolve(null),\n ]);\n\n return { app, tenant };\n }\n}\n","import { getPackageConfig } from '@happyvertical/smrt-config';\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport {\n getCachedPlaybookBase,\n getPlaybookCacheGeneration,\n setCachedPlaybookBase,\n} from './cache.js';\nimport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nimport { PlaybookRegistry } from './playbook-registry.js';\nimport type {\n PlaybookCacheValue,\n PlaybookDefinition,\n PlaybookPackageConfig,\n PlaybookPlan,\n PlaybookPlane,\n PlaybookPlanStep,\n PlaybookRejection,\n PlaybookResolution,\n ResolvePlaybookOptions,\n} from './types.js';\nimport {\n applyCapabilityDeclaration,\n isCapabilityDeclarationComplete,\n mergePlaybookLayers,\n normalizePlaybookLayer,\n} from './utils.js';\n\n/** An intent record that declares no planes is browser-only (fail closed). */\nconst DEFAULT_INTENT_PLANES: readonly PlaybookPlane[] = Object.freeze([\n 'browser',\n]);\n\nfunction getPlaybookConfig(): PlaybookPackageConfig {\n return getPackageConfig<PlaybookPackageConfig>('playbooks', {\n playbooks: {},\n });\n}\n\nfunction reject(\n key: string,\n reason: PlaybookRejection['reason'],\n message: string,\n stepIndex?: number,\n): PlaybookRejection {\n return {\n ok: false,\n reason,\n message,\n key,\n ...(stepIndex === undefined ? {} : { stepIndex }),\n };\n}\n\nasync function loadPlaybookBase(\n definition: PlaybookDefinition,\n options: ResolvePlaybookOptions,\n): Promise<PlaybookCacheValue> {\n const key = definition.key;\n const tenantId =\n options.tenantId !== undefined ? options.tenantId : (getTenantId() ?? null);\n\n let collection: PlaybookOverrideCollection | null = null;\n let cacheDb: ResolvePlaybookOptions['db'] | undefined = options.db;\n if (options.db) {\n const initializedCollection = await PlaybookOverrideCollection.create({\n db: options.db,\n });\n collection = initializedCollection;\n cacheDb = initializedCollection.db as ResolvePlaybookOptions['db'];\n }\n\n const cached = getCachedPlaybookBase(key, tenantId, cacheDb);\n if (cached) {\n return cached;\n }\n\n // Capture the invalidation generation before the asynchronous layer loads. A\n // write that lands while they are in flight bumps it, and the cache write\n // below is then refused rather than repopulating the key the write just\n // invalidated with the pre-write value.\n const loadedAtGeneration = getPlaybookCacheGeneration(key, cacheDb);\n\n const config = getPlaybookConfig();\n const layers = [\n normalizePlaybookLayer(\n config.playbooks?.[key],\n `Playbook \"${key}\" config override`,\n ),\n ];\n\n if (collection) {\n const stored = await collection.getResolutionLayers(key, tenantId);\n\n if (stored.app) {\n layers.push(stored.app.toPlaybookLayer());\n }\n\n if (stored.tenant) {\n layers.push(stored.tenant.toPlaybookLayer());\n }\n }\n\n const merged = mergePlaybookLayers(definition, ...layers);\n // Frozen: this object is shared by every resolution that hits the cache.\n const value: PlaybookCacheValue = Object.freeze({\n key,\n title: merged.title,\n description: merged.description,\n planes: merged.planes,\n onStepFailure: merged.onStepFailure,\n enabled: merged.enabled,\n metadata: merged.metadata,\n });\n\n setCachedPlaybookBase(key, tenantId, cacheDb, value, loadedAtGeneration);\n return value;\n}\n\n/**\n * Resolves a playbook to a plan for a caller on a given plane.\n *\n * Returns a discriminated result rather than throwing: resolution fails closed\n * and every rejection names a specific reason. Nothing here executes a step —\n * the plan is followed step by step by the agent, and each step is authorized\n * independently at the REST boundary or by `PrincipalRun.assertToolAllowed()`.\n */\nexport async function resolvePlaybook(\n key: string,\n options: ResolvePlaybookOptions = {},\n): Promise<PlaybookResolution> {\n const definition = PlaybookRegistry.get(key);\n if (!definition) {\n return reject(key, 'unknown-playbook', `Unknown playbook key \"${key}\"`);\n }\n\n const base = await loadPlaybookBase(definition, options);\n const runtimeOverride = normalizePlaybookLayer(\n options.override,\n `Playbook \"${key}\" runtime override`,\n );\n const merged = mergePlaybookLayers(\n {\n ...definition,\n title: base.title,\n description: base.description,\n planes: base.planes,\n onStepFailure: base.onStepFailure,\n enabled: base.enabled,\n metadata: base.metadata,\n },\n runtimeOverride,\n );\n\n if (!merged.enabled) {\n return reject(\n key,\n 'disabled',\n `Playbook \"${key}\" is disabled for this scope`,\n );\n }\n\n const plane = options.plane ?? 'server';\n if (!merged.planes.includes(plane)) {\n return reject(\n key,\n 'plane-not-declared',\n `Playbook \"${key}\" is not valid on the \"${plane}\" plane; it declares ${merged.planes.join(', ')}`,\n );\n }\n\n const steps: PlaybookPlanStep[] = [];\n\n for (const [index, step] of definition.steps.entries()) {\n if (step.kind === 'operation') {\n // The step never classifies itself: the host supplies the classification\n // emitted for the referenced operation, and anything undeclared resolves\n // to the fail-closed default.\n const declaration = options.classifier?.(step) ?? null;\n steps.push({\n index,\n step,\n classification: applyCapabilityDeclaration(declaration),\n classificationDeclared: isCapabilityDeclarationComplete(declaration),\n });\n continue;\n }\n\n // Seam for #2588: without a declared-intent registry there is no identity\n // to resolve against, so an intent step fails closed rather than being\n // assumed valid.\n if (!options.intents) {\n return reject(\n key,\n 'intent-registry-unavailable',\n `Playbook \"${key}\" step ${index} references view intent \"${step.id}\", but no intent registry was supplied (see #2588)`,\n index,\n );\n }\n\n const record = options.intents(step.id);\n if (!record) {\n return reject(\n key,\n 'unknown-intent',\n `Playbook \"${key}\" step ${index} references unknown view intent \"${step.id}\"`,\n index,\n );\n }\n\n // Fail closed: an intent record that does not declare its planes is\n // browser-only, the same default an intent-bearing playbook gets. Server\n // validity rides the #2446 command/ack bridge and must be declared at both\n // the playbook and the intent, never assumed from silence.\n const intentPlanes = record.planes ?? DEFAULT_INTENT_PLANES;\n if (!intentPlanes.includes(plane)) {\n return reject(\n key,\n 'intent-plane-not-declared',\n `Playbook \"${key}\" step ${index} references view intent \"${step.id}\", which is not valid on the \"${plane}\" plane`,\n index,\n );\n }\n\n steps.push({\n index,\n step,\n classification: applyCapabilityDeclaration(record.classification),\n classificationDeclared: isCapabilityDeclarationComplete(\n record.classification,\n ),\n });\n }\n\n // A plan is a read-only description of what the agent is about to do; the\n // policy fields it carries are shared with the cache, so freeze it rather\n // than hand a caller a handle that can widen a gate after the fact.\n const plan: PlaybookPlan = Object.freeze({\n key,\n title: merged.title,\n description: merged.description,\n plane,\n planes: merged.planes,\n onStepFailure: merged.onStepFailure,\n metadata: merged.metadata,\n // Steps come from the code definition alone. No layer can supply them.\n steps: Object.freeze(steps),\n });\n\n return { ok: true, plan };\n}\n","/**\n * Preflight result cache (issue #2590).\n *\n * Preflight predicts; it never grants. Every step re-enforces at execution, and\n * that constraint is exactly what makes this cache free:\n *\n * - a stale `allow` costs a correctly-denied step at execution;\n * - a stale `deny` costs a briefly hidden capability that expires on the TTL.\n *\n * Neither is a security event, so results are cached per\n * `(principal, key, plane, tenant)` with a short TTL and **no invalidation\n * ceremony** beyond the playbook cache's own per-`(db, key)` generation counter:\n * a result captured under generation N is dropped once an override write has\n * bumped the counter past N. There is deliberately no per-principal invalidation\n * on a permission change — the TTL is the whole contract, because execution\n * re-enforces regardless.\n */\n\nimport { getPlaybookCacheGeneration, playbookCacheNamespace } from './cache.js';\nimport type { PlaybookPreflightReport } from './preflight-types.js';\nimport type { PlaybookPlane } from './types.js';\n\nconst PREFLIGHT_CACHE_TTL_MS = 15_000;\n\n/**\n * Hard ceiling on retained entries. The browser route is reachable without app\n * auth by design (it never invokes `authMiddleware`), so the cache must not be\n * a memory sink an anonymous caller can grow. Unknown keys are not cached at\n * all; this bounds everything else.\n */\nconst PREFLIGHT_CACHE_MAX_ENTRIES = 1_000;\n\n/** Identity a cached preflight result is stored under. */\nexport interface PreflightCacheScope {\n /** Opaque, caller-scoped principal partition. Never consulted for authority. */\n principal: string;\n key: string;\n plane: PlaybookPlane;\n /**\n * Tenant the result was resolved for. Part of the key because a tenant\n * override can disable a playbook or narrow its planes — the base playbook\n * cache scopes by tenant for the same reason.\n */\n tenantId: string | null;\n db: unknown;\n}\n\ninterface PreflightCacheEntry {\n expiresAt: number;\n generation: number;\n report: PlaybookPreflightReport;\n}\n\nconst preflightCache = new Map<string, PreflightCacheEntry>();\n\nfunction buildKey(scope: PreflightCacheScope): string {\n return `${playbookCacheNamespace(scope.db)}::${scope.principal}::${scope.key}::${scope.plane}::${scope.tenantId ?? 'app'}`;\n}\n\n/** Drops expired entries, then the oldest insertions if still over the cap. */\nfunction evict(): void {\n const now = Date.now();\n for (const [cacheKey, entry] of preflightCache) {\n if (entry.expiresAt <= now) {\n preflightCache.delete(cacheKey);\n }\n }\n\n // Map iteration is insertion-ordered, so this drops the oldest first.\n for (const cacheKey of preflightCache.keys()) {\n if (preflightCache.size <= PREFLIGHT_CACHE_MAX_ENTRIES) {\n break;\n }\n preflightCache.delete(cacheKey);\n }\n}\n\n/** TTL applied to every cached preflight result. */\nexport function getPlaybookPreflightCacheTtlMs(): number {\n return PREFLIGHT_CACHE_TTL_MS;\n}\n\n/**\n * Reads a cached preflight result.\n *\n * A result captured before an override write (a lower generation) is dropped\n * rather than served, so an override change is reflected without waiting out\n * the TTL.\n */\nexport function getCachedPlaybookPreflight(\n scope: PreflightCacheScope,\n): PlaybookPreflightReport | null {\n const cacheKey = buildKey(scope);\n const entry = preflightCache.get(cacheKey);\n if (!entry) {\n return null;\n }\n\n if (\n entry.expiresAt <= Date.now() ||\n entry.generation !== getPlaybookCacheGeneration(scope.key, scope.db)\n ) {\n preflightCache.delete(cacheKey);\n return null;\n }\n\n return entry.report;\n}\n\n/**\n * Stores a preflight result. `loadedAtGeneration` is the generation captured\n * *before* resolution started: a write that landed while it was in flight makes\n * the result stale on arrival, and it is dropped rather than cached.\n */\nexport function setCachedPlaybookPreflight(\n scope: PreflightCacheScope,\n report: PlaybookPreflightReport,\n loadedAtGeneration: number,\n): void {\n if (getPlaybookCacheGeneration(scope.key, scope.db) !== loadedAtGeneration) {\n return;\n }\n\n preflightCache.set(buildKey(scope), {\n expiresAt: Date.now() + PREFLIGHT_CACHE_TTL_MS,\n generation: loadedAtGeneration,\n report,\n });\n\n if (preflightCache.size > PREFLIGHT_CACHE_MAX_ENTRIES) {\n evict();\n }\n}\n\n/** Drops every cached preflight result. Use in tests. */\nexport function clearPlaybookPreflightCache(): void {\n preflightCache.clear();\n}\n","/**\n * Playbook preflight (issue #2590).\n *\n * Playbooks are not atomic and have no compensation, so the real failure mode\n * is half-execution — five steps started, dead at four. Preflight is what\n * prevents that, and secondarily lets a tool listing hide playbooks the caller\n * cannot run.\n *\n * **Preflight predicts; it never grants.** Every step re-enforces at execution,\n * unconditionally — at the REST boundary in the browser, and by\n * `PrincipalRun.assertToolAllowed()` / `assertOperation()` server-side. Without\n * that, a permission revoked mid-playbook would leave a cached \"allowed\"\n * standing, which is a time-of-check/time-of-use bypass. Nothing in this module\n * executes, authorizes, or caches an authorization.\n *\n * The two planes are deliberately asymmetric:\n *\n * - **Server** is a predicate that already exists. `PrincipalRun` exposes\n * `isToolAllowed(tool): boolean` alongside the throwing `assertToolAllowed()`,\n * and `assertOperation` wraps `assertOperationPermission`. Preflight is the\n * intersection of each step against those two, evaluated and never executed.\n * - **Browser** cannot reuse the generated REST auth contract at all.\n * `authMiddleware` is `(objectName, action) => (req) => Promise<Request |\n * Response>`: request-bound, `Response`-returning rather than boolean, and\n * free to consult session stores, rate-limit, or audit. It is not a dry-run\n * predicate, so preflight covers the **static layers only** — action exposure,\n * `public` access, and field-level permission slugs — and reports the app-auth\n * layer as `unknown`. It never invokes `authMiddleware`, synthetically or\n * otherwise.\n */\n\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport { getPlaybookCacheGeneration } from './cache.js';\nimport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nimport { resolvePlaybook } from './playbook-resolver.js';\nimport {\n getCachedPlaybookPreflight,\n setCachedPlaybookPreflight,\n} from './preflight-cache.js';\nimport type {\n BrowserPreflightOptions,\n PlaybookPreflightAvailableReport,\n PlaybookPreflightReport,\n PlaybookPreflightRequest,\n PlaybookPreflightUnavailableReport,\n PreflightLayerReport,\n PreflightStepEvaluator,\n PreflightStepReport,\n PreflightSummary,\n PreflightVerdict,\n ServerPreflightOptions,\n} from './preflight-types.js';\nimport type {\n PlaybookOperationStep,\n PlaybookPlan,\n PlaybookPlanStep,\n ResolvePlaybookOptions,\n} from './types.js';\n\n/**\n * The one response every unresolvable playbook produces.\n *\n * Frozen and shared so an unknown key and an unauthorized key are byte-for-byte\n * identical: same shape, same values, no key echo, no reason code, no message.\n * Preflight answers only for playbooks resolvable through the caller's own\n * layer chain, and says nothing about anything else — including whether it\n * exists.\n */\nexport const PLAYBOOK_PREFLIGHT_UNAVAILABLE: PlaybookPreflightUnavailableReport =\n Object.freeze({\n available: false,\n advisory: true,\n verdict: 'deny',\n steps: Object.freeze([]),\n summary: Object.freeze({ allow: 0, deny: 0, unknown: 0 }),\n }) as PlaybookPreflightUnavailableReport;\n\nconst VERDICT_RANK: Record<PreflightVerdict, number> = {\n allow: 0,\n unknown: 1,\n deny: 2,\n};\n\n/** Fail-closed combination: `deny` beats `unknown` beats `allow`. */\nexport function worstVerdict(\n left: PreflightVerdict,\n right: PreflightVerdict,\n): PreflightVerdict {\n return VERDICT_RANK[right] > VERDICT_RANK[left] ? right : left;\n}\n\nfunction combineLayers(\n layers: readonly PreflightLayerReport[],\n): Pick<PreflightStepReport, 'verdict' | 'reason'> {\n let verdict: PreflightVerdict = 'allow';\n for (const layer of layers) {\n verdict = worstVerdict(verdict, layer.verdict);\n }\n if (!layers.length) {\n // An evaluator that reports nothing has told us nothing. This is the one\n // function whose whole job is fail-closed combination, so it must not\n // answer `allow` by default for a custom evaluator that returns no layers.\n return { verdict: 'unknown', reason: 'not-evaluated' };\n }\n // The step's reason is the first layer that produced the aggregate verdict,\n // so the reason and the verdict can never describe different layers.\n const source = layers.find((layer) => layer.verdict === verdict);\n return { verdict, reason: source?.reason ?? 'ok' };\n}\n\nfunction summarize(steps: readonly PreflightStepReport[]): PreflightSummary {\n const summary: PreflightSummary = { allow: 0, deny: 0, unknown: 0 };\n for (const step of steps) {\n summary[step.verdict] += 1;\n }\n return summary;\n}\n\n/**\n * Decomposes a resolved plan into per-step verdicts using `evaluate`.\n *\n * Every step is evaluated — evaluation does not short-circuit on the first\n * denial, because the point of preflight is to show the caller *which* step of\n * five would die, not merely that one would.\n */\nexport async function preflightPlan(\n plan: PlaybookPlan,\n evaluate: PreflightStepEvaluator,\n): Promise<PlaybookPreflightAvailableReport> {\n const steps: PreflightStepReport[] = [];\n\n for (const planStep of plan.steps) {\n const { layers } = await evaluate(planStep);\n steps.push(\n Object.freeze({\n index: planStep.index,\n kind: planStep.step.kind,\n ...combineLayers(layers),\n layers: Object.freeze([...layers]),\n }),\n );\n }\n\n let verdict: PreflightVerdict = 'allow';\n for (const step of steps) {\n verdict = worstVerdict(verdict, step.verdict);\n }\n\n return Object.freeze({\n available: true,\n advisory: true,\n key: plan.key,\n plane: plan.plane,\n title: plan.title,\n description: plan.description,\n verdict,\n steps: Object.freeze(steps),\n summary: Object.freeze(summarize(steps)),\n }) as PlaybookPreflightAvailableReport;\n}\n\n/**\n * Pays the override-layer read cost a resolvable playbook would have paid.\n *\n * `resolvePlaybook()` short-circuits an unknown key before it touches the\n * override table, while a key that exists but is disabled, wrong-plane, or\n * otherwise unresolvable reads the layers first. Left alone, that difference is\n * a timing oracle over exactly the question the uniform response exists to\n * refuse. Running the same read on the unknown path puts both in the same\n * timing class (one override round-trip, then the shared frozen response).\n *\n * Best-effort by construction: preflight is advisory, so a failure here changes\n * nothing about the answer.\n */\nasync function equalizeUnknownKeyCost(\n key: string,\n options: ResolvePlaybookOptions | undefined,\n): Promise<void> {\n if (!options?.db) {\n return;\n }\n\n try {\n const collection = await PlaybookOverrideCollection.create({\n db: options.db,\n });\n // Same tenant resolution the real path uses, since matching its cost is the\n // entire purpose of this call.\n const tenantId =\n options.tenantId !== undefined\n ? options.tenantId\n : (getTenantId() ?? null);\n await collection.getResolutionLayers(key, tenantId);\n } catch {\n // Advisory only — never let the equalizer change the answer or throw.\n }\n}\n\n/**\n * Resolves the database identity the playbook cache partitions on.\n *\n * `loadPlaybookBase()` normalizes a db *config object* into the initialized\n * collection's live handle before it touches the cache, and `invalidatePlaybookCache`\n * is likewise called with the live handle. Capturing the generation from the raw\n * option would put reads and invalidations in two different buckets, and the\n * generation guard would silently never fire — degrading \"an override write is\n * reflected without waiting out the TTL\" back to plain TTL.\n */\nasync function normalizeCacheDb(\n db: ResolvePlaybookOptions['db'],\n): Promise<unknown> {\n if (!db) {\n return db;\n }\n\n try {\n const collection = await PlaybookOverrideCollection.create({ db });\n return collection.db;\n } catch {\n return db;\n }\n}\n\n/**\n * Preflights one playbook for one caller on one plane.\n *\n * Resolves through the **caller's own layer chain** (their tenant, their\n * overrides, their plane), decomposes the plan, and evaluates each step with the\n * plane's evaluator. Any playbook the chain cannot resolve — unknown, disabled,\n * wrong plane, unresolvable intent — returns the single\n * {@link PLAYBOOK_PREFLIGHT_UNAVAILABLE} value.\n *\n * Results are cached per `(principal, key, plane)` under the playbook cache's\n * generation counter; see `preflight-cache.ts` for why that is safe.\n */\nexport async function preflightPlaybook(\n request: PlaybookPreflightRequest,\n): Promise<PlaybookPreflightReport> {\n const { key, plane, principal, resolve, evaluate } = request;\n // The SAME fallback `loadPlaybookBase()` uses. `undefined` means \"take the\n // ambient tenant\", and hardcoding `null` here would let two ambient\n // `withTenant()` callers with the same principal share one cache entry\n // despite resolving different tenant overrides — a cross-tenant read of a\n // report that carries the resolved title and description.\n const tenantId =\n resolve?.tenantId !== undefined\n ? resolve.tenantId\n : (getTenantId() ?? null);\n const db = await normalizeCacheDb(resolve?.db);\n const scope = { principal, key, plane, tenantId, db };\n\n const cached = getCachedPlaybookPreflight(scope);\n if (cached) {\n return cached;\n }\n\n const loadedAtGeneration = getPlaybookCacheGeneration(key, db);\n\n try {\n const resolution = await resolvePlaybook(key, { ...resolve, plane });\n\n if (!resolution.ok) {\n if (resolution.reason === 'unknown-playbook') {\n await equalizeUnknownKeyCost(key, resolve);\n }\n // Unknown and registered-but-unavailable keys are cached alike. Caching\n // only one of them would put a repeat probe of the other in a different\n // timing class — the equalizer closes the first-request gap, and this\n // closes the every-subsequent-request gap. Unbounded growth from probing\n // random keys is handled where it belongs: the cache is capped with\n // expiry-first eviction.\n setCachedPlaybookPreflight(\n scope,\n PLAYBOOK_PREFLIGHT_UNAVAILABLE,\n loadedAtGeneration,\n );\n return PLAYBOOK_PREFLIGHT_UNAVAILABLE;\n }\n\n const report = await preflightPlan(resolution.plan, evaluate);\n setCachedPlaybookPreflight(scope, report, loadedAtGeneration);\n return report;\n } catch {\n // A failure anywhere in resolution or evaluation — a dropped connection, a\n // missing override table — must not become an existence oracle: an unknown\n // key answers from memory and could never fail here, so a thrown error\n // would mark every key that *does* exist. Answer uniformly, and do NOT\n // cache it: a transient fault must not pin a deny for the whole TTL.\n return PLAYBOOK_PREFLIGHT_UNAVAILABLE;\n }\n}\n\nfunction layer(\n layerName: PreflightLayerReport['layer'],\n verdict: PreflightVerdict,\n reason: PreflightLayerReport['reason'],\n missingPermissions?: readonly string[],\n): PreflightLayerReport {\n return Object.freeze({\n layer: layerName,\n verdict,\n reason,\n ...(missingPermissions?.length\n ? { missingPermissions: Object.freeze([...missingPermissions]) }\n : {}),\n });\n}\n\n/**\n * Server-plane evaluator: the intersection of the persona tool allow-list and\n * the operation-permission predicate, per step, with nothing executed.\n *\n * An intent step is reported against its **declared** plane validity: an intent\n * that does not declare `server` is denied with reason `plane`, and one that\n * does is `unknown`, because server validity rides the #2446 command/ack bridge\n * whose acknowledgement is not statically knowable. This repeats a check\n * resolution already made, deliberately — the same defense-in-depth shape as\n * `assertToolAllowed()` gating both the tool offer and its execution.\n */\nexport function createServerStepEvaluator(\n options: ServerPreflightOptions,\n): PreflightStepEvaluator {\n return async (planStep: PlaybookPlanStep) => {\n const step = planStep.step;\n\n if (step.kind === 'intent') {\n const planes = options.intentPlanes?.(step.id) ?? null;\n const serverValid = planes?.includes('server') ?? false;\n return {\n layers: [\n serverValid\n ? layer('plane', 'unknown', 'intent-bridge-not-evaluated')\n : layer('plane', 'deny', 'plane'),\n ],\n };\n }\n\n const operation: PlaybookOperationStep = step;\n const toolLayer = options.isToolAllowed(operation)\n ? layer('tool-allowlist', 'allow', 'ok')\n : layer('tool-allowlist', 'deny', 'tool-not-allowed');\n\n const permissionVerdict = await options.checkOperationPermission(operation);\n const permissionLayer =\n permissionVerdict === 'allow'\n ? layer('operation-permission', 'allow', 'ok')\n : permissionVerdict === 'deny'\n ? layer('operation-permission', 'deny', 'permission-denied')\n : layer('operation-permission', 'unknown', 'permission-unknown');\n\n return { layers: [toolLayer, permissionLayer] };\n };\n}\n\n/**\n * Browser-plane evaluator: the static layers only.\n *\n * - `action-exposure` — `isApiActionEnabled`. A disabled action is a hard,\n * statically knowable deny (the route answers 405).\n * - `public-access` — `isRoutePublic`. A non-public route with **no** app auth\n * middleware wired is a hard deny (the generator's fail-closed 401); with one\n * wired it is `unknown`, because whether that middleware passes is exactly\n * what preflight refuses to guess.\n * - `field-permissions` — the model's declared field read-permission slugs\n * against the caller's published set. Missing slugs redact fields rather than\n * fail the step, so the verdict stays `allow` and carries\n * `reason: 'fields-redacted'` plus the missing slugs; an unpublished\n * permission set reports `unknown`.\n * - `app-auth` — `unknown` whenever a middleware is wired. This is the layer an\n * `authPredicate` seam would later fill in.\n *\n * An intent step never crosses the REST boundary at all, so it is reported\n * `unknown` against `intent-mount`: whether the surface is mounted and what its\n * staged-control review does are runtime facts of the page, not of the build.\n */\nexport function createBrowserStepEvaluator(\n options: BrowserPreflightOptions,\n): PreflightStepEvaluator {\n const held = options.permissions ? new Set(options.permissions) : null;\n\n return (planStep: PlaybookPlanStep) => {\n const step = planStep.step;\n\n if (step.kind === 'intent') {\n return {\n layers: [layer('intent-mount', 'unknown', 'intent-not-mounted')],\n };\n }\n\n const { model, action } = step;\n const layers: PreflightLayerReport[] = [];\n\n const exposed = options.layers.isActionExposed(model, action);\n layers.push(\n exposed\n ? layer('action-exposure', 'allow', 'ok')\n : layer('action-exposure', 'deny', 'action-not-exposed'),\n );\n\n const isPublic = options.layers.isRoutePublic(model, action);\n const appAuthConfigured = options.layers.appAuthConfigured;\n layers.push(\n isPublic\n ? layer('public-access', 'allow', 'ok')\n : appAuthConfigured\n ? layer('public-access', 'unknown', 'auth-required')\n : layer('public-access', 'deny', 'not-public'),\n );\n\n const required = options.layers.requiredFieldPermissions(model, action);\n if (!required.length) {\n layers.push(layer('field-permissions', 'allow', 'ok'));\n } else if (!held) {\n layers.push(\n layer('field-permissions', 'unknown', 'field-permissions-unknown'),\n );\n } else {\n const missing = required.filter((slug) => !held.has(slug));\n layers.push(\n missing.length\n ? // Missing a field read permission redacts the field; it does not\n // fail the step. Reporting `deny` here would predict a failure that\n // will not happen.\n layer('field-permissions', 'allow', 'fields-redacted', missing)\n : layer('field-permissions', 'allow', 'ok'),\n );\n }\n\n layers.push(\n appAuthConfigured\n ? layer('app-auth', 'unknown', 'app-auth-not-evaluated')\n : layer('app-auth', 'allow', 'app-auth-not-configured'),\n );\n\n return { layers };\n };\n}\n","/**\n * Browser-plane preflight wiring for the generated REST surface (issue #2590).\n *\n * Binds the plane-agnostic evaluator in `preflight.ts` to the static layers\n * `@happyvertical/smrt-core` exposes, and produces the provider the generated\n * `_preflight` route takes as a seam. Core owns the route and the layer facts;\n * this package owns resolution and the verdict vocabulary, so the dependency\n * stays one-way (playbooks → core).\n *\n * Nothing here can reach `authMiddleware`: the provider is handed a boolean\n * `appAuthConfigured` and no auth handle at all.\n */\n\nimport {\n isApiActionEnabledForObject,\n isRestActionRoutable,\n isRestRoutePublic,\n resolveRegisteredObjectName,\n restFieldReadPermissions,\n restMethodForApiAction,\n} from '@happyvertical/smrt-core';\nimport { createBrowserStepEvaluator, preflightPlaybook } from './preflight.js';\nimport type {\n BrowserPreflightLayerSource,\n PlaybookPreflightReport,\n} from './preflight-types.js';\nimport type { ResolvePlaybookOptions } from './types.js';\n\n/**\n * A layer source reading the generated REST surface's own static rules.\n *\n * A model this build does not register resolves to no object name, and every\n * layer then fails closed: an unregistered model has no route, so a step naming\n * one can only die.\n */\nexport function createRestPreflightLayerSource(options: {\n appAuthConfigured: boolean;\n}): BrowserPreflightLayerSource {\n return {\n appAuthConfigured: options.appAuthConfigured,\n isActionExposed(model, action) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return false;\n // Two questions, both required: does a route for this action exist at\n // all, and does the decorator expose it? `include`/`exclude` alone would\n // report a typo'd or removed custom action as exposed, and the runtime\n // dispatcher — which iterates only declared `api.routes` — could never\n // run it.\n return (\n isRestActionRoutable(objectName, action) &&\n isApiActionEnabledForObject(objectName, action)\n );\n },\n isRoutePublic(model, action) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return false;\n // The object name is threaded through so a custom action declaring its\n // own HTTP verb is predicted against that verb, not a guessed POST.\n return isRestRoutePublic(\n objectName,\n restMethodForApiAction(action, objectName),\n );\n },\n requiredFieldPermissions(model) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return [];\n return restFieldReadPermissions(objectName);\n },\n };\n}\n\nexport interface BrowserPreflightProviderOptions {\n /**\n * Resolution options shared by every request — classifier, intent registry,\n * and any host-level runtime override. The per-request database handle and\n * tenant come from the route.\n */\n resolve?: Omit<ResolvePlaybookOptions, 'plane' | 'db'>;\n /**\n * Derives the cache-partitioning principal identity for a request. Opaque and\n * never echoed; defaults to the caller's permission slugs, which is the only\n * caller-distinguishing input the static layers actually read.\n */\n principal?: (request: {\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n }) => string;\n}\n\nfunction defaultPrincipal(request: {\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n}): string {\n // Every input the browser evaluation actually reads has to be in here, or the\n // process-global cache can serve one context's report to another:\n //\n // - an ABSENT permission set is not an empty one. Absent makes the field\n // layer `unknown`; an explicitly published empty set is known, and answers\n // `allow` with redactions. Both would render as a bare `perm:` without the\n // sentinel.\n // - `appAuthConfigured` decides whether `public-access` and `app-auth` report\n // `deny`/`allow` or `unknown`. Two generators in one process — one with an\n // auth middleware, one without — otherwise share entries.\n const permissions = request.permissions\n ? `perm:${[...request.permissions].sort().join(',')}`\n : 'perm:unpublished';\n return `${permissions}|auth:${request.appAuthConfigured ? 'wired' : 'none'}`;\n}\n\n/**\n * Builds the provider wired into `APIConfig.playbookPreflight`.\n *\n * ```typescript\n * const api = new APIGenerator(manifest, {\n * playbookPreflight: createBrowserPlaybookPreflight(),\n * });\n * ```\n *\n * Every unresolvable key — unknown, disabled, browser-invalid, unresolvable\n * intent — comes back as the single uniform unavailable report, so the endpoint\n * is not an enumeration oracle.\n */\nexport function createBrowserPlaybookPreflight(\n options: BrowserPreflightProviderOptions = {},\n): (request: {\n key: string;\n plane: 'browser';\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n db?: unknown;\n}) => Promise<PlaybookPreflightReport> {\n return async (request) => {\n const layers = createRestPreflightLayerSource({\n appAuthConfigured: request.appAuthConfigured,\n });\n return preflightPlaybook({\n key: request.key,\n plane: 'browser',\n principal: (options.principal ?? defaultPrincipal)(request),\n resolve: {\n ...options.resolve,\n ...(request.db === undefined\n ? {}\n : { db: request.db as ResolvePlaybookOptions['db'] }),\n },\n evaluate: createBrowserStepEvaluator({\n layers,\n permissions: request.permissions ?? null,\n }),\n });\n };\n}\n","/**\n * @happyvertical/smrt-playbooks\n *\n * Code-first playbooks — named, described, layered step sequences an agent\n * follows — with config, app-level, tenant-level, and runtime overrides.\n *\n * A playbook resolves to a plan the agent executes step by step. Nothing in\n * this package executes a step, so a playbook is never an authority boundary:\n * every step is authorized independently at the REST boundary or, server-side,\n * by `PrincipalRun.assertToolAllowed()`.\n *\n * @packageDocumentation\n */\n\n// Self-register this package's manifest before any @smrt() decorator fires\n// downstream. Must come first so the side effect runs ahead of the class\n// module loads below. See __smrt-register__.ts for issue #1132 context.\nimport './__smrt-register__.js';\n\nexport { clearPlaybookCache, getPlaybookCacheTtlMs } from './cache.js';\nexport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nexport {\n PlaybookOverride,\n type PlaybookOverrideOptions,\n} from './models/PlaybookOverride.js';\nexport { definePlaybook, PlaybookRegistry } from './playbook-registry.js';\nexport { resolvePlaybook } from './playbook-resolver.js';\n// Preflight (#2590) — advisory prediction, never a grant.\nexport {\n createBrowserStepEvaluator,\n createServerStepEvaluator,\n PLAYBOOK_PREFLIGHT_UNAVAILABLE,\n preflightPlan,\n preflightPlaybook,\n worstVerdict,\n} from './preflight.js';\nexport {\n clearPlaybookPreflightCache,\n getPlaybookPreflightCacheTtlMs,\n} from './preflight-cache.js';\nexport type {\n BrowserPreflightLayerSource,\n BrowserPreflightOptions,\n PlaybookPreflightAvailableReport,\n PlaybookPreflightReport,\n PlaybookPreflightRequest,\n PlaybookPreflightUnavailableReport,\n PreflightLayer,\n PreflightLayerReport,\n PreflightReason,\n PreflightStepEvaluation,\n PreflightStepEvaluator,\n PreflightStepReport,\n PreflightSummary,\n PreflightVerdict,\n ServerPreflightOptions,\n} from './preflight-types.js';\nexport {\n type BrowserPreflightProviderOptions,\n createBrowserPlaybookPreflight,\n createRestPreflightLayerSource,\n} from './rest-preflight.js';\nexport type {\n PlaybookAcceptance,\n PlaybookCacheValue,\n PlaybookConfigOverrideInput,\n PlaybookDefinition,\n PlaybookDefinitionInput,\n PlaybookEditableConfig,\n PlaybookFailurePolicy,\n PlaybookIntentRecord,\n PlaybookIntentResolver,\n PlaybookIntentStep,\n PlaybookLayer,\n PlaybookMetadata,\n PlaybookOperationClassifier,\n PlaybookOperationStep,\n PlaybookPackageConfig,\n PlaybookPlan,\n PlaybookPlane,\n PlaybookPlanStep,\n PlaybookRejection,\n PlaybookRejectionReason,\n PlaybookResolution,\n PlaybookStep,\n ResolvePlaybookOptions,\n} from './types.js';\nexport { PLAYBOOK_PLANES } from './types.js';\nexport {\n FAIL_CLOSED_CLASSIFICATION,\n normalizeEditableConfig,\n} from './utils.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["layer"],"sources":["../src/__smrt-register__.ts","../src/cache.ts","../src/utils.ts","../src/playbook-registry.ts","../src/models/PlaybookOverride.ts","../src/collections/PlaybookOverrideCollection.ts","../src/playbook-resolver.ts","../src/preflight-cache.ts","../src/preflight.ts","../src/rest-preflight.ts","../src/index.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt()\n * decorator in the package fires. Fixes issue #1132: in consumer runtimes\n * (tsx, SvelteKit SSR, plain `vite dev`) the decorator's synchronous manifest\n * lookup previously missed because no step populated the global manifest cache\n * — classes got registered with zero fields and `save()` / `toJSON()`\n * silently dropped every declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates\n * manifests via a different path. Only needs to succeed in the published dist\n * output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { DatabaseInterface } from '@happyvertical/sql';\nimport type { PlaybookCacheValue } from './types.js';\n\nconst PLAYBOOK_CACHE_TTL_MS = 30_000;\n\ntype CacheEntry = {\n expiresAt: number;\n value: PlaybookCacheValue;\n};\n\nconst playbookCache = new Map<string, CacheEntry>();\n/** Monotonic per-`(db, key)` invalidation counter; see getPlaybookCacheGeneration. */\nconst cacheGenerations = new Map<string, number>();\nconst dbInstanceIds = new WeakMap<object, string>();\nlet nextDbId = 1;\n\nfunction getDbNamespace(db: unknown): string {\n if (!db) {\n return 'no-db';\n }\n\n if (typeof db === 'string') {\n return `db:${db}`;\n }\n\n if (typeof db === 'object') {\n const dbObject = db as Record<string, unknown>;\n if (typeof dbObject.query === 'function') {\n if (!dbInstanceIds.has(dbObject)) {\n dbInstanceIds.set(dbObject, `db-instance:${nextDbId++}`);\n }\n const namespace = dbInstanceIds.get(dbObject);\n if (namespace) {\n return namespace;\n }\n\n return 'db-instance:unknown';\n }\n\n try {\n return `db-config:${JSON.stringify(dbObject)}`;\n } catch {\n return 'db-config:opaque';\n }\n }\n\n return 'db:unknown';\n}\n\n/**\n * The cache namespace a database handle resolves to.\n *\n * Exported so the preflight cache (#2590) partitions by the same database\n * identity this cache does, rather than growing a second, divergent notion of\n * \"which database is this\".\n *\n * @internal\n */\nexport function playbookCacheNamespace(db: unknown): string {\n return getDbNamespace(db);\n}\n\nfunction buildCacheKey(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): string {\n return `${getDbNamespace(db)}::${key}::${tenantId ?? 'app'}`;\n}\n\nfunction buildGenerationKey(\n key: string,\n db: DatabaseInterface | unknown,\n): string {\n return `${getDbNamespace(db)}::${key}`;\n}\n\nfunction bumpGeneration(key: string, db: DatabaseInterface | unknown): void {\n const generationKey = buildGenerationKey(key, db);\n cacheGenerations.set(\n generationKey,\n (cacheGenerations.get(generationKey) ?? 0) + 1,\n );\n}\n\nexport function getPlaybookCacheTtlMs(): number {\n return PLAYBOOK_CACHE_TTL_MS;\n}\n\nexport function getCachedPlaybookBase(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): PlaybookCacheValue | null {\n const cacheKey = buildCacheKey(key, tenantId, db);\n const cached = playbookCache.get(cacheKey);\n\n if (!cached) {\n return null;\n }\n\n if (cached.expiresAt <= Date.now()) {\n playbookCache.delete(cacheKey);\n return null;\n }\n\n return cached.value;\n}\n\n/**\n * Reads the current invalidation generation for a key.\n *\n * A resolution captures this *before* its asynchronous layer loads and hands\n * it back to {@link setCachedPlaybookBase}. Any write that lands while those\n * loads are in flight bumps the generation, so the in-flight resolution — which\n * read the pre-write layers — is refused the cache write instead of\n * repopulating the key it just invalidated. Without this, the acceptance rule\n * \"a stale entry is never served after a write\" held only until a read raced a\n * write, and then failed for the full TTL.\n *\n * Tracked per `(db, key)` rather than per `(db, key, tenantId)`: an app-level\n * row is inherited by every tenant, so a write to any scope of a key must\n * invalidate every scope of it.\n */\nexport function getPlaybookCacheGeneration(\n key: string,\n db: DatabaseInterface | unknown,\n): number {\n return cacheGenerations.get(buildGenerationKey(key, db)) ?? 0;\n}\n\nexport function setCachedPlaybookBase(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n value: PlaybookCacheValue,\n loadedAtGeneration: number,\n): void {\n if (getPlaybookCacheGeneration(key, db) !== loadedAtGeneration) {\n // A write landed while this resolution was loading. Its value is already\n // stale, so drop it rather than poison the key for the whole TTL.\n return;\n }\n\n playbookCache.set(buildCacheKey(key, tenantId, db), {\n expiresAt: Date.now() + PLAYBOOK_CACHE_TTL_MS,\n value,\n });\n}\n\n/**\n * Invalidates a cached resolution. An app-level write (tenantId null) clears\n * every tenant's entry for that key, because each tenant inherits from it.\n */\nexport function invalidatePlaybookCache(\n key: string,\n tenantId: string | null | undefined,\n db: DatabaseInterface | unknown,\n): void {\n const dbNamespace = getDbNamespace(db);\n bumpGeneration(key, db);\n\n if (tenantId !== null && tenantId !== undefined) {\n playbookCache.delete(buildCacheKey(key, tenantId, db));\n return;\n }\n\n const keyPrefix = `${dbNamespace}::${key}::`;\n for (const cacheKey of playbookCache.keys()) {\n if (cacheKey.startsWith(keyPrefix)) {\n playbookCache.delete(cacheKey);\n }\n }\n}\n\nexport function clearPlaybookCache(): void {\n playbookCache.clear();\n // Generations deliberately survive: resetting them to zero would let a\n // resolution that started before the clear write its stale value back.\n for (const generationKey of cacheGenerations.keys()) {\n cacheGenerations.set(\n generationKey,\n (cacheGenerations.get(generationKey) ?? 0) + 1,\n );\n }\n}\n","import type {\n CapabilityClassification,\n CapabilityDeclaration,\n} from '@happyvertical/smrt-types';\nimport {\n PLAYBOOK_PLANES,\n type PlaybookConfigOverrideInput,\n type PlaybookDefinition,\n type PlaybookDefinitionInput,\n type PlaybookEditableConfig,\n type PlaybookFailurePolicy,\n type PlaybookLayer,\n type PlaybookMetadata,\n type PlaybookPlane,\n type PlaybookStep,\n} from './types.js';\n\n/**\n * Every field defaults to non-editable, matching `normalizeEditableConfig` in\n * `@happyvertical/smrt-prompts`. `steps` is not in this table at all — it is\n * structurally non-editable rather than defaulted false.\n */\nconst DEFAULT_EDITABLE: PlaybookEditableConfig = {\n title: false,\n description: false,\n planes: false,\n onStepFailure: false,\n enabled: false,\n metadata: false,\n};\n\n/**\n * Fail-closed capability classification, per epic #2585 invariant 3. Applied\n * whenever the host cannot tell us how a referenced operation is classified.\n */\nexport const FAIL_CLOSED_CLASSIFICATION: CapabilityClassification = {\n effect: 'destructive',\n idempotent: false,\n openWorld: true,\n};\n\nconst FAILURE_POLICIES = new Set<PlaybookFailurePolicy>(['abort', 'continue']);\n\n/**\n * `<package>:<Class>` — the same qualified form STI discriminators and\n * `@crossPackageRef` use. Both halves are required and neither may be padded.\n */\nconst QUALIFIED_MODEL_PATTERN = /^\\S+:\\S+$/;\n\n/** Frozen so a definition's default plane list can never be mutated in place. */\nconst BROWSER_ONLY_PLANES: readonly PlaybookPlane[] = Object.freeze([\n 'browser',\n]);\n\nexport function isPlainObject(\n value: unknown,\n): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function normalizeEditableConfig(\n editable?: Partial<PlaybookEditableConfig>,\n): PlaybookEditableConfig {\n const flag = (name: keyof PlaybookEditableConfig): boolean => {\n const supplied = editable?.[name];\n if (supplied === undefined) {\n return DEFAULT_EDITABLE[name];\n }\n\n // Only a real `true` unlocks a field. A truthy non-boolean such as\n // `'false'` would otherwise read as an explicit opt-in and open a stored\n // override the definition never meant to allow.\n if (typeof supplied !== 'boolean') {\n throw new Error(\n `Playbook editable.${name} must be a boolean, received ${typeof supplied} \"${String(supplied)}\"`,\n );\n }\n\n return supplied;\n };\n\n return {\n title: flag('title'),\n description: flag('description'),\n planes: flag('planes'),\n onStepFailure: flag('onStepFailure'),\n enabled: flag('enabled'),\n metadata: flag('metadata'),\n };\n}\n\n/**\n * Applies a partial capability declaration over the fail-closed default.\n *\n * A playbook step never classifies itself: this only fills the gaps left by a\n * declaration that the host inherited from the referenced operation.\n */\nexport function applyCapabilityDeclaration(\n declaration?: CapabilityDeclaration | null,\n): CapabilityClassification {\n if (!isPlainObject(declaration)) {\n return { ...FAIL_CLOSED_CLASSIFICATION };\n }\n\n return {\n effect: declaration.effect ?? FAIL_CLOSED_CLASSIFICATION.effect,\n idempotent: declaration.idempotent ?? FAIL_CLOSED_CLASSIFICATION.idempotent,\n openWorld: declaration.openWorld ?? FAIL_CLOSED_CLASSIFICATION.openWorld,\n };\n}\n\nexport function isCapabilityDeclarationComplete(\n declaration?: CapabilityDeclaration | null,\n): boolean {\n return (\n isPlainObject(declaration) &&\n declaration.effect !== undefined &&\n declaration.idempotent !== undefined &&\n declaration.openWorld !== undefined\n );\n}\n\nexport function normalizePlanes(\n planes: readonly PlaybookPlane[] | null | undefined,\n context: string,\n): readonly PlaybookPlane[] | undefined {\n if (planes === undefined) {\n return undefined;\n }\n\n if (planes === null) {\n return undefined;\n }\n\n if (!Array.isArray(planes)) {\n throw new Error(`${context} planes must be an array`);\n }\n\n const normalized: PlaybookPlane[] = [];\n for (const plane of planes) {\n if (!PLAYBOOK_PLANES.includes(plane)) {\n throw new Error(\n `${context} declares unknown plane \"${String(plane)}\"; expected one of ${PLAYBOOK_PLANES.join(', ')}`,\n );\n }\n if (!normalized.includes(plane)) {\n normalized.push(plane);\n }\n }\n\n if (normalized.length === 0) {\n throw new Error(`${context} must declare at least one plane`);\n }\n\n // Stable order keeps stored/serialized planes comparable.\n return PLAYBOOK_PLANES.filter((plane) => normalized.includes(plane));\n}\n\nfunction normalizeFailurePolicy(\n value: unknown,\n context: string,\n): PlaybookFailurePolicy {\n if (!FAILURE_POLICIES.has(value as PlaybookFailurePolicy)) {\n throw new Error(\n `${context} onStepFailure must be \"abort\" or \"continue\", received \"${String(value)}\"`,\n );\n }\n\n return value as PlaybookFailurePolicy;\n}\n\n/**\n * Enablement is a gate, so it is validated rather than coerced. Truthiness\n * would silently read a config or runtime `\"false\"` as enabled — the one\n * coercion failure here that fails open.\n */\nfunction normalizeEnabled(value: unknown, context: string): boolean {\n if (typeof value !== 'boolean') {\n throw new Error(\n `${context} enabled must be a boolean, received ${typeof value} \"${String(value)}\"`,\n );\n }\n\n return value;\n}\n\n/**\n * Validated, not coerced, for the same reason as {@link normalizeEnabled}: a\n * stringly-typed `\"false\"` would turn a required step into a skippable one.\n */\nfunction normalizeStepOptional(value: unknown, context: string): boolean {\n if (typeof value !== 'boolean') {\n throw new Error(\n `${context} optional must be a boolean, received ${typeof value} \"${String(value)}\"`,\n );\n }\n\n return value;\n}\n\nfunction normalizeOptionalText(\n value: unknown,\n context: string,\n field: string,\n): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value !== 'string') {\n throw new Error(`${context} ${field} must be a string`);\n }\n\n return value;\n}\n\n/**\n * Validates and freezes the declared step list.\n *\n * Rejects nested playbooks at definition time (epic #2585): a package playbook\n * referencing a tenant-overridden playbook is the description-behavior\n * mismatch one level removed.\n */\nexport function normalizeSteps(\n steps: readonly PlaybookStep[] | undefined,\n context: string,\n): readonly PlaybookStep[] {\n if (!Array.isArray(steps) || steps.length === 0) {\n throw new Error(`${context} requires at least one step`);\n }\n\n return Object.freeze(\n steps.map((rawStep, index) => {\n const stepContext = `${context} step ${index}`;\n\n if (!isPlainObject(rawStep)) {\n throw new Error(`${stepContext} must be an object`);\n }\n\n const kind = rawStep.kind;\n\n if (kind === 'playbook' || 'playbook' in rawStep) {\n throw new Error(\n `${stepContext} references another playbook; nested playbooks are not supported`,\n );\n }\n\n if (kind === 'operation') {\n const model = rawStep.model;\n const action = rawStep.action;\n\n if (typeof model !== 'string' || model.trim() === '') {\n throw new Error(`${stepContext} requires a qualified model name`);\n }\n\n // Both halves must be present and unpadded: ':', '@pkg:', and ':Order'\n // all contain a separator but identify no model, and would resolve\n // into a plan that no classifier or executor lookup can ever match.\n if (!QUALIFIED_MODEL_PATTERN.test(model)) {\n throw new Error(\n `${stepContext} model \"${model}\" must be a qualified pair such as \"@happyvertical/smrt-commerce:Order\"`,\n );\n }\n\n if (typeof action !== 'string' || action.trim() === '') {\n throw new Error(`${stepContext} requires an action name`);\n }\n\n return Object.freeze({\n kind: 'operation' as const,\n model,\n action,\n ...(normalizeOptionalText(rawStep.label, stepContext, 'label') !==\n undefined\n ? { label: rawStep.label as string }\n : {}),\n ...(normalizeOptionalText(\n rawStep.description,\n stepContext,\n 'description',\n ) !== undefined\n ? { description: rawStep.description as string }\n : {}),\n ...(rawStep.optional === undefined\n ? {}\n : {\n optional: normalizeStepOptional(rawStep.optional, stepContext),\n }),\n }) satisfies PlaybookStep;\n }\n\n if (kind === 'intent') {\n const id = rawStep.id;\n\n if (typeof id !== 'string' || id.trim() === '') {\n throw new Error(`${stepContext} requires an intent id`);\n }\n\n return Object.freeze({\n kind: 'intent' as const,\n id,\n ...(normalizeOptionalText(rawStep.label, stepContext, 'label') !==\n undefined\n ? { label: rawStep.label as string }\n : {}),\n ...(normalizeOptionalText(\n rawStep.description,\n stepContext,\n 'description',\n ) !== undefined\n ? { description: rawStep.description as string }\n : {}),\n ...(rawStep.optional === undefined\n ? {}\n : {\n optional: normalizeStepOptional(rawStep.optional, stepContext),\n }),\n }) satisfies PlaybookStep;\n }\n\n throw new Error(\n `${stepContext} has unknown kind \"${String(kind)}\"; expected \"operation\" or \"intent\"`,\n );\n }),\n );\n}\n\nexport function hasIntentStep(steps: readonly PlaybookStep[]): boolean {\n return steps.some((step) => step.kind === 'intent');\n}\n\n/**\n * Default plane validity. Operation-only playbooks are valid on both planes;\n * anything containing a view intent is browser-valid only until the author\n * explicitly declares server validity through the #2446 command/ack bridge.\n */\nexport function defaultPlanesForSteps(\n steps: readonly PlaybookStep[],\n): readonly PlaybookPlane[] {\n return hasIntentStep(steps) ? BROWSER_ONLY_PLANES : PLAYBOOK_PLANES;\n}\n\nexport function normalizePlaybookDefinitionInput(\n input: PlaybookDefinitionInput,\n): PlaybookDefinition {\n if (!input || typeof input.key !== 'string' || input.key.trim() === '') {\n throw new Error('Playbook definitions require a non-empty key');\n }\n\n const context = `Playbook \"${input.key}\"`;\n\n if (typeof input.title !== 'string' || input.title.trim() === '') {\n throw new Error(`${context} requires a title`);\n }\n\n if (\n typeof input.description !== 'string' ||\n input.description.trim() === ''\n ) {\n throw new Error(`${context} requires a description`);\n }\n\n if ('steps' in ((input.editable ?? {}) as Record<string, unknown>)) {\n throw new Error(\n `${context} cannot mark steps editable; playbook step lists are never editable`,\n );\n }\n\n const steps = normalizeSteps(input.steps, context);\n const declaredPlanes = normalizePlanes(input.planes, context);\n\n // Freeze the nested values too, not just the outer object: `planes` and\n // `editable` are the declared fail-closed policy, and a shallow freeze would\n // still let a caller push 'server' onto a browser-only definition or flip an\n // `editable` flag on the object the registry hands back.\n return Object.freeze({\n key: input.key,\n title: input.title,\n description: input.description,\n steps,\n planes: Object.freeze(declaredPlanes ?? defaultPlanesForSteps(steps)),\n onStepFailure:\n input.onStepFailure === undefined\n ? 'abort'\n : normalizeFailurePolicy(input.onStepFailure, context),\n enabled:\n input.enabled === undefined\n ? true\n : normalizeEnabled(input.enabled, context),\n metadata: Object.freeze(\n input.metadata ? sanitizeMetadata(input.metadata) : {},\n ),\n editable: Object.freeze(normalizeEditableConfig(input.editable)),\n });\n}\n\nexport function sanitizeMetadata(\n metadata: PlaybookMetadata | null | undefined,\n): PlaybookMetadata {\n if (!isPlainObject(metadata)) {\n return {};\n }\n\n // Copy and deep-freeze rather than aliasing the caller's object: metadata\n // ends up in the registered definition and the shared cache entry, and a\n // shallow freeze would leave nested values mutable through the caller's\n // original reference.\n return deepFreezeJsonObject(metadata);\n}\n\nfunction deepFreezeJsonValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return Object.freeze(value.map(deepFreezeJsonValue));\n }\n\n if (isPlainObject(value)) {\n return deepFreezeJsonObject(value);\n }\n\n return value;\n}\n\nfunction deepFreezeJsonObject(\n source: Record<string, unknown>,\n): PlaybookMetadata {\n const copied: PlaybookMetadata = {};\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) {\n continue;\n }\n copied[key] = deepFreezeJsonValue(value);\n }\n\n return Object.freeze(copied);\n}\n\nexport function parseMetadata(\n raw: string | null | undefined,\n): PlaybookMetadata {\n if (!raw) {\n return {};\n }\n\n try {\n const parsed = JSON.parse(raw);\n return isPlainObject(parsed) ? sanitizeMetadata(parsed) : {};\n } catch {\n return {};\n }\n}\n\nexport function serializeMetadata(\n metadata: PlaybookMetadata | null | undefined,\n): string | null {\n if (metadata === null || metadata === undefined) {\n return null;\n }\n\n return JSON.stringify(sanitizeMetadata(metadata));\n}\n\nexport function parsePlanes(\n raw: string | null | undefined,\n): readonly PlaybookPlane[] | null {\n if (!raw) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n return null;\n }\n return (\n normalizePlanes(parsed as PlaybookPlane[], 'Stored override') ?? null\n );\n } catch {\n return null;\n }\n}\n\nexport function serializePlanes(\n planes: readonly PlaybookPlane[] | null | undefined,\n): string | null {\n if (planes === null || planes === undefined) {\n return null;\n }\n\n return JSON.stringify(normalizePlanes(planes, 'Stored override') ?? []);\n}\n\n/**\n * Normalizes an override layer from config, storage, or a runtime call.\n *\n * A `steps` key is rejected rather than dropped: \"a tenant override cannot\n * alter steps through any path\" is enforced loudly at every entry point.\n */\nexport function normalizePlaybookLayer(\n input?: PlaybookConfigOverrideInput | null,\n context = 'Playbook override',\n): PlaybookLayer {\n if (!isPlainObject(input)) {\n return {};\n }\n\n if ('steps' in input) {\n throw new Error(\n `${context} cannot set steps; playbook step lists are never editable`,\n );\n }\n\n const layer: PlaybookLayer = {};\n\n if (input.title !== undefined) {\n layer.title =\n input.title === null\n ? null\n : (normalizeOptionalText(input.title, context, 'title') ?? null);\n }\n\n if (input.description !== undefined) {\n layer.description =\n input.description === null\n ? null\n : (normalizeOptionalText(input.description, context, 'description') ??\n null);\n }\n\n if (input.planes !== undefined) {\n layer.planes =\n input.planes === null\n ? null\n : (normalizePlanes(input.planes, context) ?? null);\n }\n\n if (input.onStepFailure !== undefined) {\n layer.onStepFailure =\n input.onStepFailure === null\n ? null\n : normalizeFailurePolicy(input.onStepFailure, context);\n }\n\n if (input.enabled !== undefined) {\n layer.enabled =\n input.enabled === null ? null : normalizeEnabled(input.enabled, context);\n }\n\n if (input.metadata !== undefined) {\n layer.metadata =\n input.metadata === null ? null : sanitizeMetadata(input.metadata);\n }\n\n return layer;\n}\n\nexport interface MergedPlaybookLayers {\n title: string;\n description: string;\n planes: readonly PlaybookPlane[];\n onStepFailure: PlaybookFailurePolicy;\n enabled: boolean;\n metadata: PlaybookMetadata;\n}\n\n/**\n * Merges layers low → high, field by field.\n *\n * Enablement is one-directional: once any layer disables a playbook, no\n * higher layer can enable it again. A tenant may narrow, never widen.\n */\nexport function mergePlaybookLayers(\n base: PlaybookDefinition,\n ...layers: Array<PlaybookLayer | null | undefined>\n): MergedPlaybookLayers {\n let title = base.title;\n let description = base.description;\n let planes = base.planes;\n let onStepFailure = base.onStepFailure;\n let enabled = base.enabled;\n let metadata: PlaybookMetadata = { ...base.metadata };\n\n for (const layer of layers) {\n if (!layer) {\n continue;\n }\n\n if (layer.title !== undefined && layer.title !== null) {\n title = layer.title;\n }\n\n if (layer.description !== undefined && layer.description !== null) {\n description = layer.description;\n }\n\n if (layer.planes !== undefined && layer.planes !== null) {\n // Plane validity only ever narrows: an override cannot claim a plane the\n // lower layers never declared.\n planes = layer.planes.filter((plane) => planes.includes(plane));\n }\n\n if (layer.onStepFailure !== undefined && layer.onStepFailure !== null) {\n onStepFailure = layer.onStepFailure;\n }\n\n if (layer.enabled !== undefined && layer.enabled !== null) {\n enabled = enabled && layer.enabled;\n }\n\n if (layer.metadata !== undefined && layer.metadata !== null) {\n metadata = { ...metadata, ...layer.metadata };\n }\n }\n\n // The merged result is both cached and handed to callers as part of the\n // plan, so the policy values must be immutable: a caller pushing onto a\n // narrowed `planes` array would otherwise corrupt the cached entry and widen\n // the declared-plane gate for every resolution until the TTL expires.\n return Object.freeze({\n title,\n description,\n planes: Object.freeze(planes),\n onStepFailure,\n enabled,\n metadata: Object.freeze(metadata),\n });\n}\n","import type { PlaybookDefinition, PlaybookDefinitionInput } from './types.js';\nimport { normalizePlaybookDefinitionInput } from './utils.js';\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtPlaybookRegistry: Map<string, PlaybookDefinition> | undefined;\n}\n\nfunction getRegistry(): Map<string, PlaybookDefinition> {\n if (!globalThis.__smrtPlaybookRegistry) {\n globalThis.__smrtPlaybookRegistry = new Map<string, PlaybookDefinition>();\n }\n\n return globalThis.__smrtPlaybookRegistry;\n}\n\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item)).join(',')}]`;\n }\n\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>).sort(\n ([left], [right]) => left.localeCompare(right),\n );\n return `{${entries\n .map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`)\n .join(',')}}`;\n }\n\n return JSON.stringify(value);\n}\n\n/**\n * Global process registry of code-default playbooks, keyed by namespaced key.\n *\n * Held on `globalThis` so it survives HMR, mirroring `PromptRegistry`.\n */\nexport const PlaybookRegistry = {\n register(input: PlaybookDefinitionInput): PlaybookDefinition {\n const definition = normalizePlaybookDefinitionInput(input);\n const registry = getRegistry();\n const existing = registry.get(definition.key);\n\n if (existing) {\n const existingSignature = stableStringify(existing);\n const incomingSignature = stableStringify(definition);\n\n if (existingSignature !== incomingSignature) {\n throw new Error(\n `Playbook \"${definition.key}\" is already registered with a different definition`,\n );\n }\n\n return existing;\n }\n\n registry.set(definition.key, definition);\n return definition;\n },\n\n get(key: string): PlaybookDefinition | undefined {\n return getRegistry().get(key);\n },\n\n has(key: string): boolean {\n return getRegistry().has(key);\n },\n\n getAll(): PlaybookDefinition[] {\n return Array.from(getRegistry().values());\n },\n\n clear(): void {\n getRegistry().clear();\n },\n};\n\n/**\n * Registers a code-default playbook. Packages call this at import time so a\n * bundled playbook resolves without any application registration.\n */\nexport function definePlaybook(\n input: PlaybookDefinitionInput,\n): PlaybookDefinition {\n return PlaybookRegistry.register(input);\n}\n","import { getPackageConfig } from '@happyvertical/smrt-config';\nimport {\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { invalidatePlaybookCache } from '../cache.js';\nimport { PlaybookRegistry } from '../playbook-registry.js';\nimport type {\n PlaybookConfigOverrideInput,\n PlaybookFailurePolicy,\n PlaybookLayer,\n PlaybookMetadata,\n PlaybookOverrideOptions as PlaybookOverrideFieldOptions,\n PlaybookPackageConfig,\n PlaybookPlane,\n} from '../types.js';\nimport {\n mergePlaybookLayers,\n normalizePlaybookLayer,\n parseMetadata,\n parsePlanes,\n serializeMetadata,\n serializePlanes,\n} from '../utils.js';\n\nexport interface PlaybookOverrideOptions\n extends SmrtObjectOptions,\n PlaybookOverrideFieldOptions {}\n\ntype PlaybookOverrideIdentity = {\n key: string;\n tenantId: string | null;\n};\n\ntype PlaybookTransactionHandle = DatabaseInterface & {\n commit: () => Promise<void>;\n rollback: () => Promise<void>;\n};\n\nfunction getPlaybookConfig(): PlaybookPackageConfig {\n return getPackageConfig<PlaybookPackageConfig>('playbooks', {\n playbooks: {},\n });\n}\n\n/**\n * Stored app-level (`tenantId = null`) and tenant-level playbook overrides.\n *\n * There is deliberately **no `steps` column**. `steps` is structurally\n * non-editable: an override layer has nowhere to put a step list, and\n * assigning one is rejected in `save()` rather than silently dropped. That is\n * the description-behavior guarantee from epic #2585 — an agent announcing\n * \"checking out your cart\" must not be following a rewritten script.\n */\n@smrt({\n tableName: '_smrt_playbook_overrides',\n conflictColumns: ['key', 'context'],\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n exclude: [\n 'getMetadata',\n 'setMetadata',\n 'getPlanes',\n 'setPlanes',\n 'toPlaybookLayer',\n ],\n },\n mcp: { include: [] },\n})\nexport class PlaybookOverride extends SmrtObject {\n @field({ required: true })\n key: string = '';\n\n // Not tenant-scoped (any scope is writable), but the value is a tenant id:\n // uuid on Postgres, so it compares with every other tenant_id column.\n @field({ sqlType: 'UUID', nullable: true })\n tenantId: string | null = null;\n\n @field({ type: 'text', nullable: true })\n title: string | null = null;\n\n @field({ type: 'text', nullable: true })\n description: string | null = null;\n\n /** JSON array of plane names; null inherits the lower layer. */\n @field({ type: 'text', nullable: true })\n planes: string | null = null;\n\n @field({ type: 'text', nullable: true })\n onStepFailure: string | null = null;\n\n /** Tri-state: null inherits, false disables. True can never widen. */\n @field({ type: 'boolean', nullable: true })\n enabled: boolean | null = null;\n\n /** JSON object; stored as a string with guarded get/set helpers. */\n @field({ type: 'text', nullable: true })\n metadata: string | null = null;\n\n constructor(options: PlaybookOverrideOptions = {}) {\n super(options);\n\n // `SmrtCollection.create()` spreads its caller's option bag straight into\n // the constructor, so this is the entry point a `steps` key actually\n // reaches. Reject it loudly instead of letting it be silently dropped:\n // step lists are never editable through any path.\n if ((options as Record<string, unknown>).steps !== undefined) {\n throw new Error(\n 'Playbook step lists are never editable; PlaybookOverride cannot carry steps',\n );\n }\n\n if (options.key !== undefined) this.key = options.key;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.onStepFailure !== undefined)\n this.onStepFailure = options.onStepFailure;\n if (options.enabled !== undefined) this.enabled = options.enabled;\n if (options.planes !== undefined) {\n this.planes =\n typeof options.planes === 'string' || options.planes === null\n ? options.planes\n : serializePlanes(options.planes);\n }\n if (options.metadata !== undefined) {\n this.metadata =\n typeof options.metadata === 'string' || options.metadata === null\n ? options.metadata\n : serializeMetadata(options.metadata);\n }\n }\n\n getMetadata(): PlaybookMetadata {\n return parseMetadata(this.metadata);\n }\n\n setMetadata(metadata: PlaybookMetadata | null): void {\n this.metadata = serializeMetadata(metadata);\n }\n\n getPlanes(): readonly PlaybookPlane[] | null {\n return parsePlanes(this.planes);\n }\n\n setPlanes(planes: readonly PlaybookPlane[] | null): void {\n this.planes = serializePlanes(planes);\n }\n\n toPlaybookLayer(): PlaybookLayer {\n return {\n title: this.title,\n description: this.description,\n planes: this.getPlanes(),\n onStepFailure: (this.onStepFailure ??\n null) as PlaybookFailurePolicy | null,\n // The database boundary is where coercion is legitimate: SQLite hydrates\n // a boolean column as 0/1. Everywhere above this, enablement is\n // validated rather than coerced.\n enabled:\n this.enabled === null || this.enabled === undefined\n ? null\n : Boolean(this.enabled),\n metadata: this.metadata === null ? null : this.getMetadata(),\n };\n }\n\n override async save(): Promise<this> {\n const previousIdentity = await this.getPersistedIdentity();\n this.normalizeForPersistence();\n await this.validatePlaybookOverride();\n // `context` is the conflictColumn-friendly scope: '__app__' for a nullable\n // tenant, tenantId otherwise. Mirrors PromptOverride / LanguageOverride so\n // app-level rows stay unique on PostgreSQL, SQLite, and DuckDB alike,\n // where multiple NULLs would otherwise all satisfy a unique index.\n this.context = this.tenantId ?? '__app__';\n\n const identityChanged =\n !!previousIdentity &&\n (previousIdentity.key !== this.key ||\n previousIdentity.tenantId !== this.tenantId);\n\n const result =\n identityChanged && previousIdentity\n ? await this.saveAfterIdentityChange()\n : await super.save();\n\n if (identityChanged && previousIdentity) {\n invalidatePlaybookCache(\n previousIdentity.key,\n previousIdentity.tenantId,\n this.db,\n );\n }\n invalidatePlaybookCache(this.key, this.tenantId, this.db);\n return result;\n }\n\n private async saveAfterIdentityChange(): Promise<this> {\n if (typeof this.db.beginTransaction === 'function') {\n return this.saveAfterIdentityChangeInTransaction();\n }\n\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n private async saveAfterIdentityChangeInTransaction(): Promise<this> {\n const originalDb = this._db;\n const originalOptionsDb = this.options.db;\n const tx = (await this.db.beginTransaction?.()) as\n | PlaybookTransactionHandle\n | undefined;\n\n if (!tx) {\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n try {\n this._db = tx;\n this.options.db = tx;\n await super.delete();\n const result = await super.save();\n await tx.commit();\n return result;\n } catch (error) {\n try {\n await tx.rollback();\n } catch {\n // Preserve the original save error; rollback failures are secondary.\n }\n throw error;\n } finally {\n this._db = originalDb;\n this.options.db = originalOptionsDb;\n }\n }\n\n private async saveAfterIdentityChangeWithDeferredDelete(): Promise<this> {\n const previousId = this.id;\n if (!previousId) {\n return super.save();\n }\n\n const replacementId = crypto.randomUUID();\n let replacementSaved = false;\n this.id = replacementId;\n\n try {\n const result = await super.save();\n replacementSaved = true;\n await this.db.delete(this.tableName, { id: previousId });\n return result;\n } catch (error) {\n if (replacementSaved) {\n try {\n await this.db.delete(this.tableName, { id: replacementId });\n } catch {\n // Best effort cleanup keeps the original row as the source of truth.\n }\n }\n\n this.id = previousId;\n throw error;\n }\n }\n\n override async delete(): Promise<void> {\n const key = this.key;\n const tenantId = this.tenantId;\n await super.delete();\n invalidatePlaybookCache(key, tenantId, this.db);\n }\n\n private async validatePlaybookOverride(): Promise<void> {\n if (!this.key || this.key.trim() === '') {\n throw new Error('PlaybookOverride.key is required');\n }\n\n // Structural, not merely defaulted-false: there is no `steps` column, and\n // a caller assigning one through the untyped option bag is rejected here\n // rather than having the value silently dropped by persistence.\n const assignedSteps = (this as unknown as Record<string, unknown>).steps;\n if (assignedSteps !== undefined) {\n throw new Error(\n `Playbook \"${this.key}\" step lists are never editable; PlaybookOverride cannot carry steps`,\n );\n }\n\n const definition = PlaybookRegistry.get(this.key);\n if (!definition) {\n throw new Error(`Unknown playbook key \"${this.key}\"`);\n }\n\n const editable = definition.editable;\n\n if (this.title !== null && !editable.title) {\n throw new Error(`Playbook \"${this.key}\" does not allow title overrides`);\n }\n\n if (this.description !== null && !editable.description) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow description overrides`,\n );\n }\n\n if (this.planes !== null && !editable.planes) {\n throw new Error(`Playbook \"${this.key}\" does not allow planes overrides`);\n }\n\n if (this.onStepFailure !== null && !editable.onStepFailure) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow onStepFailure overrides`,\n );\n }\n\n if (this.enabled !== null && !editable.enabled) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow enablement overrides`,\n );\n }\n\n if (this.metadata !== null && !editable.metadata) {\n throw new Error(\n `Playbook \"${this.key}\" does not allow metadata overrides`,\n );\n }\n\n if (this.metadata !== null) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(this.metadata);\n } catch (error) {\n throw new Error(\n `Playbook \"${this.key}\" has invalid metadata JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(\n `Playbook \"${this.key}\" metadata must be a JSON object`,\n );\n }\n }\n\n if (this.planes !== null) {\n const parsedPlanes = parsePlanes(this.planes);\n if (!parsedPlanes || parsedPlanes.length === 0) {\n throw new Error(\n `Playbook \"${this.key}\" planes must be a non-empty JSON array of plane names`,\n );\n }\n }\n\n if (\n this.onStepFailure !== null &&\n this.onStepFailure !== 'abort' &&\n this.onStepFailure !== 'continue'\n ) {\n throw new Error(\n `Playbook \"${this.key}\" onStepFailure must be \"abort\" or \"continue\"`,\n );\n }\n\n const lowerLayers = await this.getLowerPrecedenceLayers();\n const currentLayer = this.toPlaybookLayer();\n const lowerMerged = mergePlaybookLayers(definition, ...lowerLayers);\n\n // Enablement is one-directional: a layer may disable, never re-enable\n // something a lower layer disabled.\n if (this.enabled === true && !lowerMerged.enabled) {\n throw new Error(\n `Playbook \"${this.key}\" is disabled by a lower layer and cannot be re-enabled by an override`,\n );\n }\n\n // Plane validity narrows only; an override cannot claim a plane the lower\n // layers never declared.\n if (currentLayer.planes) {\n const widened = currentLayer.planes.filter(\n (plane) => !lowerMerged.planes.includes(plane),\n );\n if (widened.length > 0) {\n throw new Error(\n `Playbook \"${this.key}\" cannot add plane(s) ${widened.join(', ')} that no lower layer declares`,\n );\n }\n }\n }\n\n private async getLowerPrecedenceLayers(): Promise<PlaybookLayer[]> {\n const config = getPlaybookConfig();\n const layers: PlaybookLayer[] = [\n normalizePlaybookLayer(\n config.playbooks?.[this.key] as PlaybookConfigOverrideInput | undefined,\n `Playbook \"${this.key}\" config override`,\n ),\n ];\n\n const { PlaybookOverrideCollection } = await import(\n '../collections/PlaybookOverrideCollection.js'\n );\n const collection = await PlaybookOverrideCollection.create({\n db: this.options.db ?? this.options.persistence,\n });\n\n if (this.tenantId) {\n const appOverride = await collection.getAppOverride(this.key, {\n excludeId: this.id ?? undefined,\n });\n if (appOverride) {\n layers.push(appOverride.toPlaybookLayer());\n }\n }\n\n return layers;\n }\n\n private normalizeForPersistence(): void {\n const rawMetadata = this.metadata as unknown;\n if (rawMetadata === undefined) {\n this.metadata = null;\n } else if (\n rawMetadata !== null &&\n typeof rawMetadata === 'object' &&\n !Array.isArray(rawMetadata)\n ) {\n this.metadata = serializeMetadata(rawMetadata as PlaybookMetadata);\n } else if (rawMetadata !== null && typeof rawMetadata !== 'string') {\n throw new Error(\n `Playbook \"${this.key}\" metadata must be a JSON object or JSON string`,\n );\n }\n\n const rawPlanes = this.planes as unknown;\n if (rawPlanes === undefined) {\n this.planes = null;\n } else if (Array.isArray(rawPlanes)) {\n this.planes = serializePlanes(rawPlanes as PlaybookPlane[]);\n } else if (rawPlanes !== null && typeof rawPlanes !== 'string') {\n throw new Error(\n `Playbook \"${this.key}\" planes must be an array or JSON string`,\n );\n }\n\n // Enablement is a gate, so it is validated before it is ever persisted.\n // A stringly-typed `'false'` arriving through the untyped collection API\n // would otherwise hydrate as a truthy string and resolve as enabled.\n if (this.enabled === undefined) {\n this.enabled = null;\n } else if (this.enabled !== null && typeof this.enabled !== 'boolean') {\n throw new Error(\n `Playbook \"${this.key}\" enabled must be a boolean or null, received ${typeof this.enabled} \"${String(this.enabled)}\"`,\n );\n }\n }\n\n private async getPersistedIdentity(): Promise<PlaybookOverrideIdentity | null> {\n if (!this.id) {\n return null;\n }\n\n const existing = await this.db.get(this.tableName, { id: this.id });\n if (!existing) {\n return null;\n }\n\n const row = existing as Record<string, unknown>;\n return {\n key: String(row.key ?? this.key),\n tenantId:\n row.tenantId !== undefined\n ? (row.tenantId as string | null)\n : ((row.tenant_id as string | null | undefined) ?? null),\n };\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { PlaybookOverride } from '../models/PlaybookOverride.js';\n\nexport class PlaybookOverrideCollection extends SmrtCollection<PlaybookOverride> {\n static readonly _itemClass = PlaybookOverride;\n\n private excludeOverrideId(\n items: PlaybookOverride[],\n excludeId?: string,\n ): PlaybookOverride[] {\n return items.filter((item) => (excludeId ? item.id !== excludeId : true));\n }\n\n async getAppOverride(\n key: string,\n options: { excludeId?: string } = {},\n ): Promise<PlaybookOverride | null> {\n const items = await this.list({ where: { key, tenantId: null } });\n return this.excludeOverrideId(items, options.excludeId)[0] ?? null;\n }\n\n async getTenantOverride(\n key: string,\n tenantId: string,\n options: { excludeId?: string } = {},\n ): Promise<PlaybookOverride | null> {\n const items = await this.list({ where: { key, tenantId } });\n return this.excludeOverrideId(items, options.excludeId)[0] ?? null;\n }\n\n async getResolutionLayers(\n key: string,\n tenantId?: string | null,\n options: { excludeId?: string } = {},\n ): Promise<{\n app: PlaybookOverride | null;\n tenant: PlaybookOverride | null;\n }> {\n const [app, tenant] = await Promise.all([\n this.getAppOverride(key, options),\n tenantId != null\n ? this.getTenantOverride(key, tenantId, options)\n : Promise.resolve(null),\n ]);\n\n return { app, tenant };\n }\n}\n","import { getPackageConfig } from '@happyvertical/smrt-config';\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport {\n getCachedPlaybookBase,\n getPlaybookCacheGeneration,\n setCachedPlaybookBase,\n} from './cache.js';\nimport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nimport { PlaybookRegistry } from './playbook-registry.js';\nimport type {\n PlaybookCacheValue,\n PlaybookDefinition,\n PlaybookPackageConfig,\n PlaybookPlan,\n PlaybookPlane,\n PlaybookPlanStep,\n PlaybookRejection,\n PlaybookResolution,\n ResolvePlaybookOptions,\n} from './types.js';\nimport {\n applyCapabilityDeclaration,\n isCapabilityDeclarationComplete,\n mergePlaybookLayers,\n normalizePlaybookLayer,\n} from './utils.js';\n\n/** An intent record that declares no planes is browser-only (fail closed). */\nconst DEFAULT_INTENT_PLANES: readonly PlaybookPlane[] = Object.freeze([\n 'browser',\n]);\n\nfunction getPlaybookConfig(): PlaybookPackageConfig {\n return getPackageConfig<PlaybookPackageConfig>('playbooks', {\n playbooks: {},\n });\n}\n\nfunction reject(\n key: string,\n reason: PlaybookRejection['reason'],\n message: string,\n stepIndex?: number,\n): PlaybookRejection {\n return {\n ok: false,\n reason,\n message,\n key,\n ...(stepIndex === undefined ? {} : { stepIndex }),\n };\n}\n\nasync function loadPlaybookBase(\n definition: PlaybookDefinition,\n options: ResolvePlaybookOptions,\n): Promise<PlaybookCacheValue> {\n const key = definition.key;\n const tenantId =\n options.tenantId !== undefined ? options.tenantId : (getTenantId() ?? null);\n\n let collection: PlaybookOverrideCollection | null = null;\n let cacheDb: ResolvePlaybookOptions['db'] | undefined = options.db;\n if (options.db) {\n const initializedCollection = await PlaybookOverrideCollection.create({\n db: options.db,\n });\n collection = initializedCollection;\n cacheDb = initializedCollection.db as ResolvePlaybookOptions['db'];\n }\n\n const cached = getCachedPlaybookBase(key, tenantId, cacheDb);\n if (cached) {\n return cached;\n }\n\n // Capture the invalidation generation before the asynchronous layer loads. A\n // write that lands while they are in flight bumps it, and the cache write\n // below is then refused rather than repopulating the key the write just\n // invalidated with the pre-write value.\n const loadedAtGeneration = getPlaybookCacheGeneration(key, cacheDb);\n\n const config = getPlaybookConfig();\n const layers = [\n normalizePlaybookLayer(\n config.playbooks?.[key],\n `Playbook \"${key}\" config override`,\n ),\n ];\n\n if (collection) {\n const stored = await collection.getResolutionLayers(key, tenantId);\n\n if (stored.app) {\n layers.push(stored.app.toPlaybookLayer());\n }\n\n if (stored.tenant) {\n layers.push(stored.tenant.toPlaybookLayer());\n }\n }\n\n const merged = mergePlaybookLayers(definition, ...layers);\n // Frozen: this object is shared by every resolution that hits the cache.\n const value: PlaybookCacheValue = Object.freeze({\n key,\n title: merged.title,\n description: merged.description,\n planes: merged.planes,\n onStepFailure: merged.onStepFailure,\n enabled: merged.enabled,\n metadata: merged.metadata,\n });\n\n setCachedPlaybookBase(key, tenantId, cacheDb, value, loadedAtGeneration);\n return value;\n}\n\n/**\n * Resolves a playbook to a plan for a caller on a given plane.\n *\n * Returns a discriminated result rather than throwing: resolution fails closed\n * and every rejection names a specific reason. Nothing here executes a step —\n * the plan is followed step by step by the agent, and each step is authorized\n * independently at the REST boundary or by `PrincipalRun.assertToolAllowed()`.\n */\nexport async function resolvePlaybook(\n key: string,\n options: ResolvePlaybookOptions = {},\n): Promise<PlaybookResolution> {\n const definition = PlaybookRegistry.get(key);\n if (!definition) {\n return reject(key, 'unknown-playbook', `Unknown playbook key \"${key}\"`);\n }\n\n const base = await loadPlaybookBase(definition, options);\n const runtimeOverride = normalizePlaybookLayer(\n options.override,\n `Playbook \"${key}\" runtime override`,\n );\n const merged = mergePlaybookLayers(\n {\n ...definition,\n title: base.title,\n description: base.description,\n planes: base.planes,\n onStepFailure: base.onStepFailure,\n enabled: base.enabled,\n metadata: base.metadata,\n },\n runtimeOverride,\n );\n\n if (!merged.enabled) {\n return reject(\n key,\n 'disabled',\n `Playbook \"${key}\" is disabled for this scope`,\n );\n }\n\n const plane = options.plane ?? 'server';\n if (!merged.planes.includes(plane)) {\n return reject(\n key,\n 'plane-not-declared',\n `Playbook \"${key}\" is not valid on the \"${plane}\" plane; it declares ${merged.planes.join(', ')}`,\n );\n }\n\n const steps: PlaybookPlanStep[] = [];\n\n for (const [index, step] of definition.steps.entries()) {\n if (step.kind === 'operation') {\n // The step never classifies itself: the host supplies the classification\n // emitted for the referenced operation, and anything undeclared resolves\n // to the fail-closed default.\n const declaration = options.classifier?.(step) ?? null;\n steps.push({\n index,\n step,\n classification: applyCapabilityDeclaration(declaration),\n classificationDeclared: isCapabilityDeclarationComplete(declaration),\n });\n continue;\n }\n\n // Seam for #2588: without a declared-intent registry there is no identity\n // to resolve against, so an intent step fails closed rather than being\n // assumed valid.\n if (!options.intents) {\n return reject(\n key,\n 'intent-registry-unavailable',\n `Playbook \"${key}\" step ${index} references view intent \"${step.id}\", but no intent registry was supplied (see #2588)`,\n index,\n );\n }\n\n const record = options.intents(step.id);\n if (!record) {\n return reject(\n key,\n 'unknown-intent',\n `Playbook \"${key}\" step ${index} references unknown view intent \"${step.id}\"`,\n index,\n );\n }\n\n // Fail closed: an intent record that does not declare its planes is\n // browser-only, the same default an intent-bearing playbook gets. Server\n // validity rides the #2446 command/ack bridge and must be declared at both\n // the playbook and the intent, never assumed from silence.\n const intentPlanes = record.planes ?? DEFAULT_INTENT_PLANES;\n if (!intentPlanes.includes(plane)) {\n return reject(\n key,\n 'intent-plane-not-declared',\n `Playbook \"${key}\" step ${index} references view intent \"${step.id}\", which is not valid on the \"${plane}\" plane`,\n index,\n );\n }\n\n steps.push({\n index,\n step,\n classification: applyCapabilityDeclaration(record.classification),\n classificationDeclared: isCapabilityDeclarationComplete(\n record.classification,\n ),\n });\n }\n\n // A plan is a read-only description of what the agent is about to do; the\n // policy fields it carries are shared with the cache, so freeze it rather\n // than hand a caller a handle that can widen a gate after the fact.\n const plan: PlaybookPlan = Object.freeze({\n key,\n title: merged.title,\n description: merged.description,\n plane,\n planes: merged.planes,\n onStepFailure: merged.onStepFailure,\n metadata: merged.metadata,\n // Steps come from the code definition alone. No layer can supply them.\n steps: Object.freeze(steps),\n });\n\n return { ok: true, plan };\n}\n","/**\n * Preflight result cache (issue #2590).\n *\n * Preflight predicts; it never grants. Every step re-enforces at execution, and\n * that constraint is exactly what makes this cache free:\n *\n * - a stale `allow` costs a correctly-denied step at execution;\n * - a stale `deny` costs a briefly hidden capability that expires on the TTL.\n *\n * Neither is a security event, so results are cached per\n * `(principal, key, plane, tenant)` with a short TTL and **no invalidation\n * ceremony** beyond the playbook cache's own per-`(db, key)` generation counter:\n * a result captured under generation N is dropped once an override write has\n * bumped the counter past N. There is deliberately no per-principal invalidation\n * on a permission change — the TTL is the whole contract, because execution\n * re-enforces regardless.\n */\n\nimport { getPlaybookCacheGeneration, playbookCacheNamespace } from './cache.js';\nimport type { PlaybookPreflightReport } from './preflight-types.js';\nimport type { PlaybookPlane } from './types.js';\n\nconst PREFLIGHT_CACHE_TTL_MS = 15_000;\n\n/**\n * Hard ceiling on retained entries. The browser route is reachable without app\n * auth by design (it never invokes `authMiddleware`), so the cache must not be\n * a memory sink an anonymous caller can grow. Unknown keys are not cached at\n * all; this bounds everything else.\n */\nconst PREFLIGHT_CACHE_MAX_ENTRIES = 1_000;\n\n/** Identity a cached preflight result is stored under. */\nexport interface PreflightCacheScope {\n /** Opaque, caller-scoped principal partition. Never consulted for authority. */\n principal: string;\n key: string;\n plane: PlaybookPlane;\n /**\n * Tenant the result was resolved for. Part of the key because a tenant\n * override can disable a playbook or narrow its planes — the base playbook\n * cache scopes by tenant for the same reason.\n */\n tenantId: string | null;\n db: unknown;\n}\n\ninterface PreflightCacheEntry {\n expiresAt: number;\n generation: number;\n report: PlaybookPreflightReport;\n}\n\nconst preflightCache = new Map<string, PreflightCacheEntry>();\n\nfunction buildKey(scope: PreflightCacheScope): string {\n return `${playbookCacheNamespace(scope.db)}::${scope.principal}::${scope.key}::${scope.plane}::${scope.tenantId ?? 'app'}`;\n}\n\n/** Drops expired entries, then the oldest insertions if still over the cap. */\nfunction evict(): void {\n const now = Date.now();\n for (const [cacheKey, entry] of preflightCache) {\n if (entry.expiresAt <= now) {\n preflightCache.delete(cacheKey);\n }\n }\n\n // Map iteration is insertion-ordered, so this drops the oldest first.\n for (const cacheKey of preflightCache.keys()) {\n if (preflightCache.size <= PREFLIGHT_CACHE_MAX_ENTRIES) {\n break;\n }\n preflightCache.delete(cacheKey);\n }\n}\n\n/** TTL applied to every cached preflight result. */\nexport function getPlaybookPreflightCacheTtlMs(): number {\n return PREFLIGHT_CACHE_TTL_MS;\n}\n\n/**\n * Reads a cached preflight result.\n *\n * A result captured before an override write (a lower generation) is dropped\n * rather than served, so an override change is reflected without waiting out\n * the TTL.\n */\nexport function getCachedPlaybookPreflight(\n scope: PreflightCacheScope,\n): PlaybookPreflightReport | null {\n const cacheKey = buildKey(scope);\n const entry = preflightCache.get(cacheKey);\n if (!entry) {\n return null;\n }\n\n if (\n entry.expiresAt <= Date.now() ||\n entry.generation !== getPlaybookCacheGeneration(scope.key, scope.db)\n ) {\n preflightCache.delete(cacheKey);\n return null;\n }\n\n return entry.report;\n}\n\n/**\n * Stores a preflight result. `loadedAtGeneration` is the generation captured\n * *before* resolution started: a write that landed while it was in flight makes\n * the result stale on arrival, and it is dropped rather than cached.\n */\nexport function setCachedPlaybookPreflight(\n scope: PreflightCacheScope,\n report: PlaybookPreflightReport,\n loadedAtGeneration: number,\n): void {\n if (getPlaybookCacheGeneration(scope.key, scope.db) !== loadedAtGeneration) {\n return;\n }\n\n preflightCache.set(buildKey(scope), {\n expiresAt: Date.now() + PREFLIGHT_CACHE_TTL_MS,\n generation: loadedAtGeneration,\n report,\n });\n\n if (preflightCache.size > PREFLIGHT_CACHE_MAX_ENTRIES) {\n evict();\n }\n}\n\n/** Drops every cached preflight result. Use in tests. */\nexport function clearPlaybookPreflightCache(): void {\n preflightCache.clear();\n}\n","/**\n * Playbook preflight (issue #2590).\n *\n * Playbooks are not atomic and have no compensation, so the real failure mode\n * is half-execution — five steps started, dead at four. Preflight is what\n * prevents that, and secondarily lets a tool listing hide playbooks the caller\n * cannot run.\n *\n * **Preflight predicts; it never grants.** Every step re-enforces at execution,\n * unconditionally — at the REST boundary in the browser, and by\n * `PrincipalRun.assertToolAllowed()` / `assertOperation()` server-side. Without\n * that, a permission revoked mid-playbook would leave a cached \"allowed\"\n * standing, which is a time-of-check/time-of-use bypass. Nothing in this module\n * executes, authorizes, or caches an authorization.\n *\n * The two planes are deliberately asymmetric:\n *\n * - **Server** is a predicate that already exists. `PrincipalRun` exposes\n * `isToolAllowed(tool): boolean` alongside the throwing `assertToolAllowed()`,\n * and `assertOperation` wraps `assertOperationPermission`. Preflight is the\n * intersection of each step against those two, evaluated and never executed.\n * - **Browser** cannot reuse the generated REST auth contract at all.\n * `authMiddleware` is `(objectName, action) => (req) => Promise<Request |\n * Response>`: request-bound, `Response`-returning rather than boolean, and\n * free to consult session stores, rate-limit, or audit. It is not a dry-run\n * predicate, so preflight covers the **static layers only** — action exposure,\n * `public` access, and field-level permission slugs — and reports the app-auth\n * layer as `unknown`. It never invokes `authMiddleware`, synthetically or\n * otherwise.\n */\n\nimport { getTenantId } from '@happyvertical/smrt-tenancy';\nimport { getPlaybookCacheGeneration } from './cache.js';\nimport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nimport { resolvePlaybook } from './playbook-resolver.js';\nimport {\n getCachedPlaybookPreflight,\n setCachedPlaybookPreflight,\n} from './preflight-cache.js';\nimport type {\n BrowserPreflightOptions,\n PlaybookPreflightAvailableReport,\n PlaybookPreflightReport,\n PlaybookPreflightRequest,\n PlaybookPreflightUnavailableReport,\n PreflightLayerReport,\n PreflightStepEvaluator,\n PreflightStepReport,\n PreflightSummary,\n PreflightVerdict,\n ServerPreflightOptions,\n} from './preflight-types.js';\nimport type {\n PlaybookOperationStep,\n PlaybookPlan,\n PlaybookPlanStep,\n ResolvePlaybookOptions,\n} from './types.js';\n\n/**\n * The one response every unresolvable playbook produces.\n *\n * Frozen and shared so an unknown key and an unauthorized key are byte-for-byte\n * identical: same shape, same values, no key echo, no reason code, no message.\n * Preflight answers only for playbooks resolvable through the caller's own\n * layer chain, and says nothing about anything else — including whether it\n * exists.\n */\nexport const PLAYBOOK_PREFLIGHT_UNAVAILABLE: PlaybookPreflightUnavailableReport =\n Object.freeze({\n available: false,\n advisory: true,\n verdict: 'deny',\n steps: Object.freeze([]),\n summary: Object.freeze({ allow: 0, deny: 0, unknown: 0 }),\n }) as PlaybookPreflightUnavailableReport;\n\nconst VERDICT_RANK: Record<PreflightVerdict, number> = {\n allow: 0,\n unknown: 1,\n deny: 2,\n};\n\n/** Fail-closed combination: `deny` beats `unknown` beats `allow`. */\nexport function worstVerdict(\n left: PreflightVerdict,\n right: PreflightVerdict,\n): PreflightVerdict {\n return VERDICT_RANK[right] > VERDICT_RANK[left] ? right : left;\n}\n\nfunction combineLayers(\n layers: readonly PreflightLayerReport[],\n): Pick<PreflightStepReport, 'verdict' | 'reason'> {\n let verdict: PreflightVerdict = 'allow';\n for (const layer of layers) {\n verdict = worstVerdict(verdict, layer.verdict);\n }\n if (!layers.length) {\n // An evaluator that reports nothing has told us nothing. This is the one\n // function whose whole job is fail-closed combination, so it must not\n // answer `allow` by default for a custom evaluator that returns no layers.\n return { verdict: 'unknown', reason: 'not-evaluated' };\n }\n // The step's reason is the first layer that produced the aggregate verdict,\n // so the reason and the verdict can never describe different layers.\n const source = layers.find((layer) => layer.verdict === verdict);\n return { verdict, reason: source?.reason ?? 'ok' };\n}\n\nfunction summarize(steps: readonly PreflightStepReport[]): PreflightSummary {\n const summary: PreflightSummary = { allow: 0, deny: 0, unknown: 0 };\n for (const step of steps) {\n summary[step.verdict] += 1;\n }\n return summary;\n}\n\n/**\n * Decomposes a resolved plan into per-step verdicts using `evaluate`.\n *\n * Every step is evaluated — evaluation does not short-circuit on the first\n * denial, because the point of preflight is to show the caller *which* step of\n * five would die, not merely that one would.\n */\nexport async function preflightPlan(\n plan: PlaybookPlan,\n evaluate: PreflightStepEvaluator,\n): Promise<PlaybookPreflightAvailableReport> {\n const steps: PreflightStepReport[] = [];\n\n for (const planStep of plan.steps) {\n const { layers } = await evaluate(planStep);\n steps.push(\n Object.freeze({\n index: planStep.index,\n kind: planStep.step.kind,\n ...combineLayers(layers),\n layers: Object.freeze([...layers]),\n }),\n );\n }\n\n let verdict: PreflightVerdict = 'allow';\n for (const step of steps) {\n verdict = worstVerdict(verdict, step.verdict);\n }\n\n return Object.freeze({\n available: true,\n advisory: true,\n key: plan.key,\n plane: plan.plane,\n title: plan.title,\n description: plan.description,\n verdict,\n steps: Object.freeze(steps),\n summary: Object.freeze(summarize(steps)),\n }) as PlaybookPreflightAvailableReport;\n}\n\n/**\n * Pays the override-layer read cost a resolvable playbook would have paid.\n *\n * `resolvePlaybook()` short-circuits an unknown key before it touches the\n * override table, while a key that exists but is disabled, wrong-plane, or\n * otherwise unresolvable reads the layers first. Left alone, that difference is\n * a timing oracle over exactly the question the uniform response exists to\n * refuse. Running the same read on the unknown path puts both in the same\n * timing class (one override round-trip, then the shared frozen response).\n *\n * Best-effort by construction: preflight is advisory, so a failure here changes\n * nothing about the answer.\n */\nasync function equalizeUnknownKeyCost(\n key: string,\n options: ResolvePlaybookOptions | undefined,\n): Promise<void> {\n if (!options?.db) {\n return;\n }\n\n try {\n const collection = await PlaybookOverrideCollection.create({\n db: options.db,\n });\n // Same tenant resolution the real path uses, since matching its cost is the\n // entire purpose of this call.\n const tenantId =\n options.tenantId !== undefined\n ? options.tenantId\n : (getTenantId() ?? null);\n await collection.getResolutionLayers(key, tenantId);\n } catch {\n // Advisory only — never let the equalizer change the answer or throw.\n }\n}\n\n/**\n * Resolves the database identity the playbook cache partitions on.\n *\n * `loadPlaybookBase()` normalizes a db *config object* into the initialized\n * collection's live handle before it touches the cache, and `invalidatePlaybookCache`\n * is likewise called with the live handle. Capturing the generation from the raw\n * option would put reads and invalidations in two different buckets, and the\n * generation guard would silently never fire — degrading \"an override write is\n * reflected without waiting out the TTL\" back to plain TTL.\n */\nasync function normalizeCacheDb(\n db: ResolvePlaybookOptions['db'],\n): Promise<unknown> {\n if (!db) {\n return db;\n }\n\n try {\n const collection = await PlaybookOverrideCollection.create({ db });\n return collection.db;\n } catch {\n return db;\n }\n}\n\n/**\n * Preflights one playbook for one caller on one plane.\n *\n * Resolves through the **caller's own layer chain** (their tenant, their\n * overrides, their plane), decomposes the plan, and evaluates each step with the\n * plane's evaluator. Any playbook the chain cannot resolve — unknown, disabled,\n * wrong plane, unresolvable intent — returns the single\n * {@link PLAYBOOK_PREFLIGHT_UNAVAILABLE} value.\n *\n * Results are cached per `(principal, key, plane)` under the playbook cache's\n * generation counter; see `preflight-cache.ts` for why that is safe.\n */\nexport async function preflightPlaybook(\n request: PlaybookPreflightRequest,\n): Promise<PlaybookPreflightReport> {\n const { key, plane, principal, resolve, evaluate } = request;\n // The SAME fallback `loadPlaybookBase()` uses. `undefined` means \"take the\n // ambient tenant\", and hardcoding `null` here would let two ambient\n // `withTenant()` callers with the same principal share one cache entry\n // despite resolving different tenant overrides — a cross-tenant read of a\n // report that carries the resolved title and description.\n const tenantId =\n resolve?.tenantId !== undefined\n ? resolve.tenantId\n : (getTenantId() ?? null);\n const db = await normalizeCacheDb(resolve?.db);\n const scope = { principal, key, plane, tenantId, db };\n\n const cached = getCachedPlaybookPreflight(scope);\n if (cached) {\n return cached;\n }\n\n const loadedAtGeneration = getPlaybookCacheGeneration(key, db);\n\n try {\n const resolution = await resolvePlaybook(key, { ...resolve, plane });\n\n if (!resolution.ok) {\n if (resolution.reason === 'unknown-playbook') {\n await equalizeUnknownKeyCost(key, resolve);\n }\n // Unknown and registered-but-unavailable keys are cached alike. Caching\n // only one of them would put a repeat probe of the other in a different\n // timing class — the equalizer closes the first-request gap, and this\n // closes the every-subsequent-request gap. Unbounded growth from probing\n // random keys is handled where it belongs: the cache is capped with\n // expiry-first eviction.\n setCachedPlaybookPreflight(\n scope,\n PLAYBOOK_PREFLIGHT_UNAVAILABLE,\n loadedAtGeneration,\n );\n return PLAYBOOK_PREFLIGHT_UNAVAILABLE;\n }\n\n const report = await preflightPlan(resolution.plan, evaluate);\n setCachedPlaybookPreflight(scope, report, loadedAtGeneration);\n return report;\n } catch {\n // A failure anywhere in resolution or evaluation — a dropped connection, a\n // missing override table — must not become an existence oracle: an unknown\n // key answers from memory and could never fail here, so a thrown error\n // would mark every key that *does* exist. Answer uniformly, and do NOT\n // cache it: a transient fault must not pin a deny for the whole TTL.\n return PLAYBOOK_PREFLIGHT_UNAVAILABLE;\n }\n}\n\nfunction layer(\n layerName: PreflightLayerReport['layer'],\n verdict: PreflightVerdict,\n reason: PreflightLayerReport['reason'],\n missingPermissions?: readonly string[],\n): PreflightLayerReport {\n return Object.freeze({\n layer: layerName,\n verdict,\n reason,\n ...(missingPermissions?.length\n ? { missingPermissions: Object.freeze([...missingPermissions]) }\n : {}),\n });\n}\n\n/**\n * Server-plane evaluator: the intersection of the persona tool allow-list and\n * the operation-permission predicate, per step, with nothing executed.\n *\n * An intent step is reported against its **declared** plane validity: an intent\n * that does not declare `server` is denied with reason `plane`, and one that\n * does is `unknown`, because server validity rides the #2446 command/ack bridge\n * whose acknowledgement is not statically knowable. This repeats a check\n * resolution already made, deliberately — the same defense-in-depth shape as\n * `assertToolAllowed()` gating both the tool offer and its execution.\n */\nexport function createServerStepEvaluator(\n options: ServerPreflightOptions,\n): PreflightStepEvaluator {\n return async (planStep: PlaybookPlanStep) => {\n const step = planStep.step;\n\n if (step.kind === 'intent') {\n const planes = options.intentPlanes?.(step.id) ?? null;\n const serverValid = planes?.includes('server') ?? false;\n return {\n layers: [\n serverValid\n ? layer('plane', 'unknown', 'intent-bridge-not-evaluated')\n : layer('plane', 'deny', 'plane'),\n ],\n };\n }\n\n const operation: PlaybookOperationStep = step;\n const toolLayer = options.isToolAllowed(operation)\n ? layer('tool-allowlist', 'allow', 'ok')\n : layer('tool-allowlist', 'deny', 'tool-not-allowed');\n\n const permissionVerdict = await options.checkOperationPermission(operation);\n const permissionLayer =\n permissionVerdict === 'allow'\n ? layer('operation-permission', 'allow', 'ok')\n : permissionVerdict === 'deny'\n ? layer('operation-permission', 'deny', 'permission-denied')\n : layer('operation-permission', 'unknown', 'permission-unknown');\n\n return { layers: [toolLayer, permissionLayer] };\n };\n}\n\n/**\n * Browser-plane evaluator: the static layers only.\n *\n * - `action-exposure` — `isApiActionEnabled`. A disabled action is a hard,\n * statically knowable deny (the route answers 405).\n * - `public-access` — `isRoutePublic`. A non-public route with **no** app auth\n * middleware wired is a hard deny (the generator's fail-closed 401); with one\n * wired it is `unknown`, because whether that middleware passes is exactly\n * what preflight refuses to guess.\n * - `field-permissions` — the model's declared field read-permission slugs\n * against the caller's published set. Missing slugs redact fields rather than\n * fail the step, so the verdict stays `allow` and carries\n * `reason: 'fields-redacted'` plus the missing slugs; an unpublished\n * permission set reports `unknown`.\n * - `app-auth` — `unknown` whenever a middleware is wired. This is the layer an\n * `authPredicate` seam would later fill in.\n *\n * An intent step never crosses the REST boundary at all, so it is reported\n * `unknown` against `intent-mount`: whether the surface is mounted and what its\n * staged-control review does are runtime facts of the page, not of the build.\n */\nexport function createBrowserStepEvaluator(\n options: BrowserPreflightOptions,\n): PreflightStepEvaluator {\n const held = options.permissions ? new Set(options.permissions) : null;\n\n return (planStep: PlaybookPlanStep) => {\n const step = planStep.step;\n\n if (step.kind === 'intent') {\n return {\n layers: [layer('intent-mount', 'unknown', 'intent-not-mounted')],\n };\n }\n\n const { model, action } = step;\n const layers: PreflightLayerReport[] = [];\n\n const exposed = options.layers.isActionExposed(model, action);\n layers.push(\n exposed\n ? layer('action-exposure', 'allow', 'ok')\n : layer('action-exposure', 'deny', 'action-not-exposed'),\n );\n\n const isPublic = options.layers.isRoutePublic(model, action);\n const appAuthConfigured = options.layers.appAuthConfigured;\n layers.push(\n isPublic\n ? layer('public-access', 'allow', 'ok')\n : appAuthConfigured\n ? layer('public-access', 'unknown', 'auth-required')\n : layer('public-access', 'deny', 'not-public'),\n );\n\n const required = options.layers.requiredFieldPermissions(model, action);\n if (!required.length) {\n layers.push(layer('field-permissions', 'allow', 'ok'));\n } else if (!held) {\n layers.push(\n layer('field-permissions', 'unknown', 'field-permissions-unknown'),\n );\n } else {\n const missing = required.filter((slug) => !held.has(slug));\n layers.push(\n missing.length\n ? // Missing a field read permission redacts the field; it does not\n // fail the step. Reporting `deny` here would predict a failure that\n // will not happen.\n layer('field-permissions', 'allow', 'fields-redacted', missing)\n : layer('field-permissions', 'allow', 'ok'),\n );\n }\n\n layers.push(\n appAuthConfigured\n ? layer('app-auth', 'unknown', 'app-auth-not-evaluated')\n : layer('app-auth', 'allow', 'app-auth-not-configured'),\n );\n\n return { layers };\n };\n}\n","/**\n * Browser-plane preflight wiring for the generated REST surface (issue #2590).\n *\n * Binds the plane-agnostic evaluator in `preflight.ts` to the static layers\n * `@happyvertical/smrt-core` exposes, and produces the provider the generated\n * `_preflight` route takes as a seam. Core owns the route and the layer facts;\n * this package owns resolution and the verdict vocabulary, so the dependency\n * stays one-way (playbooks → core).\n *\n * Nothing here can reach `authMiddleware`: the provider is handed a boolean\n * `appAuthConfigured` and no auth handle at all.\n */\n\nimport {\n isApiActionEnabledForObject,\n isRestActionRoutable,\n isRestRoutePublic,\n resolveRegisteredObjectName,\n restFieldReadPermissions,\n restMethodForApiAction,\n} from '@happyvertical/smrt-core';\nimport { createBrowserStepEvaluator, preflightPlaybook } from './preflight.js';\nimport type {\n BrowserPreflightLayerSource,\n PlaybookPreflightReport,\n} from './preflight-types.js';\nimport type { ResolvePlaybookOptions } from './types.js';\n\n/**\n * A layer source reading the generated REST surface's own static rules.\n *\n * A model this build does not register resolves to no object name, and every\n * layer then fails closed: an unregistered model has no route, so a step naming\n * one can only die.\n */\nexport function createRestPreflightLayerSource(options: {\n appAuthConfigured: boolean;\n}): BrowserPreflightLayerSource {\n return {\n appAuthConfigured: options.appAuthConfigured,\n isActionExposed(model, action) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return false;\n // Two questions, both required: does a route for this action exist at\n // all, and does the decorator expose it? `include`/`exclude` alone would\n // report a typo'd or removed custom action as exposed, and the runtime\n // dispatcher — which iterates only declared `api.routes` — could never\n // run it.\n return (\n isRestActionRoutable(objectName, action) &&\n isApiActionEnabledForObject(objectName, action)\n );\n },\n isRoutePublic(model, action) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return false;\n // The object name is threaded through so a custom action declaring its\n // own HTTP verb is predicted against that verb, not a guessed POST.\n return isRestRoutePublic(\n objectName,\n restMethodForApiAction(action, objectName),\n );\n },\n requiredFieldPermissions(model) {\n const objectName = resolveRegisteredObjectName(model);\n if (!objectName) return [];\n return restFieldReadPermissions(objectName);\n },\n };\n}\n\nexport interface BrowserPreflightProviderOptions {\n /**\n * Resolution options shared by every request — classifier, intent registry,\n * and any host-level runtime override. The per-request database handle and\n * tenant come from the route.\n */\n resolve?: Omit<ResolvePlaybookOptions, 'plane' | 'db'>;\n /**\n * Derives the cache-partitioning principal identity for a request. Opaque and\n * never echoed; defaults to the caller's permission slugs, which is the only\n * caller-distinguishing input the static layers actually read.\n */\n principal?: (request: {\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n }) => string;\n}\n\nfunction defaultPrincipal(request: {\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n}): string {\n // Every input the browser evaluation actually reads has to be in here, or the\n // process-global cache can serve one context's report to another:\n //\n // - an ABSENT permission set is not an empty one. Absent makes the field\n // layer `unknown`; an explicitly published empty set is known, and answers\n // `allow` with redactions. Both would render as a bare `perm:` without the\n // sentinel.\n // - `appAuthConfigured` decides whether `public-access` and `app-auth` report\n // `deny`/`allow` or `unknown`. Two generators in one process — one with an\n // auth middleware, one without — otherwise share entries.\n const permissions = request.permissions\n ? `perm:${[...request.permissions].sort().join(',')}`\n : 'perm:unpublished';\n return `${permissions}|auth:${request.appAuthConfigured ? 'wired' : 'none'}`;\n}\n\n/**\n * Builds the provider wired into `APIConfig.playbookPreflight`.\n *\n * ```typescript\n * const api = new APIGenerator(manifest, {\n * playbookPreflight: createBrowserPlaybookPreflight(),\n * });\n * ```\n *\n * Every unresolvable key — unknown, disabled, browser-invalid, unresolvable\n * intent — comes back as the single uniform unavailable report, so the endpoint\n * is not an enumeration oracle.\n */\nexport function createBrowserPlaybookPreflight(\n options: BrowserPreflightProviderOptions = {},\n): (request: {\n key: string;\n plane: 'browser';\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n db?: unknown;\n}) => Promise<PlaybookPreflightReport> {\n return async (request) => {\n const layers = createRestPreflightLayerSource({\n appAuthConfigured: request.appAuthConfigured,\n });\n return preflightPlaybook({\n key: request.key,\n plane: 'browser',\n principal: (options.principal ?? defaultPrincipal)(request),\n resolve: {\n ...options.resolve,\n ...(request.db === undefined\n ? {}\n : { db: request.db as ResolvePlaybookOptions['db'] }),\n },\n evaluate: createBrowserStepEvaluator({\n layers,\n permissions: request.permissions ?? null,\n }),\n });\n };\n}\n","/**\n * @happyvertical/smrt-playbooks\n *\n * Code-first playbooks — named, described, layered step sequences an agent\n * follows — with config, app-level, tenant-level, and runtime overrides.\n *\n * A playbook resolves to a plan the agent executes step by step. Nothing in\n * this package executes a step, so a playbook is never an authority boundary:\n * every step is authorized independently at the REST boundary or, server-side,\n * by `PrincipalRun.assertToolAllowed()`.\n *\n * @packageDocumentation\n */\n\n// Self-register this package's manifest before any @smrt() decorator fires\n// downstream. Must come first so the side effect runs ahead of the class\n// module loads below. See __smrt-register__.ts for issue #1132 context.\nimport './__smrt-register__.js';\n\nexport { clearPlaybookCache, getPlaybookCacheTtlMs } from './cache.js';\nexport { PlaybookOverrideCollection } from './collections/PlaybookOverrideCollection.js';\nexport {\n PlaybookOverride,\n type PlaybookOverrideOptions,\n} from './models/PlaybookOverride.js';\nexport { definePlaybook, PlaybookRegistry } from './playbook-registry.js';\nexport { resolvePlaybook } from './playbook-resolver.js';\n// Preflight (#2590) — advisory prediction, never a grant.\nexport {\n createBrowserStepEvaluator,\n createServerStepEvaluator,\n PLAYBOOK_PREFLIGHT_UNAVAILABLE,\n preflightPlan,\n preflightPlaybook,\n worstVerdict,\n} from './preflight.js';\nexport {\n clearPlaybookPreflightCache,\n getPlaybookPreflightCacheTtlMs,\n} from './preflight-cache.js';\nexport type {\n BrowserPreflightLayerSource,\n BrowserPreflightOptions,\n PlaybookPreflightAvailableReport,\n PlaybookPreflightReport,\n PlaybookPreflightRequest,\n PlaybookPreflightUnavailableReport,\n PreflightLayer,\n PreflightLayerReport,\n PreflightReason,\n PreflightStepEvaluation,\n PreflightStepEvaluator,\n PreflightStepReport,\n PreflightSummary,\n PreflightVerdict,\n ServerPreflightOptions,\n} from './preflight-types.js';\nexport {\n type BrowserPreflightProviderOptions,\n createBrowserPlaybookPreflight,\n createRestPreflightLayerSource,\n} from './rest-preflight.js';\nexport type {\n PlaybookAcceptance,\n PlaybookCacheValue,\n PlaybookConfigOverrideInput,\n PlaybookDefinition,\n PlaybookDefinitionInput,\n PlaybookEditableConfig,\n PlaybookFailurePolicy,\n PlaybookIntentRecord,\n PlaybookIntentResolver,\n PlaybookIntentStep,\n PlaybookLayer,\n PlaybookMetadata,\n PlaybookOperationClassifier,\n PlaybookOperationStep,\n PlaybookPackageConfig,\n PlaybookPlan,\n PlaybookPlane,\n PlaybookPlanStep,\n PlaybookRejection,\n PlaybookRejectionReason,\n PlaybookResolution,\n PlaybookStep,\n ResolvePlaybookOptions,\n} from './types.js';\nexport { PLAYBOOK_PLANES } from './types.js';\nexport {\n FAIL_CLOSED_CLASSIFICATION,\n normalizeEditableConfig,\n} from './utils.js';\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":"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"}
|
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-playbooks",
|
|
5
|
-
"packageVersion": "0.
|
|
5
|
+
"packageVersion": "0.52.0",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-playbooks:PlaybookOverrideCollection": {
|
|
8
8
|
"name": "playbookoverridecollection",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"collectionExportName": "PlaybookOverrideCollectionCollection",
|
|
108
108
|
"schema": {
|
|
109
109
|
"tableName": "_smrt_playbook_overrides",
|
|
110
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"_smrt_playbook_overrides\" (\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\" TEXT NOT NULL DEFAULT '',\n \"tenant_id\"
|
|
110
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"_smrt_playbook_overrides\" (\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\" TEXT NOT NULL DEFAULT '',\n \"tenant_id\" UUID,\n \"title\" TEXT,\n \"description\" TEXT,\n \"planes\" TEXT,\n \"on_step_failure\" TEXT,\n \"enabled\" BOOLEAN,\n \"metadata\" TEXT\n);",
|
|
111
111
|
"columns": {
|
|
112
112
|
"id": {
|
|
113
113
|
"type": "UUID",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"default": ""
|
|
142
142
|
},
|
|
143
143
|
"tenant_id": {
|
|
144
|
-
"type": "
|
|
144
|
+
"type": "UUID",
|
|
145
145
|
"notNull": false,
|
|
146
146
|
"unique": false
|
|
147
147
|
},
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
]
|
|
200
200
|
}
|
|
201
201
|
],
|
|
202
|
-
"version": "
|
|
202
|
+
"version": "0da8c622"
|
|
203
203
|
},
|
|
204
204
|
"importPath": "@happyvertical/smrt-playbooks"
|
|
205
205
|
},
|
|
@@ -223,6 +223,7 @@
|
|
|
223
223
|
"type": "text",
|
|
224
224
|
"required": false,
|
|
225
225
|
"_meta": {
|
|
226
|
+
"sqlType": "UUID",
|
|
226
227
|
"nullable": true
|
|
227
228
|
}
|
|
228
229
|
},
|
|
@@ -395,7 +396,7 @@
|
|
|
395
396
|
],
|
|
396
397
|
"schema": {
|
|
397
398
|
"tableName": "_smrt_playbook_overrides",
|
|
398
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"_smrt_playbook_overrides\" (\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\" TEXT NOT NULL DEFAULT '',\n \"tenant_id\"
|
|
399
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"_smrt_playbook_overrides\" (\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\" TEXT NOT NULL DEFAULT '',\n \"tenant_id\" UUID,\n \"title\" TEXT,\n \"description\" TEXT,\n \"planes\" TEXT,\n \"on_step_failure\" TEXT,\n \"enabled\" BOOLEAN,\n \"metadata\" TEXT\n);",
|
|
399
400
|
"columns": {
|
|
400
401
|
"id": {
|
|
401
402
|
"type": "UUID",
|
|
@@ -429,7 +430,7 @@
|
|
|
429
430
|
"default": ""
|
|
430
431
|
},
|
|
431
432
|
"tenant_id": {
|
|
432
|
-
"type": "
|
|
433
|
+
"type": "UUID",
|
|
433
434
|
"notNull": false,
|
|
434
435
|
"unique": false
|
|
435
436
|
},
|
|
@@ -487,7 +488,7 @@
|
|
|
487
488
|
]
|
|
488
489
|
}
|
|
489
490
|
],
|
|
490
|
-
"version": "
|
|
491
|
+
"version": "0da8c622"
|
|
491
492
|
},
|
|
492
493
|
"importPath": "@happyvertical/smrt-playbooks"
|
|
493
494
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlaybookOverride.d.ts","sourceRoot":"","sources":["../../src/models/PlaybookOverride.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EAGV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,IAAI,4BAA4B,EAEvD,aAAa,EACd,MAAM,aAAa,CAAC;AAUrB,MAAM,WAAW,uBACf,SAAQ,iBAAiB,EACvB,4BAA4B;CAAG;AAkBnC;;;;;;;;GAQG;AACH,qBAgBa,gBAAiB,SAAQ,UAAU;IAE9C,GAAG,EAAE,MAAM,CAAM;
|
|
1
|
+
{"version":3,"file":"PlaybookOverride.d.ts","sourceRoot":"","sources":["../../src/models/PlaybookOverride.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,UAAU,EACV,KAAK,iBAAiB,EAEvB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EAGV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,IAAI,4BAA4B,EAEvD,aAAa,EACd,MAAM,aAAa,CAAC;AAUrB,MAAM,WAAW,uBACf,SAAQ,iBAAiB,EACvB,4BAA4B;CAAG;AAkBnC;;;;;;;;GAQG;AACH,qBAgBa,gBAAiB,SAAQ,UAAU;IAE9C,GAAG,EAAE,MAAM,CAAM;IAKjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAElC,gEAAgE;IAEhE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEpC,sEAAsE;IAEtE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAQ;IAE/B,oEAAoE;IAEpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEnB,OAAO,GAAE,uBAA4B;IAmCjD,WAAW,IAAI,gBAAgB;IAI/B,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAIpD,SAAS,IAAI,SAAS,aAAa,EAAE,GAAG,IAAI;IAI5C,SAAS,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,GAAG,IAAI;IAIxD,eAAe,IAAI,aAAa;IAkBjB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA+BtB,uBAAuB;YAQvB,oCAAoC;YA+BpC,yCAAyC;IA6BxC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAOxB,wBAAwB;YAsHxB,wBAAwB;IA4BtC,OAAO,CAAC,uBAAuB;YAuCjB,oBAAoB;CAmBnC"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-playbooks",
|
|
6
|
-
"packageVersion": "0.
|
|
6
|
+
"packageVersion": "0.52.0",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
10
|
+
"manifest": "43c8cda9dc814965ae3c347844752662c757cc9e4a31b54ac979908a2dbf1856",
|
|
11
|
+
"packageJson": "5b72ec78b9587a7008c3e4836512ed5f5de7e81853bcf11ff645d1ddb545db46",
|
|
12
12
|
"agents": "24cc1ff7167bdb3f2c2b9350d6ba24fb0286013023c5cc49876f16811387e312"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"name": "tenantId",
|
|
183
183
|
"type": "text",
|
|
184
184
|
"required": false,
|
|
185
|
-
"columnType": "
|
|
185
|
+
"columnType": "UUID"
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
"name": "title",
|
|
@@ -535,7 +535,7 @@
|
|
|
535
535
|
"junctionCollections": 0,
|
|
536
536
|
"hierarchicalObjects": 0,
|
|
537
537
|
"polymorphicAssociations": 0,
|
|
538
|
-
"uuidColumns":
|
|
538
|
+
"uuidColumns": 4
|
|
539
539
|
},
|
|
540
540
|
"agentDoc": "# smrt-playbooks\n\nLayered playbook registry, app/tenant overrides, and plan resolution. A\nplaybook is a named, described, layered sequence of steps an agent follows;\nbrowser agents, in-app agents, the Node MCP server, and the CLI all follow the\nsame resolved plan. Fourth instance of the `smrt-prompts` layered-override\npattern — keep it consistent with `prompts`, `languages`, and `features`.\n\n## Core pieces\n\n- `definePlaybook()` registers code defaults in a global process registry\n (`globalThis.__smrtPlaybookRegistry`), so a package-bundled playbook resolves\n with no application registration\n- `resolvePlaybook()` merges the layers and returns a `PlaybookResolution`\n- `PlaybookOverride` (`_smrt_playbook_overrides`) stores partial app-level and\n tenant-level overrides with write-time validation\n- `PlaybookOverrideCollection` exposes the standard SmrtCollection CRUD surface\n\n## Resolution layers (priority low → high)\n\n1. Code default — `definePlaybook({ key, title, description, steps })`\n2. File/config override — `getPackageConfig<PlaybookPackageConfig>('playbooks', defaults)`\n3. App-level stored override — `PlaybookOverride` row with `tenantId = null`\n4. Tenant-level stored override — `PlaybookOverride` row with the current tenant\n5. Runtime override — passed to `resolvePlaybook(key, { override })`\n\nInheritance is field-by-field: a stored column is nullable, and `null` means\n\"use the lower layer\".\n\n## Script semantics\n\nA playbook resolves to a plan the agent executes step by step. **It never\nexecutes as a unit** (epic #2585 invariant 4), so it is never an authority\nboundary and adds no new security object to review. Each step is authorized\nindependently — at the REST boundary in the browser, or by\n`PrincipalRun.assertToolAllowed()` server-side. Nothing in this package\nexecutes a step, and there is deliberately no executor export.\n\nPlaybooks are consequently never atomic and have no compensation.\n`onStepFailure` (`'abort'` | `'continue'`) is the whole of the contract for\nwhat an agent does when step 3 of 5 fails.\n\n## Steps\n\nExactly two kinds in v1, and playbooks cannot nest:\n\n- `{ kind: 'operation', model: '@happyvertical/smrt-commerce:Order', action: 'submit' }`\n — the qualified pair already used by STI discriminators and\n `@crossPackageRef`. **Never a generated tool name**: that is derived from\n model, action, and namespace, and would silently orphan stored tenant\n overrides on a namespace change.\n- `{ kind: 'intent', id }` — a view intent named by its declared #2588\n identity. Valid only where a surface is mounted.\n\nA step referencing another playbook is rejected in `normalizeSteps()` at\ndefinition time.\n\n### Classification is inherited, never self-declared\n\nA step never classifies itself. `resolvePlaybook()` takes a\n`classifier` (and, for intents, an `intents` registry) supplied by the host,\nwhich returns the `CapabilityDeclaration` emitted for the referenced operation\nby `@happyvertical/smrt-types`. Anything undeclared resolves fail-closed to\n`{ effect: 'destructive', idempotent: false, openWorld: true }`. The\nvocabulary itself is older and lower and shared with model tools — it lives in\n`smrt-types`, not here (owning it here would close a\ncore → playbooks → core cycle).\n\n## Plane validity\n\nA playbook declares `planes: readonly ('browser' | 'server')[]`. Resolution for\na caller on an undeclared plane fails closed with reason\n`'plane-not-declared'`.\n\n- Operation-only playbooks default to both planes.\n- A playbook containing a view-intent step defaults to `['browser']`. Server\n validity rides the shipped #2446 browser command/ack bridge, which lets a\n server-side agent drive mounted surfaces with acknowledgement — it must be\n **declared explicitly**, never assumed.\n- The same default applies one level down: a `PlaybookIntentRecord` that\n declares no `planes` is browser-only, so server validity must be declared at\n **both** the playbook and the intent. Silence from the intent registry never\n widens a plane (`'intent-plane-not-declared'`).\n\nThe `#2588` intent registry does not exist yet, so an intent step with no\n`intents` resolver supplied fails closed with\n`'intent-registry-unavailable'`. That resolver is the seam #2588 wires into.\n\n## Editability\n\n`editable` defaults **all-false**, matching `normalizeEditableConfig` in\n`smrt-prompts`. Every stored column has a flag — `title`, `description`,\n`planes`, `onStepFailure`, `enabled`, `metadata` — and `save()` rejects a\nnon-null value for any field the definition has not opted in. `onStepFailure`\nis gated like the rest: flipping a locked playbook from `'abort'` to\n`'continue'` would change what an agent does after a failed prerequisite.\n\n`steps` is **structurally** non-editable, not merely defaulted false:\n\n- `PlaybookEditableConfig` has no `steps` key, and marking one throws at\n definition time\n- `_smrt_playbook_overrides` has **no `steps` column**, so no write of any kind\n has anywhere to put a step list\n- `PlaybookOverride.save()` rejects a `steps` property assigned through the\n untyped option bag rather than dropping it silently\n- `normalizePlaybookLayer()` throws on a `steps` key from the config or runtime\n layer\n- the resolver reads steps only from `PlaybookRegistry`\n\nThe reason is not escalation — under the script model a tenant cannot escalate,\nsince every step is authorized independently regardless of who wrote the list.\nIt is that an agent announcing \"checking out your cart\" while an overridden\nstep list does something else is a description-behavior mismatch.\n\nEnablement overrides are one-directional: a layer may disable, never re-enable\nwhat a lower layer disabled. Enforced in `mergePlaybookLayers()` (`enabled &&\nlayer.enabled`) and rejected at `save()` with a specific message. Plane lists\nnarrow the same way.\n\n## Preflight (#2590)\n\n`preflightPlaybook({ key, plane, principal, resolve, evaluate })` resolves a\nplaybook through the **caller's own layer chain**, decomposes it, and returns a\nper-step verdict — `allow` | `deny` | `unknown` — plus an aggregate. It executes\nnothing.\n\n**Advisory only. Preflight predicts; it never grants.** Every step re-enforces at\nexecution, unconditionally. Without that, a permission revoked mid-playbook would\nleave a cached \"allowed\" standing — a time-of-check/time-of-use bypass. That\nconstraint is also what makes the cache free: a stale `allow` costs a\ncorrectly-denied step, a stale `deny` costs a briefly hidden capability that\nexpires on the TTL, and neither is a security event.\n\nThe two planes are **not symmetric**, deliberately:\n\n| | Server (`createServerStepEvaluator`) | Browser (`createBrowserStepEvaluator`) |\n|---|---|---|\n| Layers | `tool-allowlist`, `operation-permission` | `action-exposure`, `public-access`, `field-permissions`, `app-auth` |\n| Source | `PrincipalRun.isToolAllowed` + the operation-permission predicate | `isApiActionEnabled`, `isRoutePublic`, field read-permission slugs |\n| Intent step | `plane` — denied unless the intent declares `server` | `intent-mount` — always `unknown` |\n| App auth | n/a (the predicate *is* the gate) | **`unknown`** — never evaluated |\n\nBrowser preflight covers the **static layers only**. Generated REST auth is\n`authMiddleware?: (objectName, action) => (req) => Promise<Request | Response>`:\nrequest-bound, `Response`-returning rather than boolean, and free to consult\nsession stores, rate-limit, or audit. It is not a dry-run predicate, so preflight\n**never invokes it**, synthetically or otherwise — the `_preflight` route's\noptions in `smrt-core` carry no auth handle at all, only the boolean\n`appAuthConfigured`. An optional `authPredicate` seam can later be added to\n`BrowserPreflightLayerSource` and turn the `app-auth` `unknown` into a real\nverdict **without changing the report contract**.\n\n`createBrowserPlaybookPreflight()` (in `rest-preflight.ts`) is the provider wired\ninto `APIConfig.playbookPreflight`. Core owns the route and the static-layer\nfacts because `ObjectRegistry` is core's; this package owns resolution and the\nverdict vocabulary — so the dependency stays one-way.\n\n### Not an oracle\n\nEvery playbook the caller's chain cannot resolve — unknown key, disabled,\nwrong-plane, unresolvable intent, or an error thrown anywhere in resolution or\nevaluation — returns the single frozen `PLAYBOOK_PREFLIGHT_UNAVAILABLE` value: no\nkey echo, no reason, no message, and served by the route with an unconditional\n200. An unknown key and an unauthorized key are byte-identical.\n\nTiming is held in the same class from both ends. The unknown-key path pays the\nsame override-layer read a resolvable key pays (`equalizeUnknownKeyCost`),\nbecause `resolvePlaybook()` short-circuits a registry miss before touching the\ndatabase; and unavailable results are cached for unknown and\nregistered-but-unavailable keys **alike**, because caching only one of them would\nput every *repeat* probe of the other in a different timing class. Growth from\nprobing random keys is bounded where it belongs — the preflight cache is capped\nwith expiry-first eviction — not by declining to cache.\n\n### Verdict rules worth knowing\n\n- `deny` beats `unknown` beats `allow` (`worstVerdict`), and a step's `reason` is\n always the first layer that produced its verdict — reason and verdict can never\n describe different layers.\n- Evaluation never short-circuits: preflight exists to say *which* step of five\n would die.\n- A missing **field** read-permission slug redacts a field, it does not fail the\n step, so `field-permissions` stays `allow` with `reason: 'fields-redacted'` and\n the missing slugs attached. Reporting `deny` would predict a failure that will\n not happen.\n- A non-public route with **no** middleware wired is a real, statically knowable\n `deny` (the generator's fail-closed 401). With one wired it is `unknown`.\n- A tool listing may filter on preflight (`filterPlaybooksByPreflight` in\n `smrt-agents`), but that filter is a listing convenience and **never**\n load-bearing for authorization.\n\n## Caching\n\nResolutions are cached per `(key, tenantId, db)` with a TTL. The cache is\ninvalidated on `PlaybookOverride.save()` and `.delete()`; an app-level write\n(`tenantId = null`) clears every tenant's entry for that key, because each\ntenant inherits from it. Use `clearPlaybookCache()` in tests.\n\nA monotonic per-`(db, key)` invalidation generation closes the read-racing-a-\nwrite window; see the Gotchas entry below before touching `cache.ts`.\n\nPreflight results cache separately, per `(principal, key, plane, tenant)`, with a\nshorter TTL and **no invalidation ceremony of their own** — an entry captured\nunder an older generation of the playbook cache is dropped on read. `principal`\nis an opaque, caller-scoped partition key: it is never echoed in a report and\nnever consulted for authority. Use `clearPlaybookPreflightCache()` in tests.\n\nTwo partitioning traps, both of which produce a cross-context read rather than a\nstale one:\n\n- **The tenant is resolved, not defaulted.** `preflightPlaybook()` applies the\n same `tenantId !== undefined ? tenantId : (getTenantId() ?? null)` fallback\n `loadPlaybookBase()` does. Scoping an omitted tenant to `null` would let two\n ambient `withTenant()` callers with one principal share an entry — and the\n report carries the resolved title and description.\n- **The principal must cover every input the evaluation reads.** The REST\n provider's default folds in `appAuthConfigured` (it decides whether\n `public-access` / `app-auth` are verdicts or `unknown`) and distinguishes an\n *absent* permission set (`perm:unpublished`, field layer `unknown`) from an\n explicitly empty one (known, `allow` with redactions). A custom `principal`\n must do the same.\n\n## Gotchas\n\n- **`context` carries the tenant scope.** `save()` sets\n `this.context = this.tenantId ?? '__app__'` and `conflictColumns` is\n `['key', 'context']`. `tenantId` is nullable, and a unique index over it\n would let multiple NULL rows coexist on PostgreSQL and DuckDB. The `context`\n trick (from `smrt-languages`) is what makes the same upsert correct on all\n three dialects — do not \"simplify\" it to `['key', 'tenantId']`.\n- **Identity changes need the delete-then-insert dance.** Changing `key` or\n `tenantId` on an existing row changes the conflict identity, so a plain\n `super.save()` writes the old primary key under a new one. Same handling as\n `PromptOverride` / `LanguageOverride`: a transaction where the driver has\n one, otherwise a staged replacement row deleted only after the new row is\n durable.\n- **JSON fields are stored as strings.** `planes` and `metadata` are text\n columns with guarded `getPlanes()` / `setPlanes()` / `getMetadata()` /\n `setMetadata()` helpers that swallow parse errors. Never override\n `toJSON()`; extend serialization through `transformJSON()`.\n- **`resolvePlaybook()` returns a result, it does not throw for policy.**\n Unknown key, disabled, wrong plane, and unresolvable intents all come back as\n `{ ok: false, reason, message }`. It *does* throw for programming errors —\n notably a `steps` key on an override layer.\n- **The cache carries an invalidation generation, and this is where it\n diverges from `smrt-prompts`.** A resolution captures\n `getPlaybookCacheGeneration(key, db)` before its asynchronous layer loads and\n hands it back to `setCachedPlaybookBase()`; a concurrent `save()` / `delete()`\n bumps the generation, and the in-flight resolution is then refused the cache\n write instead of repopulating the key it just invalidated with the pre-write\n value. Without it, \"a stale entry is never served after a write\" held only\n until a read raced a write, and then failed for the full 30s TTL. Generations\n are tracked per `(db, key)`, not per tenant, because an app-level row is\n inherited by every tenant. `clearPlaybookCache()` bumps rather than resets\n them, so a resolution that started before the clear cannot write back either.\n `smrt-prompts` and `smrt-languages` carry the same mechanism (#2609);\n `smrt-languages` additionally keys its generation by locale, and both raise a\n single `clearedThrough` floor in `clear*Cache()` instead of the per-key bump\n here. The per-key bump cannot reach a key that has never been invalidated —\n it has no map entry, so it reads as generation 0 before and after the clear\n and an in-flight resolution writes its pre-clear value back. `clearPlaybookCache()`\n still carries that gap; #2716 ports the floor.\n- **Restart vitest after adding a decorated class**; the manifest is generated\n at startup.\n\n## Related\n\n- `@happyvertical/smrt-prompts` — the pattern this package clones\n- `@happyvertical/smrt-languages` — source of the `context` column convention\n- `@happyvertical/smrt-features` — parallel package for feature flags\n- Epic #2585 — declared agent surface; #2587 capability vocabulary,\n #2588 view intents, #2590 preflight, #2591 manifest emission\n"
|
|
541
541
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-playbooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Layered playbook registry, app/tenant overrides, and plan resolution for SMRT agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"./manifest.json": "./dist/manifest.json"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@happyvertical/smrt-config": "0.
|
|
23
|
-
"@happyvertical/smrt-core": "0.
|
|
24
|
-
"@happyvertical/smrt-tenancy": "0.
|
|
25
|
-
"@happyvertical/smrt-types": "0.
|
|
26
|
-
"@happyvertical/sql": "^0.96.
|
|
22
|
+
"@happyvertical/smrt-config": "0.52.0",
|
|
23
|
+
"@happyvertical/smrt-core": "0.52.0",
|
|
24
|
+
"@happyvertical/smrt-tenancy": "0.52.0",
|
|
25
|
+
"@happyvertical/smrt-types": "0.52.0",
|
|
26
|
+
"@happyvertical/sql": "^0.96.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@happyvertical/smrt-vitest": "0.
|
|
29
|
+
"@happyvertical/smrt-vitest": "0.52.0",
|
|
30
30
|
"@types/node": "24.13.6",
|
|
31
31
|
"typescript": "6.0.3",
|
|
32
32
|
"vite": "8.1.4",
|