@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
@@ -1,5 +1,5 @@
1
1
  import { type Telemetry } from "@vendoai/telemetry";
2
- export declare const CLI_VERSION = "0.4.2";
2
+ export declare const CLI_VERSION = "0.4.4";
3
3
  export interface Output {
4
4
  log(message: string): void;
5
5
  error(message: string): void;
@@ -4,7 +4,7 @@ import { dirname, join } from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin, stdout } from "node:process";
6
6
  import { initTelemetry, repoHost } from "@vendoai/telemetry";
7
- export const CLI_VERSION = "0.4.2";
7
+ export const CLI_VERSION = "0.4.4";
8
8
  export const consoleOutput = {
9
9
  log: (message) => console.log(message),
10
10
  error: (message) => console.error(message),
@@ -1,4 +1,4 @@
1
- import { vendoSync, type SyncReportWithWarnings } from "@vendoai/actions";
1
+ import { vendoSync, type SyncReportWithWarnings } from "@vendoai/actions/sync";
2
2
  import type { ToolImpact } from "../sync-impact.js";
3
3
  import { type Output, type TelemetryOptions } from "./shared.js";
4
4
  export interface SyncReportPayload {
package/dist/cli/sync.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { join, resolve } from "node:path";
2
- import { vendoSync } from "@vendoai/actions";
2
+ import { vendoSync } from "@vendoai/actions/sync";
3
3
  import { pushSyncReport } from "./cloud/services.js";
4
4
  import { syncSemantics } from "./semantics.js";
5
5
  import { consoleOutput, withCommandRun } from "./shared.js";
@@ -1,5 +1,6 @@
1
1
  import { publishRecordSchema, shareSnapshotSchema, } from "@vendoai/apps";
2
2
  import { consoleSender, raiseCloudError } from "./cloud-console.js";
3
+ import { defaultFetch } from "@vendoai/core";
3
4
  const DEFAULT_TIMEOUT_MS = 30_000;
4
5
  /** The console mounts the share/publish surface here. */
5
6
  const CONSOLE_APPS_PATH = "/api/v1/apps";
@@ -17,7 +18,7 @@ export function cloudApps(options) {
17
18
  mountPath: CONSOLE_APPS_PATH,
18
19
  apiKey: options.apiKey,
19
20
  timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
20
- fetchImpl: options.fetch ?? globalThis.fetch,
21
+ fetchImpl: options.fetch ?? defaultFetch,
21
22
  raise: raiseAppsError,
22
23
  });
23
24
  const post = async (path, appId, doc, schema) => {
@@ -0,0 +1,16 @@
1
+ export interface CloudUrlOptions {
2
+ apiUrl?: string;
3
+ env?: Record<string, string | undefined>;
4
+ }
5
+ export declare function resolveCloudBaseUrl(options?: CloudUrlOptions): string;
6
+ export interface CloudKeyFetchOptions extends CloudUrlOptions {
7
+ apiKey?: string;
8
+ method?: string;
9
+ body?: unknown;
10
+ fetchImpl?: typeof fetch;
11
+ signal?: AbortSignal;
12
+ }
13
+ /** POST/GET a console API path with VENDO_API_KEY bearer auth. The console's
14
+ * shared auth middleware upserts deployment inventory and meters usage from
15
+ * the identity headers on real service calls (deployment-identity.ts). */
16
+ export declare function cloudKeyFetch<T = unknown>(path: string, options?: CloudKeyFetchOptions): Promise<T>;
@@ -0,0 +1,40 @@
1
+ /** Portable key-authenticated console calls for RUNTIME code (capability
2
+ * misses today). The CLI's cloudFetch adds user sessions, refresh, and disk
3
+ * state on top — Node-only concerns that used to ride into Worker bundles
4
+ * whenever runtime code borrowed it. Keep this module free of node builtins
5
+ * and CLI imports; the portability gate bundles it. */
6
+ import { defaultFetch } from "@vendoai/core";
7
+ import { deploymentIdentityHeaders } from "./deployment-identity.js";
8
+ import { VERSION } from "./wire/shared.js";
9
+ const DEFAULT_CLOUD_URL = "https://console.vendo.run";
10
+ const processEnv = () => globalThis.process?.env ?? {};
11
+ export function resolveCloudBaseUrl(options = {}) {
12
+ const value = options.apiUrl ?? (options.env ?? processEnv()).VENDO_CLOUD_URL ?? DEFAULT_CLOUD_URL;
13
+ return value.replace(/\/+$/, "");
14
+ }
15
+ /** POST/GET a console API path with VENDO_API_KEY bearer auth. The console's
16
+ * shared auth middleware upserts deployment inventory and meters usage from
17
+ * the identity headers on real service calls (deployment-identity.ts). */
18
+ export async function cloudKeyFetch(path, options = {}) {
19
+ const token = options.apiKey ?? (options.env ?? processEnv()).VENDO_API_KEY;
20
+ if (token === undefined || token === "") {
21
+ throw new Error("Vendo Cloud key call without a key: pass apiKey or set VENDO_API_KEY");
22
+ }
23
+ const headers = {
24
+ accept: "application/json",
25
+ authorization: `Bearer ${token}`,
26
+ "user-agent": `vendo-cli/${VERSION}`,
27
+ ...(options.body === undefined ? {} : { "content-type": "application/json" }),
28
+ ...(await deploymentIdentityHeaders()),
29
+ };
30
+ const response = await (options.fetchImpl ?? defaultFetch)(`${resolveCloudBaseUrl(options)}${path}`, {
31
+ method: options.method ?? (options.body === undefined ? "GET" : "POST"),
32
+ headers,
33
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
34
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
35
+ });
36
+ if (!response.ok) {
37
+ throw new Error(`Vendo Cloud ${path} answered ${response.status}`);
38
+ }
39
+ return await response.json();
40
+ }
@@ -1,5 +1,6 @@
1
1
  import { composioToolRisk, normalizeToolName } from "@vendoai/actions";
2
2
  import { deploymentIdentityHeaders } from "./deployment-identity.js";
3
+ import { defaultFetch } from "@vendoai/core";
3
4
  function errorOutcome(message) {
4
5
  return { status: "error", error: { code: "connector-error", message } };
5
6
  }
@@ -8,7 +9,7 @@ function withIdentity(outcome, identity) {
8
9
  }
9
10
  export function cloudTools(options) {
10
11
  const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
11
- const fetchImpl = options.fetch ?? globalThis.fetch;
12
+ const fetchImpl = options.fetch ?? defaultFetch;
12
13
  let normalizedToRaw = new Map();
13
14
  async function cloudFetch(path, init) {
14
15
  const response = await fetchImpl(`${base}${path}`, {
@@ -1,4 +1,4 @@
1
- import { VendoError } from "@vendoai/core";
1
+ import { defaultFetch, VendoError } from "@vendoai/core";
2
2
  import { consoleSender, raiseCloudError } from "./cloud-console.js";
3
3
  /** Subjects the runtime mints for machine principals (automations webhook
4
4
  * triggers today; the reserved `vendo:` namespace going forward). A synthetic
@@ -95,7 +95,7 @@ const raiseConnectionsError = (response) => raiseCloudError(response, "connectio
95
95
  * the wire it must serve. */
96
96
  export function cloudConnections(options) {
97
97
  const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
98
- const fetchImpl = options.fetch ?? globalThis.fetch;
98
+ const fetchImpl = options.fetch ?? defaultFetch;
99
99
  // The key-authed console sender (cloud-console.ts): Bearer auth + deployment
100
100
  // identity (the console meters usage from real traffic) + per-request abort
101
101
  // timeout, raising through the shared error table on any non-2xx.
@@ -0,0 +1,21 @@
1
+ /** Web-standard-runtime build of the dev credential ladder, selected by the
2
+ * package's `worker`/`workerd`/`edge-light`/`browser` import conditions
3
+ * (see `#dev-creds/model` in package.json).
4
+ *
5
+ * The real ladder is Node by design: it resolves the host's own @ai-sdk
6
+ * provider install via createRequire from the project root, reads `vendo
7
+ * login` sessions off disk, and anchors on process.cwd(). None of that
8
+ * exists inside a Worker bundle — Mohamed's field report had the ladder
9
+ * dying on runtime require.resolve under Wrangler even with VENDO_API_KEY
10
+ * set. On these runtimes the model seam is explicit by contract: the host
11
+ * wiring constructs the provider (init generates it; the Cloud gateway is
12
+ * the stock Anthropic provider pointed at the console). The ladder's edge
13
+ * build therefore resolves to honest guidance instead of half-working
14
+ * magic. Keep this module free of node builtins and CLI imports; the
15
+ * portability gate bundles it. */
16
+ import type { LanguageModel } from "ai";
17
+ import type { DevModelOptions } from "./model.js";
18
+ export type { DevModelOptions };
19
+ /** Same seam as the Node build; announces its unavailability once on first
20
+ * use through the server log, like the Node ladder's unavailable rung. */
21
+ export declare function devModel(_options?: DevModelOptions): LanguageModel;
@@ -0,0 +1,24 @@
1
+ const EDGE_MESSAGE = "the dev model ladder needs Node (it resolves the host's provider install and dev credentials from disk); "
2
+ + "on this runtime pass `model:` to createVendo explicitly — with a Vendo Cloud key that is the stock Anthropic "
3
+ + "provider pointed at the console gateway: createAnthropic({ apiKey: VENDO_API_KEY, baseURL: `${VENDO_CLOUD_URL ?? \"https://console.vendo.run\"}/api/v1` })(\"vendo-default\")";
4
+ /** Same seam as the Node build; announces its unavailability once on first
5
+ * use through the server log, like the Node ladder's unavailable rung. */
6
+ export function devModel(_options = {}) {
7
+ let announced = false;
8
+ const refuse = () => {
9
+ if (!announced) {
10
+ announced = true;
11
+ console.error(`[vendo] model: ${EDGE_MESSAGE}`);
12
+ }
13
+ throw new Error(EDGE_MESSAGE);
14
+ };
15
+ const model = {
16
+ specificationVersion: "v3",
17
+ provider: "vendo-dev",
18
+ modelId: "dev-env",
19
+ supportedUrls: {},
20
+ doGenerate: () => Promise.resolve().then(refuse),
21
+ doStream: () => Promise.resolve().then(refuse),
22
+ };
23
+ return model;
24
+ }
@@ -2,6 +2,7 @@ import { vendoRecordSchema, } from "@vendoai/core";
2
2
  import { DEDICATED_RECORD_COLLECTIONS, RESERVED_COLLECTIONS, } from "@vendoai/store";
3
3
  import { consoleSender, raiseCloudError, toArrayBuffer } from "./cloud-console.js";
4
4
  import { deploymentIdentityHeaders } from "./deployment-identity.js";
5
+ import { defaultFetch } from "@vendoai/core";
5
6
  /** The console mounts the hosted-store surface here
6
7
  * (apps/console/app/api/v1/store/*). */
7
8
  const CONSOLE_STORE_PATH = "/api/v1/store";
@@ -77,7 +78,7 @@ function parseNullableRecord(value) {
77
78
  export function hostedStore(options) {
78
79
  const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
79
80
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
80
- const fetchImpl = options.fetch ?? globalThis.fetch;
81
+ const fetchImpl = options.fetch ?? defaultFetch;
81
82
  const send = consoleSender({
82
83
  base,
83
84
  mountPath: CONSOLE_STORE_PATH,
package/dist/mastra.js CHANGED
@@ -29,6 +29,74 @@ export const VENDO_PRINCIPAL_KEY = "vendo-principal";
29
29
  /** Optional request-context key carrying the host session id into Vendo's
30
30
  * audit trail; unset, the shim mints one per call. */
31
31
  export const VENDO_SESSION_KEY = "vendo-session-id";
32
+ /** The bridge property carrying an OPEN tool's arguments (see
33
+ * {@link isOpenObjectSchema}). */
34
+ const OPEN_ARGS_KEY = "args";
35
+ /**
36
+ * An OPEN tool input: an object schema with no declared properties that
37
+ * accepts arbitrary fields (extraction emits these for routes whose body shape
38
+ * it cannot type, e.g. `{type:"object", properties:{}, additionalProperties:
39
+ * true}`). Mastra's provider schema-compat layers hard-close every object node
40
+ * for strict-mode providers (the OpenAI layer sets `additionalProperties:
41
+ * false` on all of them), so an open schema reaches the model as "this tool
42
+ * takes NO arguments" — and the model dutifully calls it with `{}` even when
43
+ * the user dictated exact args (0.4.x E2E, report-mastra defect 5: the
44
+ * approved call then executed with an empty body). A schema with declared
45
+ * properties survives those transforms, so open inputs ride a single declared
46
+ * JSON-string property instead, unwrapped in execute before the guard.
47
+ */
48
+ function isOpenObjectSchema(schema) {
49
+ if (typeof schema !== "object" || schema === null || Array.isArray(schema))
50
+ return false;
51
+ const record = schema;
52
+ if (record.type !== undefined && record.type !== "object")
53
+ return false;
54
+ const properties = record.properties;
55
+ const declared = typeof properties === "object" && properties !== null
56
+ && Object.keys(properties).length > 0;
57
+ return !declared && record.additionalProperties !== false;
58
+ }
59
+ function openArgsBridgeSchema(tool) {
60
+ return {
61
+ type: "object",
62
+ properties: {
63
+ [OPEN_ARGS_KEY]: {
64
+ // Both forms validate at runtime; strict-mode providers ride the
65
+ // string branch (their compat layer closes every object node), and
66
+ // permissive providers may pass the object directly.
67
+ type: ["string", "object"],
68
+ description: `The arguments to pass to ${tool}: a JSON object, or that object encoded as one JSON string literal (e.g. {"field":"value"}). Include every field the request calls for; pass {} when there are none.`,
69
+ },
70
+ },
71
+ };
72
+ }
73
+ /** Recover the real tool args from a bridged call: a JSON-string `args`
74
+ * (the advertised shape), a plain-object `args` (a model that skipped the
75
+ * encoding), or the raw payload itself (a provider that ignored the bridge). */
76
+ function unwrapOpenArgs(input) {
77
+ if (typeof input !== "object" || input === null || Array.isArray(input))
78
+ return input ?? {};
79
+ const record = input;
80
+ if (OPEN_ARGS_KEY in record) {
81
+ const value = record[OPEN_ARGS_KEY];
82
+ if (value === null || value === undefined)
83
+ return {};
84
+ if (typeof value === "string") {
85
+ const trimmed = value.trim();
86
+ if (trimmed === "")
87
+ return {};
88
+ try {
89
+ return JSON.parse(trimmed);
90
+ }
91
+ catch {
92
+ throw new VendoError("validation", `${OPEN_ARGS_KEY} must be one JSON object literal (e.g. {"field":"value"}); the provided value did not parse as JSON`);
93
+ }
94
+ }
95
+ if (typeof value === "object" && !Array.isArray(value))
96
+ return value;
97
+ }
98
+ return record;
99
+ }
32
100
  function principalFrom(context) {
33
101
  const candidate = context?.requestContext?.get(VENDO_PRINCIPAL_KEY);
34
102
  if (typeof candidate?.kind !== "string" || typeof candidate.subject !== "string") {
@@ -74,11 +142,12 @@ export async function vendoMastraTools(vendo, options) {
74
142
  });
75
143
  const tools = {};
76
144
  for (const entry of pack) {
145
+ const bridged = isOpenObjectSchema(entry.inputSchema);
77
146
  tools[entry.name] = createTool({
78
147
  id: entry.name,
79
148
  description: entry.description,
80
- inputSchema: entry.inputSchema,
81
- execute: (inputData, context) => entry.execute(inputData, {
149
+ inputSchema: bridged ? openArgsBridgeSchema(entry.name) : entry.inputSchema,
150
+ execute: (inputData, context) => entry.execute(bridged ? unwrapOpenArgs(inputData) : inputData, {
82
151
  ctx: runContextFrom(context),
83
152
  ...(context?.agent?.toolCallId === undefined ? {} : { callId: context.agent.toolCallId }),
84
153
  }),
package/dist/react.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { VendoProvider } from "@vendoai/ui";
2
2
  import { type ComponentProps } from "react";
3
3
  export { createVendoClient, type VendoClient, type VendoClientConfig, VendoProvider, hostComponentMap, useVendoContext, useVendoDiscoverability, useVendoGreeting, useVendoTheme, useVendoTools, type ConnectorOption, type HostComponentsInput, defaultVendoGreeting, type VendoDiscoverability, type VendoGreeting, type ToolMeta, type ToolMetaMap, VendoAppEmbed, VendoApprovalEmbed, VendoToolResult, useActivity, useApp, useApps, useApprovals, useAutomations, useConnections, useConnectorCatalog, useGrants, useMobileTakeover, type MobileTakeover, type PollOptions, useSlotApp, useThreads, useVendoOverlay, type VendoOverlayController, useVendoStatus, useVendoThread, type VendoThreadApproval, ScriptedTransport, type DirectorCue, type DirectorScript, defaultVendoTheme, resolveTheme, themeCssVariables, useVoice, type UseVoiceResult, type OpenSurface, type InClientVenue, type PinDrift, type ShipDiff, type EditResult, type PinRebaseResult, type VersionEntry, type ConnectionAccount, type InitiatedConnection, type RunStatus, type RunRecord, type RunPlan, type AutomationEntry, type EnableResult, type Thread, type ThreadSummary, type GuardPosture, type VendoStatus, } from "@vendoai/ui";
4
+ export { VendoOverlay, type VendoOverlayProps } from "@vendoai/ui/chrome";
4
5
  export { remixable, type RemixableRegistration, type RemixableReportOptions } from "./remixable.js";
5
6
  type ProviderProps = ComponentProps<typeof VendoProvider>;
6
7
  /** 09-vendo §1 — the UI provider prewired to the default wire base. */
package/dist/react.js CHANGED
@@ -23,6 +23,13 @@ useActivity, useApp, useApps, useApprovals, useAutomations, useConnections, useC
23
23
  defaultVendoTheme, resolveTheme, themeCssVariables,
24
24
  // voice/use-voice.ts
25
25
  useVoice, } from "@vendoai/ui";
26
+ // The visible agent surface (launcher pill + panel) — re-exported from the
27
+ // chrome subpath so the init-scaffolded layout wrapper can import everything
28
+ // from "@vendoai/vendo/react": hosts only get @vendoai/vendo as a direct
29
+ // dependency, and under pnpm strict linking the transitive "@vendoai/ui/chrome"
30
+ // does not resolve for them (same TS2307 story as the registry's
31
+ // ComponentRegistry import).
32
+ export { VendoOverlay } from "@vendoai/ui/chrome";
26
33
  export { remixable } from "./remixable.js";
27
34
  /** 09-vendo §1 — the UI provider prewired to the default wire base. */
28
35
  export function VendoRoot(props) {
package/dist/refine.js CHANGED
@@ -4,6 +4,7 @@ import { validateCapabilities, capabilitiesFileSchema, overridesFileSchema, tool
4
4
  import { TOOL_NAME_PATTERN, VENDO_CAPABILITIES_FORMAT, VENDO_OVERRIDES_FORMAT, capabilityMissEventSchema, } from "@vendoai/core";
5
5
  import { generateObject } from "ai";
6
6
  import { z } from "zod";
7
+ import { defaultFetch } from "@vendoai/core";
7
8
  /**
8
9
  * The refine engine (ENG-250, extraction spec §3). One engine, two surfaces:
9
10
  * the `vendo refine` CLI command and the end-of-init offer — both call
@@ -496,7 +497,7 @@ const stringify = (value) => `${JSON.stringify(value, null, 2)}\n`;
496
497
  // ---------------------------------------------------------------------------
497
498
  export async function runRefine(options) {
498
499
  const root = resolve(options.root);
499
- const fetchImpl = options.fetchImpl ?? fetch;
500
+ const fetchImpl = options.fetchImpl ?? defaultFetch;
500
501
  const interview = options.interview ?? [];
501
502
  const startedAt = new Date().toISOString();
502
503
  const inputs = await loadInputs(root, options);
package/dist/sandbox.js CHANGED
@@ -1,4 +1,4 @@
1
- import { VendoError } from "@vendoai/core";
1
+ import { defaultFetch, VendoError } from "@vendoai/core";
2
2
  import { deploymentIdentityHeaders } from "./deployment-identity.js";
3
3
  import { CLOUD_BOX_PORT, CLOUD_SANDBOX_PATH, CLOUD_SNAPSHOT_REF_PREFIX, CLOUD_SNAPSHOTS_SUBPATH, CONSOLE_SNAPSHOT_REF_PREFIX, } from "./sandbox-wire.js";
4
4
  /** Console error codes forwarded as-is when they are wire-legal VendoError
@@ -142,7 +142,7 @@ const isGone = (error) => error instanceof VendoError && error.code === "not-fou
142
142
  export function cloudSandbox(options) {
143
143
  const base = (options.baseUrl ?? "https://console.vendo.run").replace(/\/$/, "");
144
144
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
145
- const fetchImpl = options.fetch ?? globalThis.fetch;
145
+ const fetchImpl = options.fetch ?? defaultFetch;
146
146
  const send = async (path, init = {}) => {
147
147
  const response = await fetchImpl(`${base}${CLOUD_SANDBOX_PATH}${path}`, {
148
148
  ...init,
package/dist/server.d.ts CHANGED
@@ -11,7 +11,7 @@ export { createStore, envSecrets, secretStore, storeSecrets } from "@vendoai/sto
11
11
  export { hostAuthPresetConformance, type HostAuthPreset, type HostAuthPresetConformanceOptions, type HostAuthPresetOptions, type HostAuthPresetUser, type HostAuthPresetUserResolver, type SupabaseHostAuthPresetOptions, } from "./auth-presets/index.js";
12
12
  import type { HostAuthPreset } from "./auth-presets/index.js";
13
13
  import type { LanguageModel } from "ai";
14
- export { devModel, type DevModelOptions } from "./dev-creds/model.js";
14
+ export { devModel, type DevModelOptions } from "#dev-creds/model";
15
15
  import { type ConnectionsService } from "./connections.js";
16
16
  export { byoConnections, cloudConnections, unconfiguredConnections, type CloudConnectionsOptions, type ConnectionsService, } from "./connections.js";
17
17
  export { cloudSandbox, type CloudSandboxOptions } from "./sandbox.js";
package/dist/server.js CHANGED
@@ -28,11 +28,11 @@ import { createByoApprovals } from "./byo-approvals.js";
28
28
  import { initTelemetry } from "@vendoai/telemetry";
29
29
  import { capabilitySurfaceSnapshot, createCapabilityMissCapture, } from "./capability-misses.js";
30
30
  import { catalogThemeSummary, mergeRuntimeCatalog, normalizeCatalogConfig, runtimeCatalogFromJson } from "./catalog.js";
31
- import { devModel } from "./dev-creds/model.js";
31
+ import { devModel } from "#dev-creds/model";
32
32
  // install-dx v1 — `devModel()` is the env-resolving model createVendo composes
33
33
  // when the host passes none; the resolver is shared by init and doctor (one
34
34
  // credential story, real keys only).
35
- export { devModel } from "./dev-creds/model.js";
35
+ export { devModel } from "#dev-creds/model";
36
36
  import { byoConnections, cloudConnections, hasConnections, unconfiguredConnections, } from "./connections.js";
37
37
  // The shipped connections adapters ride the server surface so a host can pass
38
38
  // one explicitly via createVendo({ connections }) — see selectConnections.
@@ -594,7 +594,7 @@ function createWireHandler(deps) {
594
594
  // addressing a real Vendo WIRE route may (04 §4), and the door's paths are
595
595
  // the door's, not wire routes.
596
596
  if (deps.door !== undefined && isDoorPath(url.pathname)) {
597
- await deps.ready;
597
+ await deps.ready();
598
598
  return await deps.door.handler(request);
599
599
  }
600
600
  const path = relativePath(url);
@@ -606,7 +606,7 @@ function createWireHandler(deps) {
606
606
  if (jsonMutationRequired(request, path) && !isJsonRequest(request)) {
607
607
  throw new VendoError("validation", "content-type must be application/json");
608
608
  }
609
- await deps.ready;
609
+ await deps.ready();
610
610
  // The per-request view the route table dispatches on (kill-list B4).
611
611
  // Segments decode lazily so raw-matched pre-routes (proxy, webhooks,
612
612
  // doctor) never decode — preserving the old chain's decode timing.
@@ -688,10 +688,23 @@ export function createVendo(config) {
688
688
  // Inference, selected by the adapter rule at this composition seam
689
689
  // (selectModel above) — the one model the agent and apps blocks consume.
690
690
  const inference = selectModel(config.model);
691
- const ready = store.ensureSchema();
692
- // Keep eager schema readiness for hosts that reach into composed blocks,
693
- // while preventing an unhandled rejection before the first handler/emit awaits it.
694
- void ready.catch(() => undefined);
691
+ // Construction stays PURE — no I/O, no timers — because the common edge
692
+ // wiring calls createVendo() at module init, where Workers forbids both
693
+ // (Mohamed's field report: "Disallowed operation called within global
694
+ // scope"). The first handler/emit touch starts schema readiness and the
695
+ // background sweep together through this once-latch; on Node the first
696
+ // request pays the same cost the old eager kick merely front-loaded.
697
+ let startBackgroundSweep = () => undefined;
698
+ let readyState;
699
+ const ready = () => {
700
+ if (readyState === undefined) {
701
+ readyState = store.ensureSchema();
702
+ // No unhandled rejection before a handler/emit awaits the latch.
703
+ void readyState.catch(() => undefined);
704
+ startBackgroundSweep();
705
+ }
706
+ return readyState;
707
+ };
695
708
  let resolveAppToolRisk;
696
709
  const guard = createGuard({
697
710
  store,
@@ -1101,16 +1114,21 @@ export function createVendo(config) {
1101
1114
  // engine pattern) so an idle process still reclaims sessions with no traffic;
1102
1115
  // unref'd means it never keeps the event loop alive. Torn down with the store.
1103
1116
  if (sweepEnabled) {
1104
- const sweepTimer = setInterval(() => {
1105
- runSweep().catch((error) => {
1106
- console.warn(`[vendo] session sweep failed; will retry next interval: ${error instanceof Error ? error.message : String(error)}`);
1107
- });
1108
- }, sessionsConfig.sweepIntervalMs);
1109
- sweepTimer.unref?.();
1110
- const closeStore = store.close.bind(store);
1111
- store.close = async () => {
1112
- clearInterval(sweepTimer);
1113
- await closeStore();
1117
+ // Armed by the ready() latch above, NOT at construction: timers are
1118
+ // illegal in Workers global scope, and a process that never serves a
1119
+ // request has nothing to sweep.
1120
+ startBackgroundSweep = () => {
1121
+ const sweepTimer = setInterval(() => {
1122
+ runSweep().catch((error) => {
1123
+ console.warn(`[vendo] session sweep failed; will retry next interval: ${error instanceof Error ? error.message : String(error)}`);
1124
+ });
1125
+ }, sessionsConfig.sweepIntervalMs);
1126
+ sweepTimer.unref?.();
1127
+ const closeStore = store.close.bind(store);
1128
+ store.close = async () => {
1129
+ clearInterval(sweepTimer);
1130
+ await closeStore();
1131
+ };
1114
1132
  };
1115
1133
  }
1116
1134
  // Wave 2 (Cloud auto): a keyed deployment's schedule- and external-triggered
@@ -1196,7 +1214,11 @@ export function createVendo(config) {
1196
1214
  ...(theme === undefined ? {} : { theme }),
1197
1215
  });
1198
1216
  }
1199
- const sessionId = `session_${globalThis.crypto.randomUUID()}`;
1217
+ // Minted on first request via the deps getter below — Workers forbids
1218
+ // generating random values in global scope, and createVendo runs at module
1219
+ // init in the edge wiring. Still one fallback id per process.
1220
+ let processSessionId;
1221
+ const sessionId = () => (processSessionId ??= `session_${globalThis.crypto.randomUUID()}`);
1200
1222
  // Anonymous principals are minted per-CLIENT in the handler (opaque cookie
1201
1223
  // pointer; the store's vendo_sessions row is the authority — kill-list B3).
1202
1224
  // An https VENDO_BASE_URL means TLS terminates at a trusted proxy and requests
@@ -1220,7 +1242,7 @@ export function createVendo(config) {
1220
1242
  principal: resolvePrincipal,
1221
1243
  ready,
1222
1244
  trustedBaseIsHttps,
1223
- sessionId,
1245
+ get sessionId() { return sessionId(); },
1224
1246
  store,
1225
1247
  telemetry: telemetryClient(config.telemetry),
1226
1248
  agent,
@@ -1267,12 +1289,24 @@ export function createVendo(config) {
1267
1289
  return {
1268
1290
  handler,
1269
1291
  async emit(event, payload, principal) {
1270
- await ready;
1292
+ await ready();
1271
1293
  return automations.emit(event, payload, principal);
1272
1294
  },
1273
1295
  agent,
1274
1296
  guard,
1275
- guardedTools: byoApprovals.registry,
1297
+ // The BYO seam (ai-sdk.ts / mastra.ts tool packs) reaches the store
1298
+ // without ever touching handler/emit, so its execute leg arms the same
1299
+ // ready() latch — the composed-block head start the old eager kick gave
1300
+ // such hosts, without the construction-time I/O Workers forbids. Direct
1301
+ // vendo.store/automations reach-ins still own their readiness (await
1302
+ // store.ensureSchema(), as the mastra example and defer tests do).
1303
+ guardedTools: {
1304
+ ...byoApprovals.registry,
1305
+ execute: async (call, ctx) => {
1306
+ await ready();
1307
+ return byoApprovals.registry.execute(call, ctx);
1308
+ },
1309
+ },
1276
1310
  apps,
1277
1311
  automations,
1278
1312
  actions,
package/dist/wire/apps.js CHANGED
@@ -1,5 +1,21 @@
1
1
  import { VendoError } from "@vendoai/core";
2
+ import { appStore } from "@vendoai/store";
2
3
  import { json, requestJson, route, string } from "./shared.js";
4
+ /** Unscoped existence probe behind the ?pending=1 disambiguation: does ANY
5
+ principal own a record with this id? Owner-scoped open() answers not-found
6
+ for both "still building" and "exists under someone else", and masking the
7
+ latter as pending is the infinite skeleton of 0.4.1 E2E cert B4 (wire
8
+ principal ≠ chat principal). No document content leaves this check. A
9
+ store that can't answer (hosted wire-door stores have no local db handle)
10
+ keeps the pending window — today's behavior. */
11
+ async function appRecordExists(store, appId) {
12
+ try {
13
+ return (await appStore(store).get(appId)) !== null;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
3
19
  /** 06-apps / 09 §3 — the /apps wire area: CRUD, open/call/edit, history,
4
20
  ship-diff, pin drift/rebase, the gesture fork (fork-pin), export/import,
5
21
  fork (whole-app copy — a different feature from fork-pin). */
@@ -71,13 +87,26 @@ export const appRoutes = [
71
87
  // console 404. Under the additive ?pending=1 flag, ONLY that expected
72
88
  // pre-servable miss becomes a quiet 200 {kind:"pending"}; unflagged
73
89
  // callers keep the contracted 404, and every other failure keeps its
74
- // envelope and status either way.
90
+ // envelope and status either way. A record that DOES exist — just not
91
+ // for this caller — is not a build in progress and never will be: that
92
+ // answers the terminal failed vocabulary (with the principal-mismatch
93
+ // diagnosis) so the embed resolves promptly instead of skeleton-polling
94
+ // to its deadline (0.4.1 E2E cert B4).
75
95
  if (wire.url.searchParams.get("pending") === "1") {
76
96
  try {
77
97
  return json(await deps.apps.open(appId, ctx));
78
98
  }
79
99
  catch (reason) {
80
100
  if (reason instanceof VendoError && reason.code === "not-found") {
101
+ if (await appRecordExists(deps.store, appId)) {
102
+ return json({
103
+ kind: "failed",
104
+ reason: "this app exists but is not visible to this surface's caller — "
105
+ + "the wire route's principal must resolve the same subject your agent loop uses "
106
+ + "(see the principal comment in the generated route, or docs.vendo.run/existing-agents)",
107
+ retryable: false,
108
+ });
109
+ }
81
110
  return json({ kind: "pending" });
82
111
  }
83
112
  throw reason;
@@ -14,7 +14,7 @@ import type { RuntimeCaptureHandler } from "../runtime-capture.js";
14
14
  shares. The anonymous-session + RunContext resolution lives in
15
15
  wire/context.ts; server.ts assembles the table from the per-area modules
16
16
  under src/wire/. */
17
- export declare const VERSION = "0.4.2";
17
+ export declare const VERSION = "0.4.4";
18
18
  export declare const BASE_PATH = "/api/vendo";
19
19
  export type SandboxVenue = "e2b" | "cloud" | "custom" | false;
20
20
  /** How inference is served: "custom" (a host-passed model) or "ladder" (the
@@ -24,7 +24,7 @@ export type SandboxVenue = "e2b" | "cloud" | "custom" | false;
24
24
  export type ModelVenue = "custom" | "ladder";
25
25
  export interface WireDeps {
26
26
  principal: (req: Request) => Promise<Principal | null>;
27
- ready: Promise<void>;
27
+ ready: () => Promise<void>;
28
28
  /** VENDO_BASE_URL is https → TLS terminates upstream; see secureRequest. */
29
29
  trustedBaseIsHttps: boolean;
30
30
  sessionId: string;
@@ -4,7 +4,7 @@ import { VendoError, } from "@vendoai/core";
4
4
  shares. The anonymous-session + RunContext resolution lives in
5
5
  wire/context.ts; server.ts assembles the table from the per-area modules
6
6
  under src/wire/. */
7
- export const VERSION = "0.4.2";
7
+ export const VERSION = "0.4.4";
8
8
  export const BASE_PATH = "/api/vendo";
9
9
  const STATUS_BY_CODE = {
10
10
  validation: 400,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendoai/vendo",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "The Vendo umbrella: default composition, public wire routes, React provider, and the vendo CLI.",
5
5
  "keywords": [
6
6
  "ai",
@@ -86,16 +86,16 @@
86
86
  "ajv": "^8.20.0",
87
87
  "ajv-formats": "^3.0.1",
88
88
  "zod": "^3.25.0",
89
- "@vendoai/actions": "0.4.2",
90
- "@vendoai/apps": "0.4.2",
91
- "@vendoai/automations": "0.4.2",
92
- "@vendoai/agent": "0.4.2",
93
- "@vendoai/core": "0.4.2",
94
- "@vendoai/guard": "0.4.2",
95
- "@vendoai/mcp": "0.4.2",
96
- "@vendoai/store": "0.4.2",
97
- "@vendoai/telemetry": "0.3.1",
98
- "@vendoai/ui": "0.4.2"
89
+ "@vendoai/actions": "0.4.4",
90
+ "@vendoai/agent": "0.4.4",
91
+ "@vendoai/apps": "0.4.4",
92
+ "@vendoai/automations": "0.4.4",
93
+ "@vendoai/core": "0.4.4",
94
+ "@vendoai/guard": "0.4.4",
95
+ "@vendoai/mcp": "0.4.4",
96
+ "@vendoai/store": "0.4.4",
97
+ "@vendoai/ui": "0.4.4",
98
+ "@vendoai/telemetry": "0.3.2"
99
99
  },
100
100
  "peerDependencies": {
101
101
  "@auth/core": "^0.34.3",
@@ -137,6 +137,15 @@
137
137
  "vite": "^6.0.0",
138
138
  "vitest": "^2.1.0"
139
139
  },
140
+ "imports": {
141
+ "#dev-creds/model": {
142
+ "workerd": "./dist/dev-creds/model-edge.js",
143
+ "worker": "./dist/dev-creds/model-edge.js",
144
+ "edge-light": "./dist/dev-creds/model-edge.js",
145
+ "browser": "./dist/dev-creds/model-edge.js",
146
+ "default": "./dist/dev-creds/model.js"
147
+ }
148
+ },
140
149
  "scripts": {
141
150
  "build:playground": "node scripts/build-playground.mjs",
142
151
  "prebuild": "pnpm run build:playground",