@happyvertical/smrt-template-sveltekit 0.53.3 → 0.54.1

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.
Files changed (49) hide show
  1. package/AGENTS.md +60 -27
  2. package/README.md +23 -16
  3. package/mcp-apps-template/package.dependencies.json +2 -2
  4. package/mcp-apps-template/src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts +10 -6
  5. package/mcp-apps-template/src/routes/api/mcp/+server.ts +68 -12
  6. package/package.json +19 -15
  7. package/template/AGENTS.md +20 -5
  8. package/template/Dockerfile +0 -1
  9. package/template/INSTALL_PROMPT.md +2 -2
  10. package/template/README.md +86 -40
  11. package/template/compose.yaml +2 -2
  12. package/template/package.json +28 -27
  13. package/template/smrt.config.ts +16 -34
  14. package/template/src/app.d.ts +4 -11
  15. package/template/src/hooks.server.ts +10 -51
  16. package/template/src/lib/server/smrt.ts +22 -98
  17. package/template/src/routes/+layout.server.ts +4 -12
  18. package/template/src/routes/+layout.svelte +27 -99
  19. package/template/src/routes/+page.server.ts +4 -4
  20. package/template/src/routes/api/_runtime/diagnostics/+server.ts +17 -3
  21. package/template/src/routes/api/_runtime/health/+server.ts +4 -28
  22. package/template/src/routes/settings/+page.svelte +3 -34
  23. package/template/src/routes/setup/+page.server.ts +7 -94
  24. package/template/src/routes/setup/+page.svelte +3 -23
  25. package/template/vite.config.ts +5 -43
  26. package/template.config.js +11 -11
  27. package/mcp-apps-template/src/lib/McpAppsBridge.svelte +0 -37
  28. package/mcp-apps-template/src/lib/server/mcp-hosted-principal.ts +0 -12
  29. package/mcp-apps-template/src/lib/server/mcp-hosted.ts +0 -28
  30. package/mcp-apps-template/src/lib/server/mcp.ts +0 -85
  31. package/template/scripts/smrt-app.mjs +0 -885
  32. package/template/scripts/smrt-mcp-apps.mjs +0 -18
  33. package/template/scripts/smrt-operation-lock.mjs +0 -102
  34. package/template/scripts/smrt-portability-assets.mjs +0 -707
  35. package/template/scripts/smrt-portability.mjs +0 -470
  36. package/template/scripts/smrt-prepare-migration.mjs +0 -93
  37. package/template/scripts/smrt-process.mjs +0 -82
  38. package/template/scripts/smrt-provider-readiness.mjs +0 -33
  39. package/template/scripts/smrt-runtime-identity.mjs +0 -245
  40. package/template/scripts/smrt-vite.mjs +0 -45
  41. package/template/scripts/smrt-web.mjs +0 -10
  42. package/template/scripts/smrt-worker.mjs +0 -100
  43. package/template/scripts/smrt-writer-lease.mjs +0 -199
  44. package/template/src/lib/RuntimeDiagnosticsWebMcp.svelte +0 -11
  45. package/template/src/lib/runtime-diagnostics-webmcp.ts +0 -108
  46. package/template/src/lib/server/application-runtime.ts +0 -262
  47. package/template/src/lib/server/runtime-diagnostics.ts +0 -114
  48. package/template/src/lib/server/tenancy.ts +0 -114
  49. package/template/src/smrt-webmcp.d.ts +0 -6
package/AGENTS.md CHANGED
@@ -17,6 +17,22 @@ It is the ground-up alternative to `smrt-saas-starter`.
17
17
  images ship no Corepack, so the Dockerfile installs a pinned copy.
18
18
  - `runtime.profile` is the canonical infrastructure selector. Generated apps
19
19
  expose deterministic `app:*` operations and keep runtime state outside source.
