@happyvertical/smrt-core 0.55.19 → 0.55.21

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.
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.55.19",
5
+ "packageVersion": "0.55.21",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-core:SmrtClass": {
8
8
  "name": "smrtclass",
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-core",
6
- "packageVersion": "0.55.19",
6
+ "packageVersion": "0.55.21",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "12e7a85fe74dff733bad723ee812820fa6636fc50af24550742a65ea347256c7",
11
- "packageJson": "e193fb3ad945b3b2acb7d73eb7b9cf5a6c1cc8e5c42402d14489e5f46456e54e",
10
+ "manifest": "5bcc6326190c7725a495679e91140adedf7f61d840370d75e2df21640c3beff6",
11
+ "packageJson": "7d78fa56f9071e2be127fc55415b653f8e0fbff80f536db3a7354828afc8d84a",
12
12
  "agents": "a55cc2d7dccf8f5dc9fd515361fddf719466159ee11e265285cf14b3fcdd96fa",
13
13
  "moduleDoc:agents/object-runtime.md": "93ba429467c5f3c886613fe5748e14844ceb8d2bbdebd47214a013a8e60b2a06",
14
14
  "moduleDoc:agents/revision-guard.md": "f6ea32a6860964c98aba14b93587f40b33c8f7d0deccb2c54d21bff45db3da42",
@@ -26,8 +26,8 @@
26
26
  "moduleDoc:agents/model-moves.md": "c64147fee1b7ec242fd1c178e7aea7cc90cafc704dee2738141c7a6d61403cd8",
27
27
  "moduleDoc:agents/run-once.md": "0b858c18c78eca5050257a2af06f16de99327e05e0bf9fee9e6277242e7707ef",
28
28
  "moduleDoc:agents/recipes.md": "50ddedb14583e073c4af9c15142f4838c4326c1955931f743148e41e15dbdf38",
29
- "moduleDoc:agents/cookbook.md": "eba11de72b49ee196aa8d7bcc48cf42be9322f6c6200896a524ce45f1cd15831",
30
- "moduleDoc:agents/cookbook-engine.md": "d4943b15e7b90a133b28a4c86b498c16eb623cd78b5022585873276cb1a5a5ab",
29
+ "moduleDoc:agents/cookbook.md": "20c256f4f6219f9b2add6365ef930e0bb3eda37e7866ddbc4bdd466830f76e4f",
30
+ "moduleDoc:agents/cookbook-engine.md": "4ea0b3cdb952788372596cbf1f51e326b0d696302f6358a21e3330402ef1f8aa",
31
31
  "moduleDoc:agents/browser-entry.md": "04d26eff8a65f02e88b20cbb77b1f94f58e29b62178ac6fbec129ee436a732eb",
32
32
  "moduleDoc:agents/test-types.md": "37c009ae2bd21001d83bba6cfab2047465fe32d5443de12661e24ba7d4e2d613"
33
33
  },
