@vendoai/vendo 0.4.2 → 0.4.4

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 (55) hide show
  1. package/dist/capability-misses.js +2 -3
  2. package/dist/cli/cloud/client.d.ts +2 -5
  3. package/dist/cli/cloud/client.js +4 -4
  4. package/dist/cli/cloud/device-login.d.ts +11 -0
  5. package/dist/cli/cloud/device-login.js +52 -1
  6. package/dist/cli/dep-versions.d.ts +1 -1
  7. package/dist/cli/dep-versions.js +1 -1
  8. package/dist/cli/doctor-codes.d.ts +7 -0
  9. package/dist/cli/doctor-codes.js +7 -0
  10. package/dist/cli/doctor-live.js +11 -1
  11. package/dist/cli/doctor.js +122 -11
  12. package/dist/cli/extract/apply.js +1 -1
  13. package/dist/cli/extract/delegate.js +9 -1
  14. package/dist/cli/extract/extraction.d.ts +4 -0
  15. package/dist/cli/extract/extraction.js +29 -2
  16. package/dist/cli/extract/harness.d.ts +12 -0
  17. package/dist/cli/extract/harness.js +3 -0
  18. package/dist/cli/extract/stages.js +5 -1
  19. package/dist/cli/framework.d.ts +17 -2
  20. package/dist/cli/framework.js +32 -5
  21. package/dist/cli/init-scaffolds.d.ts +32 -1
  22. package/dist/cli/init-scaffolds.js +153 -9
  23. package/dist/cli/init.d.ts +14 -4
  24. package/dist/cli/init.js +212 -43
  25. package/dist/cli/playground/app/fake-client.js +11 -5
  26. package/dist/cli/playground/app/main.js +23 -13
  27. package/dist/cli/playground/app/scenario-mount.js +4 -1
  28. package/dist/cli/playground/app/theme-editor.js +27 -27
  29. package/dist/cli/playground/bundle.gen.d.ts +1 -1
  30. package/dist/cli/playground/bundle.gen.js +1 -1
  31. package/dist/cli/playground/embed-bundle.gen.d.ts +1 -1
  32. package/dist/cli/playground/embed-bundle.gen.js +1 -1
  33. package/dist/cli/shared.d.ts +1 -1
  34. package/dist/cli/shared.js +1 -1
  35. package/dist/cli/sync.d.ts +1 -1
  36. package/dist/cli/sync.js +1 -1
  37. package/dist/cloud-apps.js +2 -1
  38. package/dist/cloud-key-fetch.d.ts +16 -0
  39. package/dist/cloud-key-fetch.js +40 -0
  40. package/dist/cloud-tools.js +2 -1
  41. package/dist/connections.js +2 -2
  42. package/dist/dev-creds/model-edge.d.ts +21 -0
  43. package/dist/dev-creds/model-edge.js +24 -0
  44. package/dist/hosted-store.js +2 -1
  45. package/dist/mastra.js +71 -2
  46. package/dist/react.d.ts +1 -0
  47. package/dist/react.js +7 -0
  48. package/dist/refine.js +2 -1
  49. package/dist/sandbox.js +2 -2
  50. package/dist/server.d.ts +1 -1
  51. package/dist/server.js +56 -22
  52. package/dist/wire/apps.js +30 -1
  53. package/dist/wire/shared.d.ts +2 -2
  54. package/dist/wire/shared.js +1 -1
  55. package/package.json +20 -11
@@ -115,10 +115,14 @@ export function composeInstructions(tools, appName, surface) {
115
115
  "",
116
116
  "Rules:",
117
117
  "- Reply with ONLY one fenced json block matching:",
118
- ' { "tools": [{ "name", "description", "risk"?, "critical"?, "disabled"?, "reasoning"? }], "missedSurfaces"?: string[] }',
118
+ ' { "tools": [{ "name", "description", "risk"?, "critical"?, "disabled"?, "audience"?, "reasoning"? }], "missedSurfaces"?: string[] }',
119
119
  "- tools: include ONLY names from the list above. Rewrite each description so an agent choosing tools understands what it actually does (read the handler source). <= 200 chars each.",
120
120
  "- risk: you may RAISE risk (read->write->destructive) when the handler is more dangerous than labeled; never lower it. Mark irreversible operations critical: true.",
121
121
  "- A tool listed as disabled was statically unclassifiable. If you can read its handler and grade it, set disabled: false WITH a risk and one-line reasoning. Leave it out otherwise.",
122
+ "- audience: who the handler's own auth admits — \"end-user\" (a signed-in customer acting on their own data),",
123
+ " \"operator\" (admin/staff/support consoles), or \"internal\" (machine-to-machine: webhooks, cron, reconciliation,",
124
+ " service tokens). Read the auth checks, not the route name. When unsure, default to internal — non-end-user",
125
+ " tools are excluded from the embedded agent by default, and a wrong \"end-user\" grade exposes a privileged surface.",
122
126
  "- missedSurfaces: API surfaces you found that the list is missing (path + one line). Do not invent tools for them.",
123
127
  ].join("\n");
