devframe 1.2.3 → 1.2.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 (61) hide show
  1. package/dist/{_shared-B2PZsp-J.mjs → _shared-DlRoV7v3.mjs} +1 -0
  2. package/dist/{_shared-DjkowZKP.d.mts → _shared-NSl4p_om.d.mts} +4 -2
  3. package/dist/adapters/build.d.mts +1 -1
  4. package/dist/adapters/build.mjs +3 -3
  5. package/dist/adapters/cac.d.mts +1 -1
  6. package/dist/adapters/cac.mjs +2 -2
  7. package/dist/adapters/dev.d.mts +3 -3
  8. package/dist/adapters/dev.mjs +2 -2
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +2 -2
  11. package/dist/adapters/initiate.mjs +1 -1
  12. package/dist/adapters/mcp.d.mts +2 -2
  13. package/dist/{agentic-B8y_r5tQ.d.mts → agentic-D61fXLzA.d.mts} +1 -1
  14. package/dist/client/index.d.mts +2 -1
  15. package/dist/client/index.mjs +60 -11
  16. package/dist/constants.d.mts +1 -0
  17. package/dist/constants.mjs +1 -1
  18. package/dist/{context-BqpgC5tq.mjs → context-BKw9KkCR.mjs} +110 -83
  19. package/dist/{context-CNX8VDIJ.d.mts → context-Bgvuxv1C.d.mts} +1 -1
  20. package/dist/{context-CHpyLX8W.mjs → context-CNq7GLeT.mjs} +2 -2
  21. package/dist/{context-BvaYawdU.d.mts → context-DhycZ2lx.d.mts} +14 -0
  22. package/dist/{dev-CInztvNK.mjs → dev-BViAfnlw.mjs} +4 -4
  23. package/dist/{events-BV4Dj59a.mjs → events-0wDwalmL.mjs} +1 -0
  24. package/dist/handler-DmaAfpGq.mjs +110 -0
  25. package/dist/{host-agent-mIbQ6Qi5.mjs → host-agent-CJu735ch.mjs} +1 -1
  26. package/dist/in-page-channel/index.d.mts +1 -1
  27. package/dist/in-page-channel/index.mjs +1 -1
  28. package/dist/{index-LtvVaxSc.d.mts → index-DFPsvDGu.d.mts} +2 -2
  29. package/dist/index.d.mts +1 -1
  30. package/dist/{instance-shell-C3VrwJRS.mjs → instance-shell-DmyVIJzj.mjs} +5 -5
  31. package/dist/{instance-shell-CM2nruNe.d.mts → instance-shell-fl3ir6RC.d.mts} +2 -2
  32. package/dist/{interactive-auth-BCa_iamv.mjs → interactive-auth-FFOGnYIC.mjs} +1 -1
  33. package/dist/internal/index.d.mts +5 -5
  34. package/dist/internal/index.mjs +5 -5
  35. package/dist/node/auth.d.mts +2 -2
  36. package/dist/node/auth.mjs +1 -1
  37. package/dist/node/hub-internals.d.mts +2 -2
  38. package/dist/node/hub-internals.mjs +2 -2
  39. package/dist/node/index.d.mts +1 -1
  40. package/dist/node/index.mjs +2 -2
  41. package/dist/recipes/interactive-auth.d.mts +2 -2
  42. package/dist/recipes/interactive-auth.mjs +1 -1
  43. package/dist/{revoke-cqztiCX4.mjs → revoke-CeQUo6KI.mjs} +1 -1
  44. package/dist/rpc/dump.mjs +1 -1
  45. package/dist/rpc/index.mjs +2 -1
  46. package/dist/rpc/transports/ws-bun.d.mts +1 -1
  47. package/dist/rpc/transports/ws-deno.d.mts +1 -1
  48. package/dist/{rpc-core-CZ72-_vD.d.mts → rpc-core-BcThIWzY.d.mts} +2 -2
  49. package/dist/{rpc-core-BXyMjbKG.mjs → rpc-core-CXgALhUi.mjs} +13 -9
  50. package/dist/{tool-input-BAQXvvqL.mjs → tool-input-CES3Pc4y.mjs} +1 -0
  51. package/dist/types/index.d.mts +1 -1
  52. package/dist/utils/events.d.mts +1 -1
  53. package/dist/utils/open.mjs +1 -1
  54. package/dist/utils/serve-static.mjs +1 -1
  55. package/dist/utils/shared-state.d.mts +1 -1
  56. package/dist/utils/streaming-channel.d.mts +1 -1
  57. package/dist/{validation-BzxTTYaV.mjs → validation-CMfDAti3.mjs} +1 -109
  58. package/dist/{write-BWIO0h0Z.mjs → write-DK7rBUN3.mjs} +2 -1
  59. package/package.json +3 -3
  60. package/dist/{instance-registry-C-nC0By7.mjs → instance-registry-DFrZdU4B.mjs} +1 -1
  61. package/dist/{storage-CHD1IkqU.mjs → storage-DPoitS2X.mjs} +1 -1
@@ -143,6 +143,7 @@ function resolveMcpConfig(mcp) {
143
143
  return {
144
144
  ...mcp.path !== void 0 ? { path: mcp.path } : {},
145
145
  ...mcp.allowedOrigins !== void 0 ? { allowedOrigins: mcp.allowedOrigins } : {},
146
+ ...mcp.exposeSharedState !== void 0 ? { exposeSharedState: mcp.exposeSharedState } : {},
146
147
  authorization
147
148
  };
148
149
  }