20
+ - Every lifecycle script is a `smrt app <operation>` one-liner
21
+ (`packages/cli/agents/app-commands.md`); the template ships no `scripts/`.
22
+ Never reintroduce copied operator scripts: fix the CLI command instead.
23
+ - Server composition is `createSmrtSvelteKitRuntime()` from
24
+ `@happyvertical/smrt-app-runtime/sveltekit`, built once in
25
+ `src/lib/server/smrt.ts`, which holds only `runtime` and its options
26
+ (provider readiness via `createProviderReadinessProbe`), so the web process
27
+ never imports the CLI. The runtime takes the local writer lease by default,
28
+ the `smrt()` plugin injects the generated registration, and each generated
29
+ route embeds a prelude that imports `$lib/server/smrt` and resolves
30
+ collections through its exported `runtime.getCollection()` (#3416). A legacy
31
+ `getCollection`/`getSmrtConfig` export would take precedence (deprecated,
32
+ honoured for one release): never re-add one, a register import, guard, or
33
+ manifest hydration.
34
+ Health, diagnostics, layout session, and owner setup routes mount that
35
+ package's handlers.
20
36
  - The production baseline uses adapter-node with separate web, task-worker, and
21
37
  schedule-worker processes. Workers import the build-compiled
22
38
  `.smrt/runtime/register.js` (#3117) before creating runners so app-defined
@@ -37,8 +53,9 @@ It is the ground-up alternative to `smrt-saas-starter`.
37
53
  different or stale server is never accepted as this app. Deployed health
38
54
  responses expose only generic status and profile.
39
55
  - Directly used `@happyvertical/smrt-*` packages share one current release range.
40
- - `@happyvertical/smrt-cli` is a direct dev dependency because scripts/docs use
41
- its binary. The template includes `@happyvertical/smrt-web` because the root
56
+ - `@happyvertical/smrt-cli` is a direct dev dependency because every
57
+ lifecycle script runs its binary; the runtime image keeps dev dependencies so
58
+ workers and operator commands can. The template includes `@happyvertical/smrt-web` because the root
42
59
  Provider wires the generated read-only WebMCP definitions for every page.
43
60
  - The generated MCP server resolves its imports from the scaffolded app, not
44
61
  from the CLI, so every specifier `generate-mcp` emits must be declared here.
@@ -49,9 +66,10 @@ It is the ground-up alternative to `smrt-saas-starter`.
49
66
  for the default worker and tenant surfaces. Do not assume pnpm exposes the
50
67
  CLI's or core's transitive dependencies to the app root — its strict layout
51
68
  does not (#2297).
52
- - `smrtConsumer()` explicitly consumes profiles, tenancy, and users manifests;
53
- `smrtPlugin()` scans `src/lib/objects`, generates Vite virtual definitions,
54
- SvelteKit routes, runtime registration, and knowledge artifacts.
69
+ - `vite.config.ts` is `[sveltekit(), smrt()]`. The preset consumes exactly the
70
+ packages in `smrt.config.ts` `consumer.packages` (profiles, tenancy, users),
71
+ scans `src/lib/objects`, and generates Vite virtual definitions, SvelteKit
72
+ routes, runtime registration, and knowledge artifacts.
55
73
  - `pnpm db:migrate` builds first to refresh generated artifacts, then the
56
74
  migration wrapper holds the shared operation/writer exclusion for the full
57
75
  manifest-driven migration command. Do not restore deprecated `smrt db:setup`
@@ -59,11 +77,11 @@ It is the ground-up alternative to `smrt-saas-starter`.
59
77
 
60
78
  ## Application patterns
61
79
 
62
- - `src/hooks.server.ts` stores a URL-selected tenant candidate separately, then
63
- lets `createSessionHandler({ enterTenantContext: true })` establish the only
64
- authorized tenant context. Never turn an untrusted header into authority.
65
- - `src/lib/server/smrt.ts` imports generated local registrations, loads the
66
- generated manifest metadata, and uses the public users request-scoped DB API.
80
+ - `src/hooks.server.ts` re-exports the runtime's `handle`/`init`: URL tenant
81
+ selection stays a separate candidate and only the verified session
82
+ establishes tenant context. Never turn an untrusted header into authority.
83
+ - App code uses `runtime.getCollection()`/`runtime.classOptions()`, which are
84
+ request-scoped (RLS transaction) and must never be retained.
67
85
  - `Item` is the single example object and demonstrates optional tenant scope,
68
86
  a REST writable allowlist, shared CRUD action metadata, and the explicit
69
87
  collection registration required by generated CLI/MCP runtime commands.
@@ -71,18 +89,27 @@ It is the ground-up alternative to `smrt-saas-starter`.
71
89
  `depends('smrt:items')`, mutations call `invalidate('smrt:items')`, and
72
90
  hand-written writes call `assertOperationPermission()` with the session's
73
91
  exact permission snapshot.
74
- - The root layout uses Provider, the current smrt-ui ThemeProvider, AdminShell,
75
- and a small explicit TenantNav. Generated REST routes do not imply page routes.
76
- - The root Provider registers generated WebMCP read tools with the authenticated
77
- page session. Write/destructive effects require an explicit page-owned policy.
92
+ - The root layout is `AppShell` from `@happyvertical/smrt-svelte/app` with a
93
+ small explicit `nav`; `/setup` and `/settings` mount that package's
94
+ `OwnerSetupForm` and `ShellSettingsPage`. Generated REST routes do not imply
95
+ page routes.
96
+ - AppShell's Provider registers generated WebMCP read tools with the
97
+ authenticated page session. Write/destructive effects require an explicit
98
+ page-owned policy.
78
99
  Live browser collections remain opt-in per page and must seed from SSR
79
100
  `initialData` to avoid a duplicate first request.
80
- - `RuntimeDiagnosticsWebMcp` owns exactly one additional read-only tool,
81
- `smrt.runtime.diagnostics.read`. It uses same-origin page-session fetch to the
82
- authored `/api/_runtime/diagnostics` route and aborts its registration on
83
- unmount. The route requires a direct active tenant membership plus either the
84
- owner role or the explicit `runtime_diagnostics.read` permission before any
85
- runtime projection/probe; it never calls principal-bound server tools.
101
+ - AppShell's `runtimeDiagnostics` (on for signed-in sessions) owns exactly one
102
+ additional read-only tool, `smrt.runtime.diagnostics.read`, which fetches the
103
+ mounted `/api/_runtime/diagnostics` route as the page user. That route
104
+ (`createRuntimeDiagnosticsHandler`) requires a direct active tenant membership
105
+ plus the owner role or `runtime_diagnostics.read` before any projection or
106
+ probe; it never calls principal-bound server tools.
107
+ - The opt-in `mcp-apps-template/` overlay is one `mountMcpAppRoute()` route
108
+ and the protected-resource metadata route, both using
109
+ `createHostedMcpResourceAuth({ profile, runtime })` (local owner tokens,
110
+ hosted membership-backed principals); `pnpm typecheck` checks it against
111
+ the template. `__tests__/localMcpToken.test.ts` proves mint → real
112
+ `smrt-mcp-bridge` stdio → route against the real runtime.
86
113
 
87
114
  ## Tests
88
115
 
@@ -126,11 +153,13 @@ own. Never add a mocked REST handler, an in-memory database, or DOM automation
126
153
  presented as WebMCP execution.
127
154
 
128
155
  - `@happyvertical/smrt-cli` is a devDependency **because the gate needs it**:
129
- the app's `app:setup` migration step shells out to `pnpm exec smrt db:migrate`,
130
- and the copied app deliberately never runs `pnpm install`, so `smrt` has to
131
- arrive through the linked `node_modules/.bin`. The harness pins the app's own
132
- `.bin` ahead of `PATH` and fails with a named error if that binary is missing,
133
- so a host-global `smrt` cannot make a local run pass where CI fails.
156
+ the harness runs the app's own `smrt app setup` and `smrt app dev` from the
157
+ linked CLI, and setup's `pnpm build` is `smrt app build`. The copied app
158
+ deliberately never runs `pnpm install`, so `smrt` has to arrive through the
159
+ linked `node_modules`. The harness pins the app's own `.bin` ahead of `PATH`
160
+ and fails with a named error if the binary is missing, so a host-global
161
+ `smrt` cannot make a local run pass where CI fails. Identity and state-root
162
+ values it checks come from `@happyvertical/smrt-app-runtime`.
134
163
  - `pnpm test:e2e` runs the browser half.
135
164
  - `pnpm test:m5` runs the whole aggregate gate through
136
165
  `e2e/support/gate.mjs`, which requires a PostgreSQL service and fails when a
@@ -145,8 +174,12 @@ presented as WebMCP execution.
145
174
  from `testInfo.project.outputDir` and asserts it is outside the repository.
146
175
  The harness gives the copied app a real `node_modules` directory of
147
176
  individually symlinked entries rather than one symlink to this package's, so
148
- the served app's `node_modules/.vite` cache cannot land in the checkout —
149
- never replace that loop with a whole-directory symlink.
177
+ the served app's `node_modules/.vite` cache cannot land in the checkout.
178
+ Scope directories (`@happyvertical/`) and `.bin/` are real directories of
179
+ per-entry links too: the copy ships `pnpm-workspace.yaml` and setup runs
180
+ `pnpm build` in it, so anything pnpm relinks must land in the copy, never in
181
+ this package's `node_modules`. Never replace either loop with a
182
+ whole-directory symlink.
150
183
  - `e2e/` is outside `biome.json`'s include globs, as every package's `e2e/` is,
151
184
  so a green `Lint` job says nothing about this tree. `pnpm typecheck` covers
152
185
  the `.ts` files (`tsconfig.fixture.json` includes `e2e/**/*.ts`) but NOT
package/README.md CHANGED
@@ -52,17 +52,21 @@ fixtures.
52
52
 
53
53
  ## 2. Understand the generated files
54
54
 
55
- The checked-in template contains authored objects, hooks, server helpers,
56
- pages, and configuration. Vite generates `.smrt/manifest.json`,
55
+ The checked-in template contains authored objects, one server module
56
+ (`src/lib/server/smrt.ts`, the runtime and its options), thin hooks and
57
+ routes that mount package handlers, pages, and configuration. It ships no
58
+ operator scripts. Vite generates `.smrt/manifest.json`,
57
59
  `.smrt/smrt-knowledge.json`, `.smrt/register.js`,
58
60
  `src/lib/server/smrt-register.ts`, `src/lib/types/smrt-generated/`, and
59
61
  `src/routes/api/**/+server.ts`. All are ignored and excluded from scaffold
60
62
  fixtures.
61
63
 
62
- `smrtPlugin()` scans the local object directory and generates local routes and
63
- types. `smrtConsumer()` explicitly consumes the profiles, tenancy, and users
64
- package manifests. `@happyvertical/smrt-cli` is a direct dev dependency because
65
- the generated package scripts and README invoke its `smrt` binary.
64
+ `vite.config.ts` is `plugins: [sveltekit(), smrt()]`: the
65
+ `@happyvertical/smrt-core/vite` preset applies the decorator transform, scans
66
+ the local object directory, generates local routes and types, and consumes the
67
+ packages `smrt.config.ts` lists under `consumer.packages` (profiles, tenancy,
68
+ users). `@happyvertical/smrt-cli` is a direct dev dependency because every
69
+ lifecycle script is `smrt app <operation>`.
66
70
 
67
71
  ## 3. Define the first object
68
72
 
@@ -85,25 +89,28 @@ pnpm db:migrate
85
89
  ```
86
90
 
87
91
  The script builds first to refresh manifests, registrations, types, and API
88
- routes, then runs `smrt db:migrate`. The deprecated `smrt db:setup` command is
92
+ routes, then runs `smrt app migrate`, which holds the operation lock and runs
93
+ `smrt db:migrate`. The deprecated `smrt db:setup` command is
89
94
  not documented or shipped.
90
95
 
91
96
  ## 5. Understand tenant context
92
97
 
93
- The template resolves a URL tenant candidate into separate
94
- `selectedTenantId`/`selectedTenantSlug` locals. It does not establish query
95
- context from that selection. The session hook runs afterward and only its
96
- membership-authorized tenant enters the s-m-r-t tenancy context.
98
+ `src/hooks.server.ts` mounts `createSmrtSvelteKitRuntime()` from
99
+ `@happyvertical/smrt-app-runtime/sveltekit`. It resolves a URL tenant candidate
100
+ into separate `selectedTenantId`/`selectedTenantSlug` locals and does not
101
+ establish query context from that selection. The session step runs afterward
102
+ and only its membership-authorized tenant enters the s-m-r-t tenancy context.
97
103
 
98
- Untrusted tenant headers are ignored. Applications may replace
99
- `src/lib/server/tenancy.ts` with a path, signed-cookie, or trusted-gateway
100
- selector, but selection must remain separate from authorization and session
101
- switches must use `switchSessionTenant()`.
104
+ Untrusted tenant headers are ignored. Applications may pass `selectTenant` for
105
+ a path, signed-cookie, or trusted-gateway selector, but selection must remain
106
+ separate from authorization and session switches must use
107
+ `switchSessionTenant()`.
102
108
 
103
109
  ## 6. Understand users, profiles, memberships, roles, and permissions
104
110
 
105
111
  The generated project consumes the current profiles and users manifests and
106
- wires `createSessionHandler({ enterTenantContext: true })`. A User is the auth
112
+ loads the signed session through the runtime, which enters tenant context only
113
+ for a verified active membership. A User is the auth
107
114
  identity, Profile is person-facing metadata, Membership joins a User to a
108
115
  Tenant and Role, Role maps to Permission records, and Session publishes the
109
116
  resolved permission snapshot.
@@ -1,4 +1,4 @@
1
1
  {
2
- "@happyvertical/smrt-app-mcp": "^0.53.3",
3
- "@happyvertical/smrt-mcp-openai": "^0.53.3"
2
+ "@happyvertical/smrt-app-mcp": "^0.54.1",
3
+ "@happyvertical/smrt-mcp-openai": "^0.54.1"
4
4
  }
@@ -1,7 +1,11 @@
1
- import { hostedMcpAuth } from '$lib/server/mcp-hosted';
1
+ import { createHostedMcpResourceAuth } from '@happyvertical/smrt-app-mcp/auth';
2
+ import { mountMcpProtectedResourceMetadataRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
2
3
 
3
- /** Serve exactly the RFC 9728 metadata URL advertised by hosted bearer challenges. */
4
- export function GET() {
5
- const auth = hostedMcpAuth();
6
- return auth?.metadataResponse() ?? new Response(null, { status: 404 });
7
- }
4
+ import { runtime } from '$lib/server/smrt';
5
+
6
+ const { profile } = await runtime.resolvedRuntime();
7
+
8
+ /** RFC 9728 metadata at exactly the URL hosted bearer challenges advertise (404 locally). */
9
+ export const GET = mountMcpProtectedResourceMetadataRoute(
10
+ createHostedMcpResourceAuth({ profile, runtime }),
11
+ );
@@ -1,15 +1,71 @@
1
- import { mountMcpRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
1
+ import type { McpWorkflowToolDefinition } from '@happyvertical/smrt-app-mcp';
2
+ import { createHostedMcpResourceAuth } from '@happyvertical/smrt-app-mcp/auth';
3
+ import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
4
+ import { openAiDisplayMetadata, withOpenAiEntrypoints } from '@happyvertical/smrt-mcp-openai';
2
5
 
3
- import { mcpServer, resolveMcpPrincipal } from '$lib/server/mcp';
4
- import { hostedMcpAuth } from '$lib/server/mcp-hosted';
6
+ import { Item } from '$lib/objects/Item';
7
+ import { runtime } from '$lib/server/smrt';
5
8
 
6
- /** Stateless SDK-v2 Streamable HTTP MCP endpoint. */
7
- const localRoute = mountMcpRoute(mcpServer, { resolvePrincipal: resolveMcpPrincipal });
9
+ const ITEM_RESOURCE_URI = 'ui://smrt-app/v1/items.html';
10
+ const itemResourceHtml = `<!doctype html><html lang="en"><head><title>Items</title></head><body><main tabindex="-1"><h1>Items</h1><p>Use the host controls to review your authorized items.</p></main></body></html>`;
8
11
 
9
- export async function POST(event: Parameters<typeof localRoute>[0]) {
10
- const auth = hostedMcpAuth();
11
- if (!auth) return localRoute(event);
12
- const checked = await auth.authenticate(event.request);
13
- if (!checked.ok) return checked.response;
14
- return mountMcpRoute(mcpServer, { resolvePrincipal: () => checked.principal })(event);
15
- }
12
+ const itemsOverview: McpWorkflowToolDefinition = withOpenAiEntrypoints(
13
+ {
14
+ name: 'items_overview',
15
+ description: 'Open the authorized item overview.',
16
+ title: 'Items',
17
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
18
+ outputSchema: {
19
+ type: 'object',
20
+ properties: { state: { type: 'string' } },
21
+ required: ['state'],
22
+ additionalProperties: false,
23
+ },
24
+ effect: 'read',
25
+ idempotent: true,
26
+ openWorld: false,
27
+ ui: { resourceUri: ITEM_RESOURCE_URI },
28
+ async execute() {
29
+ return {
30
+ content: [{ type: 'text', text: 'Item overview is ready.' }],
31
+ structuredContent: { state: 'ready' },
32
+ };
33
+ },
34
+ },
35
+ ['global'],
36
+ );
37
+
38
+ const { profile } = await runtime.resolvedRuntime();
39
+
40
+ /**
41
+ * Stateless SDK-v2 Streamable HTTP MCP endpoint for this app's own `Item`.
42
+ * Locally the principal is the signed session (user, authorized tenant, and
43
+ * permission slugs), or an owner-minted bearer token from `smrt app token`
44
+ * for a local MCP client such as the `smrt-mcp-bridge` stdio bridge. Hosted
45
+ * profiles verify a bearer access token and map it through the runtime's
46
+ * membership-backed `resolveMcpPrincipal` (pass `resolvePrincipal` to
47
+ * override it). A bearer principal is bound into the request permission
48
+ * context (under `database-rls`, its own RLS transaction) with its scopes
49
+ * capped to live permissions. Every principal needs `items.read`, so only
50
+ * read-only tools are published until per-operation authorization exists;
51
+ * browser requests from a foreign `Origin` are refused.
52
+ */
53
+ export const POST = mountMcpAppRoute({
54
+ models: [Item],
55
+ requiredScopes: ['items.read'],
56
+ effects: ['read'],
57
+ smrtOptions: () => ({ db: runtime.databaseConfig() }),
58
+ auth: createHostedMcpResourceAuth({ profile, runtime }),
59
+ bindPrincipal: runtime.runAsPrincipal,
60
+ workflowTools: [itemsOverview],
61
+ resources: [
62
+ {
63
+ uri: ITEM_RESOURCE_URI,
64
+ version: 'v1',
65
+ name: 'Items',
66
+ html: itemResourceHtml,
67
+ csp: {},
68
+ metadata: openAiDisplayMetadata({ availableDisplayModes: ['inline'] }),
69
+ },
70
+ ],
71
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-template-sveltekit",
3
- "version": "0.53.3",
3
+ "version": "0.54.1",
4
4
  "description": "Minimal SvelteKit project template with s-m-r-t framework integration",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -32,23 +32,27 @@
32
32
  "directory": "packages/template-sveltekit"
33
33
  },
34
34
  "peerDependencies": {
35
- "@happyvertical/smrt-core": "0.53.3"
35
+ "@happyvertical/smrt-core": "0.54.1"
36
36
  },
37
37
  "devDependencies": {
38
- "@happyvertical/smrt-app-runtime": "0.53.3",
39
- "@happyvertical/smrt-assets": "0.53.3",
40
- "@happyvertical/smrt-cli": "0.53.3",
41
- "@happyvertical/smrt-config": "0.53.3",
42
- "@happyvertical/smrt-jobs": "0.53.3",
43
- "@happyvertical/smrt-playbooks": "0.53.3",
44
- "@happyvertical/smrt-profiles": "0.53.3",
45
- "@happyvertical/smrt-scanner": "0.53.3",
46
- "@happyvertical/smrt-svelte": "0.53.3",
47
- "@happyvertical/smrt-tenancy": "0.53.3",
48
- "@happyvertical/smrt-ui": "0.53.3",
49
- "@happyvertical/smrt-users": "0.53.3",
50
- "@happyvertical/smrt-web": "0.53.3",
38
+ "@happyvertical/smrt-app-cli": "0.54.1",
39
+ "@happyvertical/smrt-app-mcp": "0.54.1",
40
+ "@happyvertical/smrt-app-runtime": "0.54.1",
41
+ "@happyvertical/smrt-assets": "0.54.1",
42
+ "@happyvertical/smrt-cli": "0.54.1",
43
+ "@happyvertical/smrt-config": "0.54.1",
44
+ "@happyvertical/smrt-jobs": "0.54.1",
45
+ "@happyvertical/smrt-mcp-openai": "0.54.1",
46
+ "@happyvertical/smrt-playbooks": "0.54.1",
47
+ "@happyvertical/smrt-profiles": "0.54.1",
48
+ "@happyvertical/smrt-scanner": "0.54.1",
49
+ "@happyvertical/smrt-svelte": "0.54.1",
50
+ "@happyvertical/smrt-tenancy": "0.54.1",
51
+ "@happyvertical/smrt-ui": "0.54.1",
52
+ "@happyvertical/smrt-users": "0.54.1",
53
+ "@happyvertical/smrt-web": "0.54.1",
51
54
  "@happyvertical/sql": "^0.89.12",
55
+ "@modelcontextprotocol/client": "2.0.0",
52
56
  "@modelcontextprotocol/server": "^2.0.0",
53
57
  "@playwright/test": "^1.63.0",
54
58
  "@sveltejs/adapter-node": "^5.5.7",
@@ -18,7 +18,20 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
18
18
  return only the schema-versioned allowlist; never expose private runtime
19
19
  objects, identity fields, paths, URLs, secrets, PII, raw errors, or logs.
20
20
  - Never improvise setup internals. Run `pnpm app:install`; use individual
21
- `app:*` commands only for recovery or explicit operator control.
21
+ `app:*` commands only for recovery or explicit operator control. They call
22
+ `smrt app <operation>` from `@happyvertical/smrt-cli`; do not copy operator
23
+ scripts into this app.
24
+ - Server composition (profile, storage custody, owner bootstrap, tenant
25
+ selection, session, health, diagnostics) comes from
26
+ `@happyvertical/smrt-app-runtime/sveltekit`, configured once in
27
+ `src/lib/server/smrt.ts`, which holds only `runtime` and its options. Pass
28
+ app-specific behaviour as options (`selectTenant`, `classOverrides`,
29
+ `session`, `prepareDatabase`); do not reimplement hooks, re-add the
30
+ `smrt-register` import (the `smrt()` plugin injects it), or export
31
+ `getCollection` (generated routes import this module and use its
32
+ `runtime.getCollection()`; a legacy export would win, deprecated).
33
+ `runtime.getCollection()`/`runtime.classOptions()` are request-scoped: never
34
+ keep their result beyond the request.
22
35
  - Recover an interrupted unclaimed owner invitation with `pnpm app:stop`, then
23
36
  `pnpm app:recover`, `pnpm app:start`, and `pnpm app:open`; never print or copy
24
37
  its token.
@@ -48,8 +61,9 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
48
61
  - Keep object relationship metadata close to `@smrt()`.
49
62
  - Add human-facing routes explicitly; generated REST routes do not create nav
50
63
  pages.
51
- - The root layout includes `@happyvertical/smrt-web` and registers generated
52
- WebMCP read tools through Provider when the browser exposes `modelContext`.
64
+ - The root layout is `AppShell` from `@happyvertical/smrt-svelte/app`; it
65
+ includes `@happyvertical/smrt-web` and registers generated WebMCP read tools
66
+ through its Provider when the browser exposes `modelContext`.
53
67
  Keep the guard in place for SSR and seed live collections with SSR
54
68
  `initialData`.
55
69
  - Do not enable knowledge HTTP routes in production without explicit admin auth.
@@ -57,8 +71,9 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
57
71
  imports `.smrt/runtime/register.js` (compiled by `pnpm build`) before any
58
72
  runner starts, so jobs can target this app's own `@smrt()` objects; keep that
59
73
  import ahead of runner creation and keep domain objects free of SvelteKit
60
- virtual modules (`$env/*`, `$app/*`), which a worker cannot load. Extend the
61
- portability adapter instead of converting database files. Deployed provider
74
+ virtual modules (`$env/*`, `$app/*`), which a worker cannot load. Add a
75
+ `scripts/smrt-portability.mjs` adapter (it overrides the CLI's built-in one)
76
+ instead of converting database files. Deployed provider
62
77
  readiness must come from installed modules that probe the real backing
63
78
  services; environment booleans are not readiness evidence.
64
79
  - Every supported local web entry point (`app:start` or `pnpm dev`) holds the
@@ -17,7 +17,6 @@ ENV NODE_ENV=production
17
17
  ENV SMRT_RUNTIME_PROFILE=self-hosted
18
18
  COPY --from=build /app/build ./build
19
19
  COPY --from=build /app/.smrt ./.smrt
20
- COPY --from=build /app/scripts ./scripts
21
20
  COPY --from=build /app/smrt.config.ts ./smrt.config.ts
22
21
  RUN chown -R node:node /app
23
22
  USER node
@@ -7,5 +7,5 @@
7
7
  > `pnpm app:doctor`, show me its secret-free recovery findings, and ask before
8
8
  > changing provider configuration.
9
9
 
10
- The prompt is intentionally thin. `scripts/smrt-app.mjs` owns operations and
11
- recovery.
10
+ The prompt is intentionally thin. `smrt app` (from `@happyvertical/smrt-cli`)
11
+ owns operations and recovery.