124
128
  }
@@ -2,7 +2,22 @@ export type HostFramework = "next" | "express" | "unknown";
2
2
  export interface VendoWiring {
3
3
  server: boolean;
4
4
  client: boolean;
5
+ /** A VISIBLE agent surface is mounted — <VendoRoot> alone is a context
6
+ provider that renders nothing (0.4.1 E2E cert B3: by-the-book installs
7
+ ended doctor-green with nothing on screen). */
8
+ surface: boolean;
5
9
  }
10
+ /** What counts as a visible surface: the shipped chrome (<VendoOverlay> and
11
+ the pieces it is built from), the BYO embeds a host chat renders, and the
12
+ hooks a host uses to drive a custom surface. Deliberately generous — this
13
+ list gates a doctor FAILURE, so a host with any plausible surface of its
14
+ own must pass. */
15
+ export declare const SURFACE_MARKERS: readonly string[];
6
16
  export declare function detectFramework(root: string): Promise<HostFramework>;
7
- /** Bounded source scan shared by init and doctor so their wiring verdicts agree. */
8
- export declare function detectVendoWiring(root: string): Promise<VendoWiring>;
17
+ /** Bounded source scan shared by init and doctor so their wiring verdicts
18
+ agree. `exclude` skips generated files whose own markers would count as
19
+ host wiring (init's layout decision excludes the vendo-root wrapper: its
20
+ <VendoOverlay /> is only real once a layout mounts the wrapper itself). */
21
+ export declare function detectVendoWiring(root: string, options?: {
22
+ exclude?: string[];
23
+ }): Promise<VendoWiring>;
@@ -1,6 +1,24 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { walk } from "./theme/walk.js";
4
+ /** What counts as a visible surface: the shipped chrome (<VendoOverlay> and
5
+ the pieces it is built from), the BYO embeds a host chat renders, and the
6
+ hooks a host uses to drive a custom surface. Deliberately generous — this
7
+ list gates a doctor FAILURE, so a host with any plausible surface of its
8
+ own must pass. */
9
+ export const SURFACE_MARKERS = [
10
+ "<VendoOverlay",
11
+ "<VendoThread",
12
+ "<VendoTrigger",
13
+ "<VendoPalette",
14
+ "<VendoSlot",
15
+ "<VendoAppEmbed",
16
+ "<VendoApprovalEmbed",
17
+ "<VendoToolResult",
18
+ "useVendoOverlay(",
19
+ "useVendoThread(",
20
+ "useSlotApp(",
21
+ ];
4
22
  const SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/;
5
23
  const SOURCE_SCAN_MAX_FILES = 2_000;
6
24
  export async function detectFramework(root) {
@@ -17,20 +35,29 @@ export async function detectFramework(root) {
17
35
  return "unknown";
18
36
  }
19
37
  }