@@ -1,5 +1,5 @@
1
- import { B as DevframeDefinition, Q as McpSetting, V as DevframeDeploymentKind, Wt as DevframeAgentHost, X as McpAuthorization, t as ConnectionMeta } from "./context-BvaYawdU.mjs";
2
- import { t as AgenticMcpModule } from "./agentic-B8y_r5tQ.mjs";
1
+ import { B as DevframeDefinition, Q as McpSetting, V as DevframeDeploymentKind, Wt as DevframeAgentHost, X as McpAuthorization, t as ConnectionMeta } from "./context-DhycZ2lx.mjs";
2
+ import { t as AgenticMcpModule } from "./agentic-D61fXLzA.mjs";
3
3
  //#region src/adapters/_shared.d.ts
4
4
  /**
5
5
  * Resolve the mount base path for a devframe's SPA. Hosted adapters
@@ -35,6 +35,8 @@ interface ResolvedMcpConfig {
35
35
  path?: string;
36
36
  /** Origin allow-list, or `false` to disable the origin gate. */
37
37
  allowedOrigins?: readonly string[] | false;
38
+ /** Shared-state exposure from the route options; omitted means every key. */
39
+ exposeSharedState?: boolean | ((key: string) => boolean);
38
40
  /** The resolved identity policy: a bearer token, callback, or `false`. */
39
41
  authorization: McpAuthorization;
40
42
  }