@@ -1221,12 +1221,12 @@
1221
1221
  {
1222
1222
  "path": "agents/cookbook.md",
1223
1223
  "module": "cookbook",
1224
- "content": "# Cookbook v1 contract (#3747, #3748)\n\nA cookbook is the portable JSON document that says what an app is made of.\nDefined once here so the CLI (`smrt cookbook validate|apply`) and the cookbook\napp shell read the same thing.\n\n| Piece | Where |\n|---|---|\n| Types (type-only) | `@happyvertical/smrt-types`: `Cookbook`, `CookbookPolicyRow`, `CookbookLayout`, `CookbookTheme`, `CookbookOverviewOverride`, `CookbookExposureSurface` |\n| Parse / validate / migrate (browser-safe) | `@happyvertical/smrt-core/cookbook`: `validateCookbook(doc, options)`, `parseCookbookText`, `CookbookDiagnostic`, `COOKBOOK_SCHEMA_URL`, `COOKBOOK_LEGACY_SCHEMA_URL`, `COOKBOOK_VERSION` |\n| JSON Schema | `@happyvertical/smrt-core/cookbook/v1.schema.json` (`$id` is the `$schema` URL; s-m-r-t.dev hosts it later) |\n| App shell | `@happyvertical/smrt-svelte/cookbook` renders a cookbook; `loadCookbookApp` in `/cookbook/server` loads and validates it ([cookbook-shell.md](../../smrt-svelte/agents/cookbook-shell.md)) |\n| Fixtures | `src/cookbook/__fixtures__/` (the planner's bakery, mechanic, welder, yoga-studio documents) |\n\nFields: `$schema`, `version: 1`, optional `name`/`description`, `recipes`,\n`features` (absent reads as `[]`), `policies` (app-scope smrt-fields rows),\n`exposure`, `layout` (smrt-svelte `ShellLayout`), `theme`, `overviews`\n(smrt-svelte `OverviewOverride` per overview id). Sample data is not part of\nthe document; the planner's library wrapper (id, icon, keywords, settings) is\nplanner-side.\n\n`theme.customCss` is optional source text (at most 65,536 characters). It is\nportable through validation and engine import/export, but no cookbook consumer\ninjects it into a page automatically. An app may use it when generating its own\nstylesheet after review.\n\n`theme.fontFamily` is an optional host font key independent of the preset and\nbrand colour. A host that knows the key applies it; unknown keys are retained\nin the portable document. Older `theme.custom.fontFamily` values still read.\n\n## Rules\n\n- Versioning: add optional fields within `version: 1`; readers keep and ignore\n unknown keys (`additionalProperties: true`). A breaking change is `version: 2`.\n- Layout and overview values are typed structurally so smrt-types and core never\n depend on smrt-svelte. Hosts pass manifest-derived navigation ids and resolved\n shell `placementItems` (dock, slot-content, recipe, and host ids) in `layout`,\n `themePresets`, `overviewIds`, and a `validateOverview` callback to the core\n validator for checks that need resolved pages or a widget registry. The\n callback returns canonical plain data; cookbook values are never executed.\n Malformed or host-rejected overview entries are dropped and reported as\n warning diagnostics, while a malformed top-level `overviews` field rejects\n the document. `CookbookLayout`/`CookbookOverviewOverride` are\n identical to the smrt-svelte types; a typechecked assertion in smrt-svelte\n (`cookbook/__tests__/contract-types.ts`) fails when either side adds a field.\n- Legacy `.../blueprint/v1.json` is accepted on read and rewritten; never written.\n- Recipe existence, `requires`/`requiresAny` and setting targets need manifests:\n `validateCookbook(doc, { recipes })` checks ids when a catalog is passed;\n the rest is the CLI's job.\n- Every result includes structured `diagnostics` (`severity`, stable `code`,\n `path`, `message`). The existing `errors` and `warnings` string arrays remain\n for compatibility and are derived from those diagnostics.\n- Keep the JSON Schema and `validate.ts` in step; the test validates the same\n fixtures against both.\n\n## Applying a cookbook\n\n`smrt cookbook validate <file|url>` and `smrt cookbook apply <file|url> [dir]`\n(`packages/cli/agents/cookbook-apply.md`) are the consumers. Apply writes\n`smrt.cookbook.json` at the project root, plus the packages the recipes,\nfeatures and policies need, into `package.json`. The file is the cookbook\nverbatim (legacy `$schema` rewritten); apps read it, apply does not interpret it.\n`policies` is required by the validator (use `[]`). The cookbook carries no package\nversions, so apply uses the project's smrt line.\n\n## Hosting package models\n\nAn app that applies a cookbook lists the recipes' packages in `consumer.packages`\nand nothing else: the `smrt()` Vite preset derives the REST allowlist from the\ncookbook, so the Orders of a `commerce.sales` recipe get `/api/orders` routes and\nan `@smrt/web` definition (see [build-knowledge.md](build-knowledge.md), \"Package\nmodels over REST\"). `exposure` listing `api` for a model withdraws its route. Models that share an\nSTI table (the bakery's `Order`, `WholesaleOrder`, `PurchaseOrder` and\n`ProductionOrder`; `Product` and `Material`) each get their own route and\ndefinition, at `/<class collection>` (`/orders`, `/productionorders`), listing\nonly their own rows; the build-knowledge section explains why.\n\nPermissions do not follow the route. The permission catalog grants one namespace\nper STI table, the base's (`contracts.create`; there is no `orders.create`), so a\nhosted subclass moved to its own route collection keeps authorizing against the\nbase's slugs: the hosting rewrite records the original collection as\n`permissionCollection`, which the generated route guard and `sync/apply` read\nbefore the (rewritten) route collection. It matches the fail-closed `api`\ninheritance and the CLI/MCP operation guards, which already derive the base's\nslug. No role is widened: a caller needs exactly the grants the base requires.\nReads need no operation slug. A subclass that declares its own `collection` has\nits own namespace and is untouched.\n\n## Editing a cookbook\n\nThe command engine (`@happyvertical/smrt-core/cookbook/engine`) is the one place\nthat changes a cookbook: see [cookbook-engine.md](cookbook-engine.md).\n"
1224
+ "content": "# Cookbook v1 contract (#3747, #3748)\n\nA cookbook is the portable JSON document that says what an app is made of.\nDefined once here so the CLI (`smrt cookbook validate|apply`) and the cookbook\napp shell read the same thing.\n\n| Piece | Where |\n|---|---|\n| Types (type-only) | `@happyvertical/smrt-types`: `Cookbook`, `CookbookPolicyRow`, `CookbookLayout`, `CookbookTheme`, `CookbookOverviewOverride`, `CookbookExposureSurface` |\n| Parse / validate / migrate (browser-safe) | `@happyvertical/smrt-core/cookbook`: `validateCookbook(doc, options)`, `parseCookbookText`, `CookbookDiagnostic`, `COOKBOOK_SCHEMA_URL`, `COOKBOOK_LEGACY_SCHEMA_URL`, `COOKBOOK_VERSION` |\n| JSON Schema | `@happyvertical/smrt-core/cookbook/v1.schema.json` (`$id` is the `$schema` URL; s-m-r-t.dev hosts it later) |\n| App shell | `@happyvertical/smrt-svelte/cookbook` renders a cookbook; `loadCookbookApp` in `/cookbook/server` loads and validates it ([cookbook-shell.md](../../smrt-svelte/agents/cookbook-shell.md)) |\n| Fixtures | `src/cookbook/__fixtures__/` (the planner's bakery, mechanic, welder, yoga-studio documents) |\n\nFields: `$schema`, `version: 1`, optional `name`/`description`, `recipes`,\n`features` (absent reads as `[]`), `policies` (app-scope smrt-fields rows),\n`exposure`, `layout` (smrt-svelte `ShellLayout`), `theme`, `overviews`\n(smrt-svelte `OverviewOverride` per overview id). Sample data is not part of\nthe document; the planner's library wrapper (id, icon, keywords, settings) is\nplanner-side.\n\n`theme.customCss` is optional source text (at most 65,536 characters). It is\nportable through validation and engine import/export, but no cookbook consumer\ninjects it into a page automatically. An app may use it when generating its own\nstylesheet after review.\n\n`theme.fontFamily` is an optional host font key independent of the preset and\nbrand colour. A host that knows the key applies it; unknown keys are retained\nin the portable document. Older `theme.custom.fontFamily` values still read.\n\n`theme.borderRadius` (`none|sm|md|lg|xl|full`), `theme.density`\n(`comfortable|touch`) and `theme.contrast` (`auto|standard|more`) are optional\nand mirror the `ThemeProvider` props of the same names. Absent means the\npreset's own value (`contrast` absent is `auto`, which follows the OS). They are\nadditive: a document without them validates and reads unchanged, so there is no\nversion bump. Anything outside the enums is an `invalid_value` diagnostic at\n`theme.<field>`.\n\n`theme.custom.primary` and `ThemeProvider`'s `primaryColor` are different\ncontrols. The cookbook sets `custom.primary`: the shell builds a whole theme\nfrom it (`createThemeFromColor`) that REPLACES the preset, so `preset` is\nignored while it is present. `primaryColor` instead TINTS the current preset by\noverriding only the derived primary colours (`derivePrimaryOverrides`). The\ncookbook has no `primaryColor` field and the shell never passes one.\n\n## Rules\n\n- Versioning: add optional fields within `version: 1`; readers keep and ignore\n unknown keys (`additionalProperties: true`). A breaking change is `version: 2`.\n- Layout and overview values are typed structurally so smrt-types and core never\n depend on smrt-svelte. Hosts pass manifest-derived navigation ids and resolved\n shell `placementItems` (dock, slot-content, recipe, and host ids) in `layout`,\n `themePresets`, `overviewIds`, and a `validateOverview` callback to the core\n validator for checks that need resolved pages or a widget registry. The\n callback returns canonical plain data; cookbook values are never executed.\n Malformed or host-rejected overview entries are dropped and reported as\n warning diagnostics, while a malformed top-level `overviews` field rejects\n the document. `CookbookLayout`/`CookbookOverviewOverride` are\n identical to the smrt-svelte types; a typechecked assertion in smrt-svelte\n (`cookbook/__tests__/contract-types.ts`) fails when either side adds a field.\n- Legacy `.../blueprint/v1.json` is accepted on read and rewritten; never written.\n- Recipe existence, `requires`/`requiresAny` and setting targets need manifests:\n `validateCookbook(doc, { recipes })` checks ids when a catalog is passed;\n the rest is the CLI's job.\n- Every result includes structured `diagnostics` (`severity`, stable `code`,\n `path`, `message`). The existing `errors` and `warnings` string arrays remain\n for compatibility and are derived from those diagnostics.\n- Keep the JSON Schema and `validate.ts` in step; the test validates the same\n fixtures against both.\n\n## Applying a cookbook\n\n`smrt cookbook validate <file|url>` and `smrt cookbook apply <file|url> [dir]`\n(`packages/cli/agents/cookbook-apply.md`) are the consumers. Apply writes\n`smrt.cookbook.json` at the project root, plus the packages the recipes,\nfeatures and policies need, into `package.json`. The file is the cookbook\nverbatim (legacy `$schema` rewritten); apps read it, apply does not interpret it.\n`policies` is required by the validator (use `[]`). The cookbook carries no package\nversions, so apply uses the project's smrt line.\n\n## Hosting package models\n\nAn app that applies a cookbook lists the recipes' packages in `consumer.packages`\nand nothing else: the `smrt()` Vite preset derives the REST allowlist from the\ncookbook, so the Orders of a `commerce.sales` recipe get `/api/orders` routes and\nan `@smrt/web` definition (see [build-knowledge.md](build-knowledge.md), \"Package\nmodels over REST\"). `exposure` listing `api` for a model withdraws its route. Models that share an\nSTI table (the bakery's `Order`, `WholesaleOrder`, `PurchaseOrder` and\n`ProductionOrder`; `Product` and `Material`) each get their own route and\ndefinition, at `/<class collection>` (`/orders`, `/productionorders`), listing\nonly their own rows; the build-knowledge section explains why.\n\nPermissions do not follow the route. The permission catalog grants one namespace\nper STI table, the base's (`contracts.create`; there is no `orders.create`), so a\nhosted subclass moved to its own route collection keeps authorizing against the\nbase's slugs: the hosting rewrite records the original collection as\n`permissionCollection`, which the generated route guard and `sync/apply` read\nbefore the (rewritten) route collection. It matches the fail-closed `api`\ninheritance and the CLI/MCP operation guards, which already derive the base's\nslug. No role is widened: a caller needs exactly the grants the base requires.\nReads need no operation slug. A subclass that declares its own `collection` has\nits own namespace and is untouched.\n\n## Editing a cookbook\n\nThe command engine (`@happyvertical/smrt-core/cookbook/engine`) is the one place\nthat changes a cookbook: see [cookbook-engine.md](cookbook-engine.md).\n"
1225
1225
  },