20
- /** Bounded source scan shared by init and doctor so their wiring verdicts agree. */
21
- export async function detectVendoWiring(root) {
38
+ /** Bounded source scan shared by init and doctor so their wiring verdicts
39
+ agree. `exclude` skips generated files whose own markers would count as
40
+ host wiring (init's layout decision excludes the vendo-root wrapper: its
41
+ <VendoOverlay /> is only real once a layout mounts the wrapper itself). */
42
+ export async function detectVendoWiring(root, options = {}) {
22
43
  let server = false;
23
44
  let client = false;
45
+ let surface = false;
46
+ const excluded = new Set(options.exclude ?? []);
24
47
  const files = await walk(root, (relativePath) => SOURCE_FILE.test(relativePath), SOURCE_SCAN_MAX_FILES);
25
48
  for (const file of files) {
49
+ if (excluded.has(file))
50
+ continue;
26
51
  const source = await readFile(file, "utf8").catch(() => "");
27
52
  const code = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
28
53
  if (code.includes("@vendoai/vendo/server") && /\bcreateVendo\s*\(/.test(code))
29
54
  server = true;
30
- if (code.includes("<VendoRoot"))
55
+ if (code.includes("<VendoRoot") || code.includes("<VendoProvider"))
31
56
  client = true;
32
- if (server && client)
57
+ if (SURFACE_MARKERS.some((marker) => code.includes(marker)))
58
+ surface = true;
59
+ if (server && client && surface)
33
60
  break;
34
61
  }
35
- return { server, client };
62
+ return { server, client, surface };
36
63
  }
@@ -1,4 +1,4 @@
1
- import { type ServerActionRegistration } from "@vendoai/actions";
1
+ import { type ServerActionRegistration } from "@vendoai/actions/sync";
2
2
  import { type AuthMatch } from "./init-auth.js";
3
3
  /** The wired preset line plus its escape-hatch comment. The lead-in stays
4
4
  honest about how the preset got here: detection cites the found
@@ -8,6 +8,28 @@ export declare function authConfigLines(auth: AuthMatch): string;
8
8
  as `catalog` (data fields only), `<VendoRoot components={registry}>` reads
9
9
  the component references. Generated only while absent — never clobbered. */
10
10
  export declare function registrySource(variant: "tsx" | "mjs"): string;
11
+ /** The anonymous-composition principal line (no auth preset wired). The
12
+ subject matches the demo principal both existing-agents quickstarts set in
13
+ their chat routes — the wire route MUST resolve the same subject as the
14
+ host's agent loop, or every app/approval created in chat is invisible to
15
+ the embeds, which call this route directly (0.4.1 E2E cert blocker B4:
16
+ a `() => null` wire against a demo-user chat route rendered an infinite
17
+ skeleton). Replaced wholesale when an auth preset is wired. */
18
+ export declare function anonymousPrincipalLines(): string;
19
+ /**
20
+ * The client mount wrapper (visible-surface fix, 0.4.1 E2E cert B3/M3): one
21
+ * generated "use client" boundary owns the registry + theme imports and
22
+ * mounts `<VendoOverlay />` — the launcher pill + panel end users actually
23
+ * see. The registry holds component references, so importing it in a Server
24
+ * Component layout and passing it into the client provider fails RSC
25
+ * serialization ("Only plain objects can be passed to Client Components");
26
+ * this wrapper is the layout-safe mount. Generated only while absent.
27
+ */
28
+ export declare function vendoRootWrapperSource(options: {
29
+ /** Posix-style import specifier from the wrapper's dir to .vendo/theme.json,
30
+ or null when resolveJsonModule is explicitly disabled. */
31
+ themeSpecifier: string | null;
32
+ }): string;
11
33
  export declare function routeSource(options: {
12
34
  serverActions: boolean;
13
35
  auth: AuthMatch | null;
@@ -24,5 +46,14 @@ export declare function wiringServerActions(root: string): Promise<ServerActionR
24
46
  * sorted registrations, stable aliases — so re-init stays idempotent.
25
47
  */
26
48
  export declare function serverActionsModuleSource(root: string, wiringDir: string, registrations: ServerActionRegistration[]): string;
49
+ /** The runtime-neutral composition (`--framework custom`): plain Request →
50
+ * Response with env passed per call, so ONE generated module serves any
51
+ * Web-standard host — Cloudflare Workers, Bun, Deno, Hono, Lambda adapters.
52
+ * Construction is lazy (first request): the safe shape everywhere and the
53
+ * only legal one at Workers module scope. With a Vendo Cloud key the four
54
+ * infrastructure seams wire the Cloud adapters explicitly per the adapter
55
+ * rule (reference shape: the vendo-on-Workers field integration,
56
+ * 2026-07-21). */
57
+ export declare function customServerSource(typescript: boolean, auth?: AuthMatch | null): string;
27
58
  export declare function expressServerSource(typescript: boolean, auth?: AuthMatch | null): string;
28
59
  export declare const VENDO_ENV_EXAMPLE: string;
@@ -1,5 +1,5 @@
1
1
  import { join, relative, sep } from "node:path";
2
- import { extractServerActions, serverActionRegistrations, } from "@vendoai/actions";
2
+ import { extractServerActions, serverActionRegistrations, } from "@vendoai/actions/sync";
3
3
  import { AUTH_FAMILY_INFO, AUTH_PRESET_SPECIFIER } from "./init-auth.js";
4
4
  /** The wired preset line plus its escape-hatch comment. The lead-in stays
5
5
  honest about how the preset got here: detection cites the found
@@ -48,6 +48,56 @@ export function registrySource(variant) {
48
48
  ? `${header}import type { ComponentRegistry } from "@vendoai/vendo";\n\nexport const registry = {} satisfies ComponentRegistry;\n`
49
49
  : `${header}export const registry = {};\n`;
50
50
  }
51
+ /** The anonymous-composition principal line (no auth preset wired). The
52
+ subject matches the demo principal both existing-agents quickstarts set in
53
+ their chat routes — the wire route MUST resolve the same subject as the
54
+ host's agent loop, or every app/approval created in chat is invisible to
55
+ the embeds, which call this route directly (0.4.1 E2E cert blocker B4:
56
+ a `() => null` wire against a demo-user chat route rendered an infinite
57
+ skeleton). Replaced wholesale when an auth preset is wired. */
58
+ export function anonymousPrincipalLines() {
59
+ return ` // Who the wire's callers act as. This must resolve the SAME subject your\n` +
60
+ ` // agent loop uses (the docs' chat routes set this demo principal), or apps\n` +
61
+ ` // and approvals created in chat are invisible to the embeds, which call\n` +
62
+ ` // this route directly. Replace both sides with your real session lookup.\n` +
63
+ ` principal: async () => ({ kind: "user" as const, subject: "demo-user" }),\n`;
64
+ }
65
+ /**
66
+ * The client mount wrapper (visible-surface fix, 0.4.1 E2E cert B3/M3): one
67
+ * generated "use client" boundary owns the registry + theme imports and
68
+ * mounts `<VendoOverlay />` — the launcher pill + panel end users actually
69
+ * see. The registry holds component references, so importing it in a Server
70
+ * Component layout and passing it into the client provider fails RSC
71
+ * serialization ("Only plain objects can be passed to Client Components");
72
+ * this wrapper is the layout-safe mount. Generated only while absent.
73
+ */
74
+ export function vendoRootWrapperSource(options) {
75
+ const withTheme = options.themeSpecifier !== null;
76
+ return `"use client";\n\n` +
77
+ `/**\n` +
78
+ ` * The Vendo client mount — generated by \`vendo init\`, then yours.\n` +
79
+ ` * This "use client" boundary owns the registry${withTheme ? " and theme" : ""} imports: the\n` +
80
+ ` * registry carries component references, which cannot cross a Server\n` +
81
+ ` * Component boundary as props (RSC serialization). <VendoOverlay /> is the\n` +
82
+ ` * visible surface — the launcher pill + panel. Swap it for your own\n` +
83
+ ` * surface (<VendoThread />, the BYO embeds) whenever you like.\n` +
84
+ ` */\n` +
85
+ `import { VendoOverlay, VendoRoot as VendoClientRoot } from "@vendoai/vendo/react";\n` +
86
+ `import type { ReactNode } from "react";\n` +
87
+ `import { registry } from "./registry";\n` +
88
+ (withTheme
89
+ ? `import theme from ${JSON.stringify(options.themeSpecifier)};\n` +
90
+ `import type { VendoTheme } from "@vendoai/vendo";\n`
91
+ : "") +
92
+ `\nexport function VendoRoot({ children }: { children: ReactNode }) {\n` +
93
+ ` return (\n` +
94
+ ` <VendoClientRoot components={registry}${withTheme ? " theme={theme as VendoTheme}" : ""}>\n` +
95
+ ` {children}\n` +
96
+ ` <VendoOverlay />\n` +
97
+ ` </VendoClientRoot>\n` +
98
+ ` );\n` +
99
+ `}\n`;
100
+ }
51
101
  /** The preset's own import line (its own subpath, never "@vendoai/vendo/server"
52
102
  — corpus-triage Task 9: a shared barrel meant any host importing the
53
103
  server entry statically re-resolved every preset's optional peer dep,
@@ -61,7 +111,7 @@ export function routeSource(options) {
61
111
  (options.serverActions ? `import { serverActions } from "./vendo-actions";\n` : "") +
62
112
  `import { registry } from ${JSON.stringify(options.registrySpecifier)};\n` +
63
113
  `\nconst vendo = createVendo({\n` +
64
- (options.auth === null ? ` principal: async () => null,\n` : authConfigLines(options.auth)) +
114
+ (options.auth === null ? anonymousPrincipalLines() : authConfigLines(options.auth)) +
65
115
  ` catalog: registry,\n` +
66
116
  (options.serverActions ? ` serverActions,\n` : "") +
67
117
  ` policy: {}, // .vendo/policy.json: destructive asks, reads run\n` +
@@ -111,6 +161,98 @@ export function serverActionsModuleSource(root, wiringDir, registrations) {
111
161
  return `${header}${imports.join("\n")}\n\n` +
112
162
  `export const serverActions = {\n${entries.join("\n")}\n};\n`;
113
163
  }
164
+ /** The runtime-neutral composition (`--framework custom`): plain Request →
165
+ * Response with env passed per call, so ONE generated module serves any
166
+ * Web-standard host — Cloudflare Workers, Bun, Deno, Hono, Lambda adapters.
167
+ * Construction is lazy (first request): the safe shape everywhere and the
168
+ * only legal one at Workers module scope. With a Vendo Cloud key the four
169
+ * infrastructure seams wire the Cloud adapters explicitly per the adapter
170
+ * rule (reference shape: the vendo-on-Workers field integration,
171
+ * 2026-07-21). */
172
+ export function customServerSource(typescript, auth = null) {
173
+ const registrySpecifier = typescript ? "./registry" : "./registry.mjs";
174
+ const envType = typescript
175
+ ? `\nexport interface VendoEnv {\n` +
176
+ ` VENDO_API_KEY?: string;\n` +
177
+ ` VENDO_CLOUD_URL?: string;\n` +
178
+ ` VENDO_BASE_URL?: string;\n` +
179
+ `}\n`
180
+ : "";
181
+ const signatures = typescript
182
+ ? {
183
+ vendoVar: `let vendo: ReturnType<typeof createVendo> | null = null;`,
184
+ getVendo: `(env: VendoEnv = {})`,
185
+ handle: `(request: Request, env: VendoEnv = {}): Promise<Response>`,
186
+ }
187
+ : {
188
+ vendoVar: `let vendo = null;`,
189
+ getVendo: `(env = {})`,
190
+ handle: `(request, env = {})`,
191
+ };
192
+ const clientHint = typescript
193
+ ? ` * // in the client entry — theme.json adopts the host brand (08 §4);\n` +
194
+ ` * // the cast narrows TypeScript's widened JSON-module string literals;\n` +
195
+ ` * // <VendoOverlay /> is the visible surface (launcher pill + panel):\n` +
196
+ ` * import { VendoOverlay, VendoRoot } from "@vendoai/vendo/react";\n` +
197
+ ` * import { registry } from "<path-to>/vendo/registry";\n` +
198
+ ` * import theme from "<path-to>/.vendo/theme.json";\n` +
199
+ ` * import type { VendoTheme } from "@vendoai/vendo";\n` +
200
+ ` * root.render(<VendoRoot components={registry} theme={theme as VendoTheme}><App /><VendoOverlay /></VendoRoot>);\n`
201
+ : ` * // in the client entry — theme.json adopts the host brand (08 §4);\n` +
202
+ ` * // <VendoOverlay /> is the visible surface (launcher pill + panel):\n` +
203
+ ` * import { VendoOverlay, VendoRoot } from "@vendoai/vendo/react";\n` +
204
+ ` * import { registry } from "<path-to>/vendo/registry.mjs";\n` +
205
+ ` * import theme from "<path-to>/.vendo/theme.json";\n` +
206
+ ` * root.render(<VendoRoot components={registry} theme={theme}><App /><VendoOverlay /></VendoRoot>);\n`;
207
+ return `/**\n` +
208
+ ` * Route your runtime's requests through this module:\n` +
209
+ ` * // Cloudflare Workers:\n` +
210
+ ` * // export default { fetch: (request, env) => handleVendoRequest(request, env) };\n` +
211
+ ` * // Bun / Deno / Hono / Node: serve your /api/vendo routes through\n` +
212
+ ` * // handleVendoRequest(request)\n` +
213
+ clientHint +
214
+ ` * Deployed hosts must set VENDO_BASE_URL to their public origin\n` +
215
+ ` * (credential forwarding fails closed without it — vendo doctor checks).\n` +
216
+ ` */\n` +
217
+ `import { createAnthropic } from "@ai-sdk/anthropic";\n` +
218
+ authImportLine(auth) +
219
+ `import { cloudConnections, cloudSandbox, cloudTools, createVendo, hostedStore } from "@vendoai/vendo/server";\n` +
220
+ `import { registry } from ${JSON.stringify(registrySpecifier)};\n` +
221
+ envType +
222
+ `\n${signatures.vendoVar}\n` +
223
+ `\n/** Lazy singleton: constructed on the first request, never at module\n` +
224
+ ` scope — Workers forbids I/O and timers there, and lazy is correct on\n` +
225
+ ` every other runtime too. */\n` +
226
+ `function getVendo${signatures.getVendo} {\n` +
227
+ ` if (vendo === null) {\n` +
228
+ ` const processEnv = globalThis.process?.env ?? {};\n` +
229
+ ` const apiKey = env.VENDO_API_KEY ?? processEnv.VENDO_API_KEY;\n` +
230
+ ` const baseUrl = (env.VENDO_CLOUD_URL ?? processEnv.VENDO_CLOUD_URL ?? "https://console.vendo.run").replace(/\\/+$/, "");\n` +
231
+ ` const cloud = apiKey === undefined || apiKey === "" ? undefined : { apiKey, baseUrl };\n` +
232
+ ` vendo = createVendo({\n` +
233
+ (auth === null ? anonymousPrincipalLines() : authConfigLines(auth))
234
+ .split("\n").map((line) => (line === "" ? line : ` ${line}`)).join("\n") +
235
+ ` catalog: registry,\n` +
236
+ ` policy: {}, // .vendo/policy.json: destructive asks, reads run\n` +
237
+ ` // With a Vendo Cloud key the infrastructure seams wire the Cloud\n` +
238
+ ` // adapters EXPLICITLY (composition decides; blocks never read the\n` +
239
+ ` // environment). Without one, pass your own adapters here — model,\n` +
240
+ ` // store, connections, sandbox all accept custom implementations.\n` +
241
+ ` ...(cloud === undefined ? {} : {\n` +
242
+ ` model: createAnthropic({ apiKey: cloud.apiKey, baseURL: \`\${cloud.baseUrl}/api/v1\` })("vendo-default"),\n` +
243
+ ` store: hostedStore(cloud),\n` +
244
+ ` connections: cloudConnections(cloud),\n` +
245
+ ` connectors: [cloudTools(cloud)],\n` +
246
+ ` sandbox: cloudSandbox(cloud),\n` +
247
+ ` }),\n` +
248
+ ` });\n` +
249
+ ` }\n` +
250
+ ` return vendo;\n` +
251
+ `}\n` +
252
+ `\nexport function handleVendoRequest${signatures.handle} {\n` +
253
+ ` return getVendo(env).handler(request);\n` +
254
+ `}\n`;
255
+ }
114
256
  export function expressServerSource(typescript, auth = null) {
115
257
  const imports = typescript
116
258
  ? `import { once } from "node:events";\n` +
@@ -143,17 +285,19 @@ export function expressServerSource(typescript, auth = null) {
143
285
  const registrySpecifier = typescript ? "./registry" : "./registry.mjs";
144
286
  const clientHint = typescript
145
287
  ? ` * // in the client entry — theme.json adopts the host brand (08 §4);\n` +
146
- ` * // the cast narrows TypeScript's widened JSON-module string literals:\n` +
147
- ` * import { VendoRoot } from "@vendoai/vendo/react";\n` +
288
+ ` * // the cast narrows TypeScript's widened JSON-module string literals;\n` +
289
+ ` * // <VendoOverlay /> is the visible surface (launcher pill + panel):\n` +
290
+ ` * import { VendoOverlay, VendoRoot } from "@vendoai/vendo/react";\n` +
148
291
  ` * import { registry } from "<path-to>/vendo/registry";\n` +
149
292
  ` * import theme from "<path-to>/.vendo/theme.json";\n` +
150
293
  ` * import type { VendoTheme } from "@vendoai/vendo";\n` +
151
- ` * root.render(<VendoRoot components={registry} theme={theme as VendoTheme}><App /></VendoRoot>);\n`
152
- : ` * // in the client entry — theme.json adopts the host brand (08 §4):\n` +
153
- ` * import { VendoRoot } from "@vendoai/vendo/react";\n` +
294
+ ` * root.render(<VendoRoot components={registry} theme={theme as VendoTheme}><App /><VendoOverlay /></VendoRoot>);\n`
295
+ : ` * // in the client entry — theme.json adopts the host brand (08 §4);\n` +
296
+ ` * // <VendoOverlay /> is the visible surface (launcher pill + panel):\n` +
297
+ ` * import { VendoOverlay, VendoRoot } from "@vendoai/vendo/react";\n` +
154
298
  ` * import { registry } from "<path-to>/vendo/registry.mjs";\n` +
155
299
  ` * import theme from "<path-to>/.vendo/theme.json";\n` +
156
- ` * root.render(<VendoRoot components={registry} theme={theme}><App /></VendoRoot>);\n`;
300
+ ` * root.render(<VendoRoot components={registry} theme={theme}><App /><VendoOverlay /></VendoRoot>);\n`;
157
301
  return `/**\n` +
158
302
  ` * Add these wiring lines in your host:\n` +
159
303
  ` * app.use("/api/vendo", mountVendo());\n` +
@@ -165,7 +309,7 @@ export function expressServerSource(typescript, auth = null) {
165
309
  `import { registry } from ${JSON.stringify(registrySpecifier)};\n` +
166
310
  types +
167
311
  `\nconst vendo = createVendo({\n` +
168
- (auth === null ? ` principal: async () => null,\n` : authConfigLines(auth)) +
312
+ (auth === null ? anonymousPrincipalLines() : authConfigLines(auth)) +
169
313
  ` catalog: registry,\n` +
170
314
  ` policy: {}, // .vendo/policy.json: destructive asks, reads run\n` +
171
315
  `});\n\n` +
@@ -1,4 +1,4 @@
1
- import { type ExtractedTool } from "@vendoai/actions";
1
+ import type { ExtractedTool } from "@vendoai/actions";
2
2
  import { type CloudStepOptions } from "./cloud-init.js";
3
3
  import { type AiExtractionOptions } from "./extract/extraction.js";
4
4
  import { type DevCredential } from "../dev-creds/resolve.js";
@@ -14,14 +14,15 @@ export interface RiskRecommendation {
14
14
  recommendation: string;
15
15
  }
16
16
  export interface InitPlan {
17
- framework: HostFramework;
17
+ framework: Exclude<HostFramework, "unknown"> | "custom";
18
18
  root: string;
19
19
  writes: string[];
20
20
  codeChanges: Array<{
21
21
  path: string;
22
22
  diff: string;
23
23
  }>;
24
- /** The one line init never writes itself: the user pastes it. */
24
+ /** Whatever wiring the run could not do safely itself (empty when the
25
+ layout auto-wire landed): the paste lines for the user. */
25
26
  manualSteps: string[];
26
27
  /** --agent only: deterministic extraction results, so an agent can act on
27
28
  real tool names instead of re-deriving them. */
@@ -53,7 +54,7 @@ export interface InitOptions {
53
54
  detection comes back "unknown" (there is no safe default to guess).
54
55
  "unknown" is excluded: an override that answers nothing would silently
55
56
  bypass the non-interactive framework guard. */
56
- framework?: Exclude<HostFramework, "unknown">;
57
+ framework?: Exclude<HostFramework, "unknown"> | "custom";
57
58
  /** --cloud-key: answer the cloud-login offer with an existing key — landed
58
59
  in .env.local exactly where the mint would put it. */
59
60
  cloudKey?: string;
@@ -114,5 +115,14 @@ export interface StarProcess {
114
115
  repo URL instead, one line, no error noise. Exported for the timeout's
115
116
  direct unit test only. */
116
117
  export declare function starViaGh(spawnStar: NonNullable<InitOptions["spawnStar"]>, timeoutMs?: number): Promise<boolean>;
118
+ /**
119
+ * The one bounded edit init makes to user-authored code (visible-surface fix,
120
+ * 0.4.1 E2E cert B3): mount the generated client wrapper around the layout's
121
+ * `{children}` and import it. Null — leave the file untouched, fall back to
122
+ * the printed paste — whenever a Vendo mount already exists (idempotence) or
123
+ * the file doesn't have exactly one JSX `{children}` to wrap (ambiguity).
124
+ * Exported for direct unit tests.
125
+ */
126
+ export declare function wireNextLayout(source: string, wrapperSpecifier: string): string | null;
117
127
  /** 09-vendo §5 — idempotent, zero-question setup. */
118
128
  export declare function runInit(options: InitOptions): Promise<number>;