@@ -1,4 +1,4 @@
1
- import { B as DevframeDefinition, K as DevframeSnapshotRpcEntry, o as DevframeNodeContext } from "../context-BvaYawdU.mjs";
1
+ import { B as DevframeDefinition, K as DevframeSnapshotRpcEntry, o as DevframeNodeContext } from "../context-DhycZ2lx.mjs";
2
2
  import { o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
3
3
  //#region src/adapters/build.d.ts
4
4
  export interface CreateBuildOptions {
@@ -1,14 +1,14 @@
1
1
  import { s as colors } from "../nostics-D0PvLZsn.mjs";
2
2
  import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME } from "../constants.mjs";
3
- import { n as collectStaticRpcDump, t as writeStaticRpcDump } from "../write-BWIO0h0Z.mjs";
3
+ import { n as collectStaticRpcDump, t as writeStaticRpcDump } from "../write-DK7rBUN3.mjs";
4
4
  import { t as diagnostics } from "../diagnostics-Cx4_VjlG.mjs";
5
- import { t as createHostContext } from "../context-BqpgC5tq.mjs";
5
+ import { t as createHostContext } from "../context-BKw9KkCR.mjs";
6
6
  import { t as resolveStaticAssetsSource } from "../remote-assets-B9MeWmW6.mjs";
7
7
  import { t as createH3DevframeHost } from "../host-h3-BROI6SQ4.mjs";
8
8
  import fs from "node:fs/promises";
9
9
  import { resolve } from "pathe";
10
- import process from "node:process";
11
10
  import { existsSync } from "node:fs";
11
+ import process from "node:process";
12
12
  //#region src/adapters/build.ts
13
13
  /**
14
14
  * Produce a self-contained static deploy of a devframe:
@@ -1,4 +1,4 @@
1
- import { B as DevframeDefinition, Mt as InferCliFlags, Nt as defineCliFlags, Pt as parseCliFlags, Q as McpSetting, jt as CliFlagsSchema } from "../context-BvaYawdU.mjs";
1
+ import { B as DevframeDefinition, Mt as InferCliFlags, Nt as defineCliFlags, Pt as parseCliFlags, Q as McpSetting, jt as CliFlagsSchema } from "../context-DhycZ2lx.mjs";
2
2
  import { CAC } from "cac";
3
3
  import { H3 } from "h3";
4
4
  //#region src/adapters/cac.d.ts
@@ -1,7 +1,7 @@
1
1
  import { s as colors } from "../nostics-D0PvLZsn.mjs";
2
- import { i as resolveDevServerPort, s as importAgenticMcp } from "../_shared-B2PZsp-J.mjs";
2
+ import { i as resolveDevServerPort, s as importAgenticMcp } from "../_shared-DlRoV7v3.mjs";
3
3
  import { createBuild } from "./build.mjs";
4
- import { t as createDevServer } from "../dev-CInztvNK.mjs";
4
+ import { t as createDevServer } from "../dev-BViAfnlw.mjs";
5
5
  import process from "node:process";
6
6
  import cac$1 from "cac";
7
7
  //#region src/adapters/flags.ts
@@ -1,9 +1,9 @@
1
- import { At as DevframeAuthHandler, B as DevframeDefinition, J as DevframeSseOptions, Q as McpSetting, Y as DevframeWsOptions, s as DevframeNodeRpcSession } from "../context-BvaYawdU.mjs";
1
+ import { At as DevframeAuthHandler, B as DevframeDefinition, J as DevframeSseOptions, Q as McpSetting, Y as DevframeWsOptions, s as DevframeNodeRpcSession } from "../context-DhycZ2lx.mjs";
2
2
  import { n as DevframeRpcConnection, t as DevframeNodeRpcSessionMeta } from "../session-r6PDixP6.mjs";
3
3
  import { o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
4
4
  import { n as WsOriginRegistry } from "../ws-server-y2R76aSW.mjs";
5
- import { c as StartedServer } from "../instance-shell-CM2nruNe.mjs";
6
- import { c as resolveMcpConnectionMeta, o as resolveDevServerPort, t as ResolveDevServerPortOptions } from "../_shared-DjkowZKP.mjs";
5
+ import { c as StartedServer } from "../instance-shell-fl3ir6RC.mjs";
6
+ import { c as resolveMcpConnectionMeta, o as resolveDevServerPort, t as ResolveDevServerPortOptions } from "../_shared-NSl4p_om.mjs";
7
7
  import { H3 } from "h3";
8
8
  //#region src/adapters/dev.d.ts
9
9
  export interface CreateDevServerOptions {
@@ -1,3 +1,3 @@
1
- import { i as resolveDevServerPort, o as resolveMcpConnectionMeta } from "../_shared-B2PZsp-J.mjs";
2
- import { t as createDevServer } from "../dev-CInztvNK.mjs";
1
+ import { i as resolveDevServerPort, o as resolveMcpConnectionMeta } from "../_shared-DlRoV7v3.mjs";
2
+ import { t as createDevServer } from "../dev-BViAfnlw.mjs";
3
3
  export { createDevServer, resolveDevServerPort, resolveMcpConnectionMeta };
@@ -1,4 +1,4 @@
1
- import { B as DevframeDefinition, o as DevframeNodeContext } from "../context-BvaYawdU.mjs";
1
+ import { B as DevframeDefinition, o as DevframeNodeContext } from "../context-DhycZ2lx.mjs";
2
2
  //#region src/adapters/embedded.d.ts
3
3
  export interface CreateEmbeddedOptions {
4
4
  /** Target context the devframe is registered into. Required. */
@@ -1,8 +1,8 @@
1
- import { At as DevframeAuthHandler, B as DevframeDefinition, Dt as DevframeStorageScope, J as DevframeSseOptions, Q as McpSetting, Y as DevframeWsOptions, o as DevframeNodeContext, s as DevframeNodeRpcSession, t as ConnectionMeta } from "../context-BvaYawdU.mjs";
1
+ import { At as DevframeAuthHandler, B as DevframeDefinition, Dt as DevframeStorageScope, J as DevframeSseOptions, Q as McpSetting, Y as DevframeWsOptions, o as DevframeNodeContext, s as DevframeNodeRpcSession, t as ConnectionMeta } from "../context-DhycZ2lx.mjs";
2
2
  import { n as DevframeRpcConnection, t as DevframeNodeRpcSessionMeta } from "../session-r6PDixP6.mjs";
3
3
  import { o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
4
4
  import { n as WsOriginRegistry } from "../ws-server-y2R76aSW.mjs";
5
- import { c as StartedServer, f as DevframeInstanceRecord } from "../instance-shell-CM2nruNe.mjs";
5
+ import { c as StartedServer, f as DevframeInstanceRecord } from "../instance-shell-fl3ir6RC.mjs";
6
6
  import { H3 } from "h3";
7
7
  import { Buffer } from "node:buffer";
8
8
  import { IncomingMessage, Server, ServerResponse } from "node:http";
@@ -1,2 +1,2 @@
1
- import { n as getInstanceInternals, r as initDevframe } from "../dev-CInztvNK.mjs";
1
+ import { n as getInstanceInternals, r as initDevframe } from "../dev-BViAfnlw.mjs";
2
2
  export { getInstanceInternals, initDevframe };
@@ -1,4 +1,4 @@
1
- import { F as CreateMcpServerOptions, I as McpConnectionInfo, L as McpFetchHandler, P as CreateMcpFetchHandlerOptions, R as McpServerHandle } from "../context-BvaYawdU.mjs";
2
- import { n as MountMcpHttpOptions, r as MountedMcpHttp } from "../agentic-B8y_r5tQ.mjs";
1
+ import { F as CreateMcpServerOptions, I as McpConnectionInfo, L as McpFetchHandler, P as CreateMcpFetchHandlerOptions, R as McpServerHandle } from "../context-DhycZ2lx.mjs";
2
+ import { n as MountMcpHttpOptions, r as MountedMcpHttp } from "../agentic-D61fXLzA.mjs";
3
3
  import { createMcpFetchHandler, createMcpServer, mountMcpHttp } from "@devframes/agentic/mcp";
4
4
  export { type CreateMcpFetchHandlerOptions, type CreateMcpServerOptions, type McpConnectionInfo, type McpFetchHandler, type McpServerHandle, type MountMcpHttpOptions, type MountedMcpHttp, createMcpFetchHandler, createMcpServer, mountMcpHttp };
@@ -1,4 +1,4 @@
1
- import { B as DevframeDefinition, F as CreateMcpServerOptions, L as McpFetchHandler, P as CreateMcpFetchHandlerOptions, R as McpServerHandle, o as DevframeNodeContext } from "./context-BvaYawdU.mjs";
1
+ import { B as DevframeDefinition, F as CreateMcpServerOptions, L as McpFetchHandler, P as CreateMcpFetchHandlerOptions, R as McpServerHandle, o as DevframeNodeContext } from "./context-DhycZ2lx.mjs";
2
2
  import { H3 } from "h3";
3
3
  //#region src/node/agentic.d.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import { i as BirpcReturn, r as BirpcOptions } from "../index-Bia1vKjL.mjs";
2
2
  import { f as RpcCacheManager, p as RpcCacheOptions } from "../index-DuEm0sDH.mjs";
3
3
  import { h as RpcFunctionDefinition, w as RpcFunctionsCollector } from "../types-BmDbfHCx.mjs";
4
- import { Ct as DevframeRpcClientFunctions, Kt as EventEmitter, at as DevframeServiceScopeOf, b as StreamSink, bt as ScopedSharedStates, d as RpcSharedStateHost, k as SharedState, lt as DevframeServicesState, pt as DevframeSettings, rt as DevframeServiceMeta, t as ConnectionMeta, u as RpcSharedStateGetOptions, vt as ScopedRpcFn, wt as DevframeRpcServerFunctions, xt as SettingsForNamespace, y as StreamReader, yt as ScopedServerFunctions } from "../context-BvaYawdU.mjs";
4
+ import { Ct as DevframeRpcClientFunctions, Kt as EventEmitter, at as DevframeServiceScopeOf, b as StreamSink, bt as ScopedSharedStates, d as RpcSharedStateHost, k as SharedState, lt as DevframeServicesState, pt as DevframeSettings, rt as DevframeServiceMeta, t as ConnectionMeta, u as RpcSharedStateGetOptions, vt as ScopedRpcFn, wt as DevframeRpcServerFunctions, xt as SettingsForNamespace, y as StreamReader, yt as ScopedServerFunctions } from "../context-DhycZ2lx.mjs";
5
5
  import { t as SseRpcChannelOptions } from "../sse-client-DNdscfu_.mjs";
6
6
  import { t as WsRpcChannelOptions } from "../ws-client-6Mrz6eC4.mjs";
7
7
  //#region src/client/connection.d.ts
@@ -310,6 +310,7 @@ export interface DevframeRpcClientOptions extends SetupDevframeConnectionOptions
310
310
  /** Channel overrides for the SSE transport, the `wsOptions` counterpart. */
311
311
  sseOptions?: Partial<SseRpcChannelOptions>;
312
312
  rpcOptions?: Partial<BirpcOptions<DevframeRpcServerFunctions, DevframeRpcClientFunctions, boolean>>;
313
+ /** Cache declared static/opted-in query methods with `true`, or select an explicit function list. */
313
314
  cacheOptions?: boolean | Partial<RpcCacheOptions>;
314
315
  /**
315
316
  * Mirror `agent`-flagged client RPC functions (functions registered on
@@ -1,4 +1,4 @@
1
- import { a as defineDiagnostics, i as createBirpc, n as argsToJsonSchema, o as DEVFRAME_EVENTS, r as resolveAgentSafety, t as toolInputToRpcArgs } from "../tool-input-BAQXvvqL.mjs";
1
+ import { a as defineDiagnostics, i as createBirpc, n as argsToJsonSchema, o as DEVFRAME_EVENTS, r as resolveAgentSafety, t as toolInputToRpcArgs } from "../tool-input-CES3Pc4y.mjs";
2
2
  import { t as hash } from "../hash-DdPnc4k3.mjs";
3
3
  import { createEventEmitter } from "../utils/events.mjs";
4
4
  import { withBase, withProtocol } from "../utils/url.mjs";
@@ -738,7 +738,7 @@ function createRpcSharedStateClientHost(rpc) {
738
738
  return state;
739
739
  }
740
740
  }
741
- return new Promise((resolve) => {
741
+ return new Promise((resolve, reject) => {
742
742
  if (!rpc.isTrusted) {
743
743
  resolve(state);
744
744
  let initialized = false;
@@ -748,7 +748,7 @@ function createRpcSharedStateClientHost(rpc) {
748
748
  initSharedState();
749
749
  }
750
750
  });
751
- } else initSharedState().then(resolve);
751
+ } else initSharedState().then(resolve, reject);
752
752
  });
753
753
  }
754
754
  };
@@ -1599,23 +1599,24 @@ function qualifyName(namespace, name) {
1599
1599
  function createSettingsStore(store) {
1600
1600
  return {
1601
1601
  async get(key) {
1602
- return (await store()).value()[key];
1602
+ return (await store()).value()?.[key];
1603
1603
  },
1604
1604
  async set(key, value) {
1605
1605
  (await store()).mutate((draft) => {
1606
+ if (draft === void 0) return { [key]: value };
1606
1607
  draft[key] = value;
1607
1608
  });
1608
1609
  },
1609
1610
  async delete(key) {
1610
1611
  (await store()).mutate((draft) => {
1611
- delete draft[key];
1612
+ if (draft !== void 0) delete draft[key];
1612
1613
  });
1613
1614
  },
1614
1615
  async all() {
1615
- return (await store()).value();
1616
+ return (await store()).value() ?? {};
1616
1617
  },
1617
1618
  async onChange(fn) {
1618
- return (await store()).on("updated", (full) => fn(full));
1619
+ return (await store()).on("updated", (full) => fn(full ?? {}));
1619
1620
  }
1620
1621
  };
1621
1622
  }
@@ -1625,7 +1626,13 @@ function createClientSettingsStore(rpc, namespace, scope) {
1625
1626
  const stateKey = `devframe:settings:${scope}:${namespace}`;
1626
1627
  let statePromise;
1627
1628
  function store() {
1628
- if (!statePromise) statePromise = rpc.sharedState.get(stateKey, { initialValue: {} });
1629
+ if (!statePromise) statePromise = (async () => {
1630
+ await rpc.ensureTrusted();
1631
+ return rpc.sharedState.get(stateKey);
1632
+ })().catch((error) => {
1633
+ statePromise = void 0;
1634
+ throw error;
1635
+ });
1629
1636
  return statePromise;
1630
1637
  }
1631
1638
  return createSettingsStore(store);
@@ -1828,6 +1835,23 @@ async function getDevframeRpcClient(options = {}) {
1828
1835
  const disposeWebMcp = options.webmcp === false ? void 0 : registerWebMcpTools(clientRpc);
1829
1836
  let disposeBrowserAgentBridge;
1830
1837
  let closed = false;
1838
+ let cacheGeneration = 0;
1839
+ let cacheFunctions;
1840
+ function invalidateCache() {
1841
+ cacheGeneration++;
1842
+ cacheFunctions = void 0;
1843
+ cacheManager.clear();
1844
+ if (cacheOptions === true) cacheManager.updateOptions({ functions: [] });
1845
+ }
1846
+ if (cacheOptions) {
1847
+ clientRpc.register({
1848
+ name: DEVFRAME_EVENTS.broadcast.cacheInvalidate,
1849
+ type: "event",
1850
+ handler: invalidateCache
1851
+ });
1852
+ events.on(DEVFRAME_EVENTS.client.connectionStatus, invalidateCache);
1853
+ events.on(DEVFRAME_EVENTS.client.isTrustedUpdated, invalidateCache);
1854
+ }
1831
1855
  async function fetchJsonFromBases(path) {
1832
1856
  const candidates = [resolvedBaseURL, ...bases.filter((base) => base !== resolvedBaseURL)].filter((x) => x != null);
1833
1857
  const errors = [];
@@ -1841,6 +1865,9 @@ async function getDevframeRpcClient(options = {}) {
1841
1865
  }
1842
1866
  throw new Error(`Failed to load ${path} from ${candidates.join(", ")}`, { cause: errors });
1843
1867
  }
1868
+ const timeout = options.callTimeout ?? 0;
1869
+ const requestBudget = timeout > 0 ? timeout : Infinity;
1870
+ let mode;
1844
1871
  const liveModeOptions = {
1845
1872
  authToken,
1846
1873
  connectionMeta,
@@ -1851,17 +1878,38 @@ async function getDevframeRpcClient(options = {}) {
1851
1878
  rpcOptions: {
1852
1879
  ...rpcOptions,
1853
1880
  async onRequest(req, next, resolve) {
1881
+ const deadline = performance.now() + requestBudget;
1854
1882
  await rpcOptions.onRequest?.call(this, req, next, resolve);
1855
- if (cacheOptions && cacheManager?.validate(req.m)) {
1883
+ if (cacheOptions === true && req.i && mode.isTrusted && !req.m.startsWith("anonymous:") && req.m !== "devframe:rpc:cacheable-functions") {
1884
+ function checkDeadline() {
1885
+ if (performance.now() >= deadline) throw new DevframeConnectionError("timeout", `[devframe] RPC call "${req.m}" timed out after ${timeout}ms`);
1886
+ }
1887
+ while (!cacheFunctions) {
1888
+ checkDeadline();
1889
+ if (closed || mode.status !== "connected") break;
1890
+ const generation = cacheGeneration;
1891
+ cacheFunctions = mode.callOptional("devframe:rpc:cacheable-functions").then((functions) => {
1892
+ if (generation === cacheGeneration) cacheManager.updateOptions({ functions: functions ?? [] });
1893
+ }).catch((error) => {
1894
+ if (generation === cacheGeneration) cacheFunctions = void 0;
1895
+ throw error;
1896
+ });
1897
+ await cacheFunctions;
1898
+ }
1899
+ await cacheFunctions;
1900
+ checkDeadline();
1901
+ }
1902
+ const generation = cacheGeneration;
1903
+ if (cacheOptions && req.i && !closed && mode.isTrusted && cacheManager.validate(req.m)) {
1856
1904
  if (cacheManager.has(req.m, req.a)) return resolve(cacheManager.cached(req.m, req.a));
1857
1905
  const res = await next(req);
1858
- cacheManager.apply(req, res);
1906
+ if (generation === cacheGeneration && !closed) cacheManager.apply(req, res);
1859
1907
  } else await next(req);
1860
1908
  }
1861
1909
  }
1862
1910
  };
1863
1911
  const transport = resolveClientTransport(options.transport ?? "auto", connectionMeta);
1864
- const mode = transport === "static" ? await createStaticRpcClientMode({ fetchJsonFromBases }) : transport === "sse" ? createSseRpcClientMode({
1912
+ mode = transport === "static" ? await createStaticRpcClientMode({ fetchJsonFromBases }) : transport === "sse" ? createSseRpcClientMode({
1865
1913
  ...liveModeOptions,
1866
1914
  sseOptions: options.sseOptions
1867
1915
  }) : createWsRpcClientMode({
@@ -1883,6 +1931,7 @@ async function getDevframeRpcClient(options = {}) {
1883
1931
  /** Release authentication and transport resources even if another disposer fails. */
1884
1932
  function closeRpcClient() {
1885
1933
  closed = true;
1934
+ invalidateCache();
1886
1935
  try {
1887
1936
  disposeBrowserAgentBridge?.();
1888
1937
  disposeWebMcp?.();
@@ -52,6 +52,7 @@ export declare const DEVFRAME_EVENTS: {
52
52
  */
53
53
  readonly broadcast: {
54
54
  readonly authRevoked: "devframe:auth:revoked";
55
+ readonly cacheInvalidate: "devframe:rpc:cache:invalidate";
55
56
  readonly clientStateUpdated: "devframe:rpc:client-state:updated";
56
57
  readonly clientStatePatch: "devframe:rpc:client-state:patch";
57
58
  readonly streamingChunk: "devframe:streaming:chunk";
@@ -1,4 +1,4 @@
1
- import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
1
+ import { t as DEVFRAME_EVENTS } from "./events-0wDwalmL.mjs";
2
2
  //#region src/constants.ts
3
3
  /** Devframe runtime routes and static output conventions. */
4
4
  const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
@@ -2,17 +2,17 @@ import { i as defineDiagnostics } from "./nostics-D0PvLZsn.mjs";
2
2
  import { r as diagnostics } from "./serialization-BhuOoL80.mjs";
3
3
  import { RpcFunctionsCollectorBase } from "./rpc/index.mjs";
4
4
  import { defineRpcFunction } from "./index.mjs";
5
- import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
5
+ import { t as DEVFRAME_EVENTS } from "./events-0wDwalmL.mjs";
6
6
  import { DEVFRAME_SERVICES_STATE_KEY } from "./constants.mjs";
7
7
  import { t as diagnostics$1 } from "./diagnostics-Cx4_VjlG.mjs";
8
- import { r as createEventEmitter, t as DevframeAgentHost } from "./host-agent-mIbQ6Qi5.mjs";
8
+ import { r as createEventEmitter, t as DevframeAgentHost } from "./host-agent-CJu735ch.mjs";
9
9
  import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-B9MeWmW6.mjs";
10
+ import { t as createStorage } from "./storage-DPoitS2X.mjs";
10
11
  import { a as satisfiesVersionRange, n as expandResolveFrom, r as importServicePackage, t as deepMergeOptionSets } from "./services-install-D5YUwFXW.mjs";
11
- import { t as createStorage } from "./storage-CHD1IkqU.mjs";
12
- import { join } from "pathe";
12
+ import { isAbsolute, join, relative } from "pathe";
13
13
  import { createSharedState } from "devframe/utils/shared-state";
14
- import process from "node:process";
15
14
  import { existsSync } from "node:fs";
15
+ import process from "node:process";
16
16
  //#region src/node/host-diagnostics.ts
17
17
  var DevframeDiagnosticsHost = class {
18
18
  context;
@@ -75,7 +75,7 @@ function removeClientAgentSession(context, meta) {
75
75
  //#region src/node/rpc-shared-state.ts
76
76
  const debug$2 = createDebug("devframe:rpc:state:changed");
77
77
  const debugSubscribe = createDebug("devframe:rpc:state:subscribe");
78
- function createRpcSharedStateServerHost(rpc) {
78
+ function createRpcSharedStateServerHost(rpc, resolveState) {
79
79
  const sharedState = /* @__PURE__ */ new Map();
80
80
  const stateDisposers = /* @__PURE__ */ new Map();
81
81
  const keyAddedListeners = /* @__PURE__ */ new Set();
@@ -116,19 +116,28 @@ function createRpcSharedStateServerHost(rpc) {
116
116
  for (const off of offs) off();
117
117
  };
118
118
  }
119
+ function addState(key, state) {
120
+ debug$2("new-state", key);
121
+ stateDisposers.set(key, registerSharedState(key, state));
122
+ sharedState.set(key, state);
123
+ for (const fn of keyAddedListeners) fn(key);
124
+ return state;
125
+ }
126
+ function resolve(key) {
127
+ const existing = sharedState.get(key);
128
+ if (existing) return existing;
129
+ const state = resolveState?.(key);
130
+ return state ? addState(key, state) : void 0;
131
+ }
119
132
  const host = {
120
133
  get: async (key, options) => {
121
- if (sharedState.has(key)) return sharedState.get(key);
134
+ const existing = resolve(key);
135
+ if (existing) return existing;
122
136
  if (options?.initialValue === void 0 && options?.sharedState === void 0) throw diagnostics$1.DF0013({ key });
123
- debug$2("new-state", key);
124
- const state = options.sharedState ?? createSharedState({
137
+ return addState(key, options.sharedState ?? createSharedState({
125
138
  initialValue: options.initialValue,
126
139
  enablePatches: false
127
- });
128
- stateDisposers.set(key, registerSharedState(key, state));
129
- sharedState.set(key, state);
130
- for (const fn of keyAddedListeners) fn(key);
131
- return state;
140
+ }));
132
141
  },
133
142
  keys() {
134
143
  return Array.from(sharedState.keys());
@@ -165,8 +174,7 @@ function createRpcSharedStateServerHost(rpc) {
165
174
  name: "devframe:rpc:server-state:get",
166
175
  type: "query",
167
176
  handler: async (key) => {
168
- if (!sharedState.has(key)) return void 0;
169
- return (await host.get(key)).value();
177
+ return resolve(key)?.value();
170
178
  },
171
179
  /**
172
180
  * Pre-compute snapshots for the build-mode static dump so the SPA
@@ -194,8 +202,7 @@ function createRpcSharedStateServerHost(rpc) {
194
202
  name: "devframe:rpc:server-state:patch",
195
203
  type: "query",
196
204
  handler: async (key, patches, syncId) => {
197
- if (!sharedState.has(key)) return;
198
- (await host.get(key)).patch(patches, syncId);
205
+ resolve(key)?.patch(patches, syncId);
199
206
  }
200
207
  });
201
208
  return host;
@@ -771,6 +778,79 @@ function createRpcStreamingServerHost(rpc) {
771
778
  };
772
779
  }
773
780
  //#endregion
781
+ //#region src/settings-store.ts
782
+ /**
783
+ * The key-value store surface over a lazily-resolved shared state, shared by
784
+ * the node-side (file-backed) and client-side (RPC-mirrored) settings stores.
785
+ * Each side supplies its own `store()` resolver; the read/write/subscribe
786
+ * behavior on top of it is identical.
787
+ */
788
+ function createSettingsStore(store) {
789
+ return {
790
+ async get(key) {
791
+ return (await store()).value()?.[key];
792
+ },
793
+ async set(key, value) {
794
+ (await store()).mutate((draft) => {
795
+ if (draft === void 0) return { [key]: value };
796
+ draft[key] = value;
797
+ });
798
+ },
799
+ async delete(key) {
800
+ (await store()).mutate((draft) => {
801
+ if (draft !== void 0) delete draft[key];
802
+ });
803
+ },
804
+ async all() {
805
+ return (await store()).value() ?? {};
806
+ },
807
+ async onChange(fn) {
808
+ return (await store()).on("updated", (full) => fn(full ?? {}));
809
+ }
810
+ };
811
+ }
812
+ //#endregion
813
+ //#region src/node/settings.ts
814
+ const STORAGE_SCOPE = {
815
+ global: "global",
816
+ project: "project"
817
+ };
818
+ /** Resolve reserved settings keys before the sync protocol creates an in-memory state. */
819
+ function resolveSettingsState(context, key) {
820
+ const match = /^devframe:settings:(global|project):(.+)$/.exec(key);
821
+ if (!match) return;
822
+ const scope = match[1];
823
+ const namespace = match[2];
824
+ const dir = join(context.host.getStorageDir(STORAGE_SCOPE[scope]), "settings");
825
+ const filepath = join(dir, `${namespace}.json`);
826
+ const path = relative(dir, filepath);
827
+ if (namespace.includes("\0") || path === ".." || path.startsWith("../") || isAbsolute(path)) return;
828
+ return createStorage({
829
+ filepath,
830
+ initialValue: {}
831
+ });
832
+ }
833
+ function createNodeSettingsStore(context, namespace, scope) {
834
+ const stateKey = `devframe:settings:${scope}:${namespace}`;
835
+ let statePromise;
836
+ function store() {
837
+ if (!statePromise) statePromise = context.rpc.sharedState.get(stateKey);
838
+ return statePromise;
839
+ }
840
+ return createSettingsStore(store);
841
+ }
842
+ /**
843
+ * Build the node-side `settings` surface for a scope namespace. `project`
844
+ * persists under the host's `project` storage dir, `global` under its
845
+ * `global` dir. Each is a file-backed, client-synced key-value store.
846
+ */
847
+ function createNodeSettings(context, namespace) {
848
+ return {
849
+ global: createNodeSettingsStore(context, namespace, "global"),
850
+ project: createNodeSettingsStore(context, namespace, "project")
851
+ };
852
+ }
853
+ //#endregion
774
854
  //#region src/node/host-functions.ts
775
855
  const debugBroadcast = createDebug("devframe:rpc:broadcast");
776
856
  /**
@@ -790,8 +870,19 @@ var RpcFunctionsHostImpl = class extends RpcFunctionsCollectorBase {
790
870
  _asyncStorage = void 0;
791
871
  constructor(context) {
792
872
  super(context);
793
- this.sharedState = createRpcSharedStateServerHost(this);
873
+ this.sharedState = createRpcSharedStateServerHost(this, (key) => resolveSettingsState(context, key));
794
874
  this.streaming = createRpcStreamingServerHost(this);
875
+ this.register(defineRpcFunction({
876
+ name: "devframe:rpc:cacheable-functions",
877
+ type: "query",
878
+ handler: () => [...this.definitions.values()].filter((fn) => fn.type === "static" || fn.type === "query" && fn.cacheable === true).map((fn) => fn.name)
879
+ }));
880
+ this.onChanged(() => {
881
+ this.broadcast({
882
+ method: DEVFRAME_EVENTS.broadcast.cacheInvalidate,
883
+ args: []
884
+ });
885
+ });
795
886
  }
796
887
  sharedState;
797
888
  streaming;
@@ -1191,70 +1282,6 @@ function qualifyName(namespace, name) {
1191
1282
  return isQualifiedName(name) ? name : `${namespace}:${name}`;
1192
1283
  }
1193
1284
  //#endregion
1194
- //#region src/settings-store.ts
1195
- /**
1196
- * The key-value store surface over a lazily-resolved shared state, shared by
1197
- * the node-side (file-backed) and client-side (RPC-mirrored) settings stores.
1198
- * Each side supplies its own `store()` resolver; the read/write/subscribe
1199
- * behavior on top of it is identical.
1200
- */
1201
- function createSettingsStore(store) {
1202
- return {
1203
- async get(key) {
1204
- return (await store()).value()[key];
1205
- },
1206
- async set(key, value) {
1207
- (await store()).mutate((draft) => {
1208
- draft[key] = value;
1209
- });
1210
- },
1211
- async delete(key) {
1212
- (await store()).mutate((draft) => {
1213
- delete draft[key];
1214
- });
1215
- },
1216
- async all() {
1217
- return (await store()).value();
1218
- },
1219
- async onChange(fn) {
1220
- return (await store()).on("updated", (full) => fn(full));
1221
- }
1222
- };
1223
- }
1224
- //#endregion
1225
- //#region src/node/settings.ts
1226
- const STORAGE_SCOPE = {
1227
- global: "global",
1228
- project: "project"
1229
- };
1230
- function createNodeSettingsStore(context, namespace, scope) {
1231
- const stateKey = `devframe:settings:${scope}:${namespace}`;
1232
- let statePromise;
1233
- function store() {
1234
- if (!statePromise) {
1235
- const dir = context.host.getStorageDir(STORAGE_SCOPE[scope]);
1236
- const filepath = join(dir, "settings", `${namespace}.json`);
1237
- statePromise = context.rpc.sharedState.get(stateKey, { sharedState: createStorage({
1238
- filepath,
1239
- initialValue: {}
1240
- }) });
1241
- }
1242
- return statePromise;
1243
- }
1244
- return createSettingsStore(store);
1245
- }
1246
- /**
1247
- * Build the node-side `settings` surface for a scope namespace. `project`
1248
- * persists under the host's `workspace` storage dir, `global` under its
1249
- * `global` dir. Each is a file-backed, client-synced key-value store.
1250
- */
1251
- function createNodeSettings(context, namespace) {
1252
- return {
1253
- global: createNodeSettingsStore(context, namespace, "global"),
1254
- project: createNodeSettingsStore(context, namespace, "project")
1255
- };
1256
- }
1257
- //#endregion
1258
1285
  //#region src/node/scope.ts
1259
1286
  function prefixDefinition(namespace, fn) {
1260
1287
  if (isQualifiedName(fn.name)) throw diagnostics$1.DF0034({
@@ -1,4 +1,4 @@
1
- import { k as SharedState, o as DevframeNodeContext } from "./context-BvaYawdU.mjs";
1
+ import { k as SharedState, o as DevframeNodeContext } from "./context-DhycZ2lx.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -1,6 +1,6 @@
1
1
  import { n as randomToken } from "./crypto-token-TVbGGFuj.mjs";
2
- import { t as createStorage } from "./storage-CHD1IkqU.mjs";
3
- import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-cqztiCX4.mjs";
2
+ import { t as createStorage } from "./storage-DPoitS2X.mjs";
3
+ import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-CeQUo6KI.mjs";
4
4
  import { join } from "pathe";
5
5
  //#region src/node/hub-internals/context.ts
6
6
  const internalContextMap = /* @__PURE__ */ new WeakMap();
@@ -515,6 +515,8 @@ interface BrowserAgentToolManifest {
515
515
  * To be extended
516
516
  */
517
517
  interface DevframeRpcClientFunctions {
518
+ /** Clear cached RPC results and refresh cache eligibility. */
519
+ 'devframe:rpc:cache:invalidate': () => Promise<void>;
518
520
  /** Invoke a tool registered in this browser document. @internal */
519
521
  'devframe:agent:invoke-client-tool': (id: string, args: Record<string, unknown>) => Promise<unknown>;
520
522
  /**
@@ -568,6 +570,8 @@ interface DevframeRpcClientFunctions {
568
570
  * To be extended
569
571
  */
570
572
  interface DevframeRpcServerFunctions {
573
+ /** Methods eligible for automatic client caching. @internal */
574
+ 'devframe:rpc:cacheable-functions': () => Promise<string[]>;
571
575
  /** Replace this connection's browser-agent tool manifest, tagged with the calling tab's stable client id. @internal */
572
576
  'devframe:agent:sync-client-tools': (clientId: string, tools: BrowserAgentToolManifest[]) => Promise<void>;
573
577
  /**
@@ -1275,6 +1279,16 @@ interface McpRouteOptions {
1275
1279
  * prove identity too.
1276
1280
  */
1277
1281
  allowedOrigins?: readonly string[] | false;
1282
+ /**
1283
+ * Which shared-state keys the route exposes to agents, through the built-in
1284
+ * `devframe_state_read` tool and the `devframe://state/<key>` resources.
1285
+ * - `true` (default): every key the host publishes
1286
+ * - `false`: none
1287
+ * - `(key) => boolean`: only the keys the filter accepts
1288
+ *
1289
+ * The states themselves stay available to the UI over RPC.
1290
+ */
1291
+ exposeSharedState?: boolean | ((key: string) => boolean);
1278
1292
  }
1279
1293
  interface DevframeCliOptions {
1280
1294
  /** Binary name; default: the devframe's `id`. */
@@ -1,10 +1,10 @@
1
1
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
2
2
  import { t as diagnostics } from "./diagnostics-Cx4_VjlG.mjs";
3
- import { t as createHostContext } from "./context-BqpgC5tq.mjs";
3
+ import { t as createHostContext } from "./context-BKw9KkCR.mjs";
4
4
  import { t as resolveStaticAssetsSource } from "./remote-assets-B9MeWmW6.mjs";
5
- import { a as resolveMcpConfig, c as joinURL, d as withoutLeadingSlash, i as resolveDevServerPort, l as withBase, n as normalizeBasePath, r as resolveBasePath, s as importAgenticMcp, t as loadAutoMcpAdapter } from "./_shared-B2PZsp-J.mjs";
5
+ import { a as resolveMcpConfig, c as joinURL, d as withoutLeadingSlash, i as resolveDevServerPort, l as withBase, n as normalizeBasePath, r as resolveBasePath, s as importAgenticMcp, t as loadAutoMcpAdapter } from "./_shared-DlRoV7v3.mjs";
6
6
  import { t as createH3DevframeHost } from "./host-h3-BROI6SQ4.mjs";
7
- import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-C3VrwJRS.mjs";
7
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-DmyVIJzj.mjs";
8
8
  import { open } from "./utils/open.mjs";
9
9
  import { mountStaticHandler } from "./utils/serve-static.mjs";
10
10
  import { resolve } from "pathe";
@@ -139,7 +139,7 @@ async function mountMcpRoute(app, context, def, base, setting) {
139
139
  const mounted = module.mountMcpHttp(app, context, joinURL(base, route), {
140
140
  serverName: `${def.id} (devframe)`,
141
141
  serverVersion: def.version ?? "0.0.0",
142
- exposeSharedState: true,
142
+ exposeSharedState: config.exposeSharedState ?? true,
143
143
  authorization: config.authorization,
144
144
  allowedOrigins: config.allowedOrigins
145
145
  });