1226
1226
  {
1227
1227
  "path": "agents/cookbook-engine.md",
1228
1228
  "module": "cookbook-engine",
1229
- "content": "# Cookbook command engine (#3753)\n\n`@happyvertical/smrt-core/cookbook/engine` (`src/cookbook/engine/`) is the one\nplace the rules for changing a [cookbook](cookbook.md) live. A planner UI, a\nlocal Kitchen MCP server, the CLI and a website all call it; each is an adapter.\nIt imports only relative modules and `@happyvertical/smrt-types` types, so it\nruns in Node and in a browser. No SvelteKit, storage, singleton or inference.\n\nIt lives in `smrt-core` because the contract and validator (`./cookbook`) do and\nthe package DAG forbids a back edge: types -> core. A separate package would add\na node for pure functions with no other dependency.\n\n## API\n\n```ts\nimport { createCookbookEngine } from '@happyvertical/smrt-core/cookbook/engine';\nconst engine = createCookbookEngine({ catalog, cookbook?, state? });\nengine.run({ id?, name, input, expectedRevision? }) // CommandResult, never throws\nengine.batch({ id?, commands, expectedRevision? }) // BatchResult, atomic\nengine.snapshot() / .cookbook() / .state() / .subscribe(fn)\nengine.tools / engine.batchTool // JSON Schema tool definitions\n```\n\n- **Catalog is data** (`EngineCatalog`): `recipes` (manifest `RecipeDefinition`\n fits), `models` (`id, name, packageId, exposed?, fields?`), optional\n `cookbooks` (a library), `themePresets`, `currencies`, `settingTargets`.\n Nothing is bundled. Build it from published manifests or `smrt-knowledge.json`.\n- **Commands** (20, `COOKBOOK_COMMANDS_VERSION = 1`): `apply_cookbook` (select),\n `import_cookbook`, `add_cookbook`, `remove_cookbook`, `add_recipes`,\n `remove_recipes`, `add_features`, `remove_features`, `set_settings`,\n `set_policy`, `set_theme`, `reset_theme`, `set_name`, `rename_section`,\n `rename_item`, `hide`, `show`, `validate`, `export_cookbook`, `undo`. Navigation\n and focus are not commands: they are an adapter concern.\n- **Errors** are typed (`CommandError`: `code`, `message`, `path?`, `details?`):\n `unknown_command`, `invalid_input`, `not_found`, `confirmation_required`,\n `conflict`, `id_reuse`, `batch_failed`, `failed`.\n- **Result** carries the `receipt` (`name, id?, summary, changed, revisionBefore,\n revisionAfter, undoId?, changes?, warnings?`), the compact `snapshot` and\n optional `data` (`validate`, `export_cookbook`).\n\n## Execution contract\n\n- **Revisions** count applied changes (a no-op does not move it), so equal\n histories give equal revisions. `expectedRevision` that is not current fails\n `conflict` with `details.{expectedRevision,currentRevision}`: an older model\n action cannot overwrite a newer manual edit. Send it on every model action.\n- **Command ids**: a repeated `id` with the same command returns the first\n result with `replayed: true` and applies nothing (failures replay too: take a\n new id after re-reading). The same id with a different command is `id_reuse`.\n The journal keeps `maxJournal` (500) ids and is part of `state()`.\n- **Atomic batches** run on a working copy; one failure leaves the state\n byte-identical and returns `batch_failed` naming `details.index`. A batch that\n changes n times advances the revision by n, as n runs would, and has one undo.\n `undo` and per-command ids/revisions are not allowed inside a batch.\n- **Undo** restores the document from before the change; it is a `conflict`\n while the plan differs from what that change left, unless `force`.\n- **Recovery**: `state()` is plain JSON (revision, applied library id, document,\n journal); `createCookbookEngine({ catalog, state })` resumes it, retries\n included. Undo entries are not persisted.\n- **Canonical document**: ids sorted, policies sorted, empty sections absent,\n key order fixed, so two engines export identical text. Recipe/feature ids the\n catalog lacks stay in the document and show as `snapshot.unavailable`.\n\n## Deliberately not here\n\nPackage or starter version pinning (waits on #3604/#3754). Deep overview-widget\nand nav-id checks (smrt-svelte owns them; `import_cookbook` carries `overviews`\nand `layout` through unjudged, `validate` only flags layout ids the menu lacks).\nTyped diagnostics from `validateCookbook` itself (#3754): `validate` wraps its\nstrings as `invalid_document` until it returns them.\n\n## Becoming an adapter: what smrt-planner changes (after the smrt release)\n\nParity to hold: the planner's `tests/headless-planner.test.ts` scenario is\nported in `__tests__/engine.test.ts` (`SCRIPT`) over a data catalog.\n\n1. Depend on the released `@happyvertical/smrt-core`; build `EngineCatalog` from\n its catalog data (`recipes.json`/`catalog.json` -> `recipes`, `models`,\n `cookbooks`: the library wrapper keeps id/icon/keywords planner-side).\n2. Delete `commands/execute.ts`, `commands/runner.ts`, `commands/snapshot.ts`,\n `commands/schemas.ts` (keep `checkSchema` only if still used), `recipes/\n plan-data.ts`, `recipes/resolve.ts`, `settings/app-settings.ts` rules,\n `assistant/change.ts` apply* helpers, `cookbook/assemble.ts` and the\n `createPlainHost`. `createHeadlessPlanner` becomes `createCookbookEngine`\n behind the same `run/snapshot/subscribe/cookbook` names.\n3. The browser controller keeps: `focus` and the planner tab/section cells\n (snapshot `focus` is added by the adapter), reactive wrapping of\n `engine.subscribe`, `onReplaced` (regenerate sample data after\n `apply_cookbook`), and the `cookbookStore` <-> `engine.state()` sync\n (`engine.cookbook()` to persist; rebuild with `cookbook`/`state` on load).\n4. Command differences to absorb: `apply_cookbook`/`import_cookbook` report\n `changed` by content; `rename_*`/`hide`/`show` need the catalog `models`\n `packageId` for item ids; undo is whole-document (the old per-slice undo goes);\n `unsupported` is gone (the engine always has every slice); new `set_name`,\n `validate`; receipts gain `revisionBefore/After`; `expectedRevision` moves\n into the command (the option form still works).\n5. Overview customisation stays a planner/smrt-svelte concern; hold `overviews`\n in the document via `import_cookbook` and write them with a planner-side\n command until an engine command is agreed.\n6. MCP/Kitchen (#3752, done): `smrt kitchen mcp` holds one engine per plan id and\n serves `engine.batchTool`'s schema as `run_commands`; the plan reaches disk\n as `smrt.cookbook.json` through `cookbook apply` (`cli/agents/kitchen.md`).\n"
1229
+ "content": "# Cookbook command engine (#3753)\n\n`@happyvertical/smrt-core/cookbook/engine` (`src/cookbook/engine/`) is the one\nplace the rules for changing a [cookbook](cookbook.md) live. A planner UI, a\nlocal Kitchen MCP server, the CLI and a website all call it; each is an adapter.\nIt imports only relative modules and `@happyvertical/smrt-types` types, so it\nruns in Node and in a browser. No SvelteKit, storage, singleton or inference.\n\nIt lives in `smrt-core` because the contract and validator (`./cookbook`) do and\nthe package DAG forbids a back edge: types -> core. A separate package would add\na node for pure functions with no other dependency.\n\n## API\n\n```ts\nimport { createCookbookEngine } from '@happyvertical/smrt-core/cookbook/engine';\nconst engine = createCookbookEngine({ catalog, cookbook?, state? });\nengine.run({ id?, name, input, expectedRevision? }) // CommandResult, never throws\nengine.batch({ id?, commands, expectedRevision? }) // BatchResult, atomic\nengine.snapshot() / .cookbook() / .state() / .subscribe(fn)\nengine.tools / engine.batchTool // JSON Schema tool definitions\n```\n\n- **Catalog is data** (`EngineCatalog`): `recipes` (manifest `RecipeDefinition`\n fits), `models` (`id, name, packageId, exposed?, fields?`), optional\n `cookbooks` (a library), `themePresets`, `currencies`, `settingTargets`.\n Nothing is bundled. Build it from published manifests or `smrt-knowledge.json`.\n- **Commands** (20, `COOKBOOK_COMMANDS_VERSION = 1`): `apply_cookbook` (select),\n `import_cookbook`, `add_cookbook`, `remove_cookbook`, `add_recipes`,\n `remove_recipes`, `add_features`, `remove_features`, `set_settings`,\n `set_policy`, `set_theme`, `reset_theme`, `set_name`, `rename_section`,\n `rename_item`, `hide`, `show`, `validate`, `export_cookbook`, `undo`. Navigation\n and focus are not commands: they are an adapter concern.\n- **`set_theme`** takes `preset`, `primary` (a brand colour that replaces the\n preset, see cookbook.md), `colorScheme`, `fontFamily` (`null` restores the\n preset font), and `borderRadius` (`none|sm|md|lg|xl|full`), `density`\n (`comfortable|touch`), `contrast` (`auto|standard|more`). Each is optional and\n only the fields given change; repeating the same input is an unchanged\n receipt. Values outside the enums are `invalid_input`. `snapshot().theme`\n reports the three new fields (`null` when unset); `reset_theme` clears them.\n- **Errors** are typed (`CommandError`: `code`, `message`, `path?`, `details?`):\n `unknown_command`, `invalid_input`, `not_found`, `confirmation_required`,\n `conflict`, `id_reuse`, `batch_failed`, `failed`.\n- **Result** carries the `receipt` (`name, id?, summary, changed, revisionBefore,\n revisionAfter, undoId?, changes?, warnings?`), the compact `snapshot` and\n optional `data` (`validate`, `export_cookbook`).\n\n## Execution contract\n\n- **Revisions** count applied changes (a no-op does not move it), so equal\n histories give equal revisions. `expectedRevision` that is not current fails\n `conflict` with `details.{expectedRevision,currentRevision}`: an older model\n action cannot overwrite a newer manual edit. Send it on every model action.\n- **Command ids**: a repeated `id` with the same command returns the first\n result with `replayed: true` and applies nothing (failures replay too: take a\n new id after re-reading). The same id with a different command is `id_reuse`.\n The journal keeps `maxJournal` (500) ids and is part of `state()`.\n- **Atomic batches** run on a working copy; one failure leaves the state\n byte-identical and returns `batch_failed` naming `details.index`. A batch that\n changes n times advances the revision by n, as n runs would, and has one undo.\n `undo` and per-command ids/revisions are not allowed inside a batch.\n- **Undo** restores the document from before the change; it is a `conflict`\n while the plan differs from what that change left, unless `force`.\n- **Recovery**: `state()` is plain JSON (revision, applied library id, document,\n journal); `createCookbookEngine({ catalog, state })` resumes it, retries\n included. Undo entries are not persisted.\n- **Canonical document**: ids sorted, policies sorted, empty sections absent,\n key order fixed, so two engines export identical text. Recipe/feature ids the\n catalog lacks stay in the document and show as `snapshot.unavailable`.\n\n## Deliberately not here\n\nPackage or starter version pinning (waits on #3604/#3754). Deep overview-widget\nand nav-id checks (smrt-svelte owns them; `import_cookbook` carries `overviews`\nand `layout` through unjudged, `validate` only flags layout ids the menu lacks).\nTyped diagnostics from `validateCookbook` itself (#3754): `validate` wraps its\nstrings as `invalid_document` until it returns them.\n\n## Becoming an adapter: what smrt-planner changes (after the smrt release)\n\nParity to hold: the planner's `tests/headless-planner.test.ts` scenario is\nported in `__tests__/engine.test.ts` (`SCRIPT`) over a data catalog.\n\n1. Depend on the released `@happyvertical/smrt-core`; build `EngineCatalog` from\n its catalog data (`recipes.json`/`catalog.json` -> `recipes`, `models`,\n `cookbooks`: the library wrapper keeps id/icon/keywords planner-side).\n2. Delete `commands/execute.ts`, `commands/runner.ts`, `commands/snapshot.ts`,\n `commands/schemas.ts` (keep `checkSchema` only if still used), `recipes/\n plan-data.ts`, `recipes/resolve.ts`, `settings/app-settings.ts` rules,\n `assistant/change.ts` apply* helpers, `cookbook/assemble.ts` and the\n `createPlainHost`. `createHeadlessPlanner` becomes `createCookbookEngine`\n behind the same `run/snapshot/subscribe/cookbook` names.\n3. The browser controller keeps: `focus` and the planner tab/section cells\n (snapshot `focus` is added by the adapter), reactive wrapping of\n `engine.subscribe`, `onReplaced` (regenerate sample data after\n `apply_cookbook`), and the `cookbookStore` <-> `engine.state()` sync\n (`engine.cookbook()` to persist; rebuild with `cookbook`/`state` on load).\n4. Command differences to absorb: `apply_cookbook`/`import_cookbook` report\n `changed` by content; `rename_*`/`hide`/`show` need the catalog `models`\n `packageId` for item ids; undo is whole-document (the old per-slice undo goes);\n `unsupported` is gone (the engine always has every slice); new `set_name`,\n `validate`; receipts gain `revisionBefore/After`; `expectedRevision` moves\n into the command (the option form still works).\n5. Overview customisation stays a planner/smrt-svelte concern; hold `overviews`\n in the document via `import_cookbook` and write them with a planner-side\n command until an engine command is agreed.\n6. MCP/Kitchen (#3752, done): `smrt kitchen mcp` holds one engine per plan id and\n serves `engine.batchTool`'s schema as `run_commands`; the plan reaches disk\n as `smrt.cookbook.json` through `cookbook apply` (`cli/agents/kitchen.md`).\n"
1230
1230
  },
1231
1231
  {
1232
1232
  "path": "agents/browser-entry.md",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-core",
3
- "version": "0.55.19",
3
+ "version": "0.55.21",
4
4
  "description": "Core AI agent framework with standardized collections, object-relational mapping, and code generators",
5
5
  "author": "HappyVertical",
6
6
  "type": "module",
@@ -189,9 +189,9 @@
189
189
  "@happyvertical/files": "^0.103.5",
190
190
  "@happyvertical/json": "^0.103.5",
191
191
  "@happyvertical/logger": "^0.103.5",
192
- "@happyvertical/smrt-config": "0.55.19",
193
- "@happyvertical/smrt-scanner": "0.55.19",
194
- "@happyvertical/smrt-types": "0.55.19",
192
+ "@happyvertical/smrt-config": "0.55.21",
193
+ "@happyvertical/smrt-scanner": "0.55.21",
194
+ "@happyvertical/smrt-types": "0.55.21",
195
195
  "@happyvertical/sql": "^0.103.5",
196
196
  "@happyvertical/utils": "^0.103.5",
197
197
  "@noble/hashes": "^2.4.0",