@burdenoff/fe-libs 2026.1027.8 → 2026.1027.10

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.
@@ -27,16 +27,22 @@ var d = ({ isOpen: d, onClose: f, shortcuts: p }) => {
27
27
  document.body.style.overflow = "";
28
28
  };
29
29
  }, [d]), !d) return null;
30
- let _ = p.reduce((e, t) => {
31
- let n = t.category || m("appShell.otherShortcuts", "Other");
32
- return e[n] || (e[n] = []), e[n].push(t), e;
33
- }, {}), v = Object.keys(_).sort((e, t) => {
30
+ let _ = /* @__PURE__ */ new Map();
31
+ for (let e of p) {
32
+ let t = e.category || m("appShell.otherShortcuts", "Other"), n = e.categoryKey ? m(e.categoryKey, t) : t, r = _.get(n);
33
+ r ? r.shortcuts.push(e) : _.set(n, {
34
+ category: t,
35
+ label: n,
36
+ shortcuts: [e]
37
+ });
38
+ }
39
+ let v = [..._.entries()].sort(([, e], [, t]) => {
34
40
  let n = {
35
41
  Navigation: 0,
36
42
  Actions: 1,
37
43
  UI: 2
38
- }, r = n[e] ?? 99, i = n[t] ?? 99;
39
- return r === i ? e.localeCompare(t) : r - i;
44
+ }, r = n[e.category] ?? 99, i = n[t.category] ?? 99;
45
+ return r === i ? e.category.localeCompare(t.category) : r - i;
40
46
  }), y = n(), b = y === "mac" ? "macOS" : y === "windows" ? "Windows" : "Linux";
41
47
  return /* @__PURE__ */ l(i, {
42
48
  modal: !0,
@@ -74,17 +80,17 @@ var d = ({ isOpen: d, onClose: f, shortcuts: p }) => {
74
80
  className: "flex-1 overflow-y-auto p-6",
75
81
  children: /* @__PURE__ */ l("div", {
76
82
  className: "grid grid-cols-1 md:grid-cols-2 gap-8",
77
- children: v.map((e) => /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h3", {
83
+ children: v.map(([e, t]) => /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h3", {
78
84
  className: "text-sm font-semibold text-text-primary mb-3",
79
- children: e
85
+ children: t.label
80
86
  }), /* @__PURE__ */ l("div", {
81
87
  className: "space-y-1",
82
- children: _[e].map((e, t) => /* @__PURE__ */ u("div", {
88
+ children: t.shortcuts.map((e, t) => /* @__PURE__ */ u("div", {
83
89
  className: "flex items-center justify-between py-2 px-3 rounded-button hover:bg-bg-sunken transition-all duration-200 hover:translate-x-0.5",
84
90
  style: { animationDelay: `${t * 30}ms` },
85
91
  children: [/* @__PURE__ */ l("span", {
86
92
  className: "text-text-primary text-sm",
87
- children: e.description
93
+ children: e.descriptionKey ? m(e.descriptionKey, e.description) : e.description
88
94
  }), /* @__PURE__ */ l("kbd", {
89
95
  className: "ml-4 px-2.5 py-1 bg-bg-sunken border border-border-subtle rounded text-text-secondary text-xs font-mono whitespace-nowrap transition-all duration-200 hover:scale-105",
90
96
  children: r(e.keys)
@@ -38,7 +38,20 @@ export interface ClientToolOffer {
38
38
  tools: readonly AnyDefinedAgentTool[];
39
39
  manifest: ClientToolManifestEntry[];
40
40
  }
41
- export declare function offerClientTools(tools?: readonly AnyDefinedAgentTool[]): ClientToolOffer;
41
+ export declare function offerClientTools(options: {
42
+ writeCapable: boolean;
43
+ }, tools?: readonly AnyDefinedAgentTool[]): ClientToolOffer;
44
+ /**
45
+ * Whether a mode that must not change data may still offer `tool`: it reads
46
+ * (`readOnly`) or only moves the person around the app (`navigation`).
47
+ *
48
+ * ★ BOFF-9908: Ask is promised read-only, so it is offered nothing else — not
49
+ * the confirmation-gated actions, not the canvas edits, and not the generic
50
+ * `ui_click` / `ui_fill` / `ui_select` / `ui_press_key`, which can press any
51
+ * ordinary button. The runner executes only offered tools, so leaving a tool
52
+ * out of the offer is what keeps the mode read-only.
53
+ */
54
+ export declare function isReadOnlyModeTool(tool: AnyDefinedAgentTool): boolean;
42
55
  /**
43
56
  * The tools registered right now, as the prompt's `clientTools`. Product tools
44
57
  * come first, then the shared ones (`app_`, `ui_`), so if the cap is reached
@@ -60,7 +73,7 @@ export declare const CLIENT_TOOL_ORPHAN_MS = 20000;
60
73
  /** The well-formed entries of an untrusted `clientToolRequests` value. */
61
74
  export declare function parseClientToolRequests(raw: unknown): ClientToolRequest[];
62
75
  export interface ClientToolRunnerOptions {
63
- /** The definitions this turn advertised (`offerClientTools().tools`). */
76
+ /** The definitions this turn advertised (`offerClientTools(...).tools`). */
64
77
  tools: readonly AnyDefinedAgentTool[];
65
78
  /** Post one result to the sandbox. Rejects when the post did not land. */
66
79
  post: (callId: string, result: AgentToolResult) => Promise<void>;
@@ -18,14 +18,17 @@ function c(e) {
18
18
  }
19
19
  };
20
20
  }
21
- function l(e = r()) {
22
- let t = u(e), n = new Set(t.map((e) => e.name));
21
+ function l(e, t = r()) {
22
+ let n = e.writeCapable ? t : t.filter(u), i = d(n), a = new Set(i.map((e) => e.name));
23
23
  return {
24
- tools: e.filter((e) => n.has(e.name)),
25
- manifest: t
24
+ tools: n.filter((e) => a.has(e.name)),
25
+ manifest: i
26
26
  };
27
27
  }
28
- function u(e = r()) {
28
+ function u(e) {
29
+ return e.annotations?.readOnly === !0 || e.annotations?.navigation === !0;
30
+ }
31
+ function d(e = r()) {
29
32
  let n = (e) => e.startsWith("app_") || e.startsWith("ui_"), i = [...e.filter((e) => !n(e.name)), ...e.filter((e) => n(e.name))], a = [], o = 2;
30
33
  for (let e of i) {
31
34
  let n = c(e), r = s.encode(JSON.stringify(n)).length + 1;
@@ -37,14 +40,14 @@ function u(e = r()) {
37
40
  }
38
41
  return a;
39
42
  }
40
- var d = /^ct_[0-9a-f]{32}$/, f = 4, p = 2e4;
41
- function m(e) {
43
+ var f = /^ct_[0-9a-f]{32}$/, p = 4, m = 2e4;
44
+ function h(e) {
42
45
  if (!Array.isArray(e)) return [];
43
46
  let t = [];
44
- for (let n of e.slice(0, f)) {
47
+ for (let n of e.slice(0, p)) {
45
48
  if (typeof n != "object" || !n || Array.isArray(n)) continue;
46
49
  let { callId: e, tool: r, input: i } = n;
47
- typeof e != "string" || !d.test(e) || typeof r != "string" || r.length === 0 || r.length > 64 || typeof i != "object" || !i || Array.isArray(i) || t.push({
50
+ typeof e != "string" || !f.test(e) || typeof r != "string" || r.length === 0 || r.length > 64 || typeof i != "object" || !i || Array.isArray(i) || t.push({
48
51
  callId: e,
49
52
  tool: r,
50
53
  input: i
@@ -52,7 +55,7 @@ function m(e) {
52
55
  }
53
56
  return t;
54
57
  }
55
- function h(r) {
58
+ function g(r) {
56
59
  let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set(), s = !1, c = r.now ?? Date.now, l = r.orphanMs ?? 2e4, u = c(), d, f = () => {
57
60
  d !== void 0 && clearInterval(d), d = void 0;
58
61
  }, p = () => {
@@ -63,7 +66,7 @@ function h(r) {
63
66
  f();
64
67
  }
65
68
  }, Math.max(1e3, Math.floor(l / 4))));
66
- }, h = () => {
69
+ }, m = () => {
67
70
  if (!r.onActivity) return;
68
71
  let e = [...a.keys()].map((e) => g.get(e)).filter((e) => typeof e == "string");
69
72
  r.onActivity(e.length > 0 ? e.join(", ") : null);
@@ -81,20 +84,20 @@ function h(r) {
81
84
  return;
82
85
  }
83
86
  let l = new AbortController();
84
- a.set(t.callId, l), g.set(t.callId, c.title), h(), p(), i(c, t.input, {
87
+ a.set(t.callId, l), g.set(t.callId, c.title), m(), p(), i(c, t.input, {
85
88
  initiator: "assistant",
86
89
  signal: l.signal
87
90
  }).then((e) => {
88
91
  if (!l.signal.aborted) return _(t.callId, e);
89
92
  }).finally(() => {
90
- a.delete(t.callId), g.delete(t.callId), h();
93
+ a.delete(t.callId), g.delete(t.callId), m();
91
94
  });
92
95
  };
93
96
  return {
94
97
  receive(e) {
95
98
  if (s) return;
96
99
  u = c();
97
- let t = m(e), n = new Set(t.map((e) => e.callId));
100
+ let t = h(e), n = new Set(t.map((e) => e.callId));
98
101
  for (let [e, t] of a) n.has(e) || t.abort();
99
102
  for (let e of t) o.has(e.callId) || v(e);
100
103
  },
@@ -107,4 +110,4 @@ function h(r) {
107
110
  };
108
111
  }
109
112
  //#endregion
110
- export { a as CLIENT_TOOL_MANIFEST_MAX_BYTES, o as CLIENT_TOOL_MANIFEST_MAX_TOOLS, p as CLIENT_TOOL_ORPHAN_MS, u as buildClientToolManifest, h as createClientToolRunner, l as offerClientTools, m as parseClientToolRequests };
113
+ export { a as CLIENT_TOOL_MANIFEST_MAX_BYTES, o as CLIENT_TOOL_MANIFEST_MAX_TOOLS, m as CLIENT_TOOL_ORPHAN_MS, d as buildClientToolManifest, g as createClientToolRunner, u as isReadOnlyModeTool, l as offerClientTools, h as parseClientToolRequests };
@@ -110,6 +110,7 @@ function p(e) {
110
110
  title: "Open a page",
111
111
  description: "Opens a page of the app in the person's tab. Pass a page label from app_list_pages (e.g. \"Runs\") or an in-app path starting with \"/\". Refused while the current page has unsaved changes.",
112
112
  input: i.strictObject({ destination: i.string().trim().min(1).max(200).describe("A page label from app_list_pages, or an in-app path starting with \"/\".") }),
113
+ annotations: { navigation: !0 },
113
114
  execute: ({ destination: t }) => {
114
115
  let r = e();
115
116
  d(r);
@@ -133,6 +134,7 @@ function p(e) {
133
134
  "dark",
134
135
  "system"
135
136
  ]).describe("The appearance to switch to.") }),
137
+ annotations: { navigation: !0 },
136
138
  execute: ({ mode: t }) => {
137
139
  let { setThemeMode: r } = e();
138
140
  if (!r) throw new n("NEEDS_USER", "This app does not let the theme be changed here.");
@@ -26,6 +26,13 @@ export interface AgentToolAnnotations {
26
26
  untrustedContent?: boolean;
27
27
  /** The tool changes something that matters: it is confirmed before it runs. */
28
28
  consequential?: boolean;
29
+ /**
30
+ * The tool changes only where the person is in the app (the page, the
31
+ * appearance), never their data. A read-only assistant mode offers it along
32
+ * with the `readOnly` tools (`offerClientTools`). Not a WebMCP hint: it is
33
+ * never sent to the browser or to the assistant.
34
+ */
35
+ navigation?: boolean;
29
36
  }
30
37
  /** What the confirmation dialog says. Plain text — never markup. */
31
38
  export interface AgentToolConfirmation {
@@ -1598,7 +1598,7 @@ var ki = () => () => {}, Ai = () => "off", ji = ({ visible: ji = !0, writeAccess
1598
1598
  permissionsSettled: m.navPermissionsSettled,
1599
1599
  simulating: m.navSimulating,
1600
1600
  sentBefore: ri(h)
1601
- }) : null, pe = _.getState().sandboxWriteCapabilities[i]?.clientTools === !0 ? _i() : null, me = pe?.manifest ?? [], he = {
1601
+ }) : null, pe = _.getState().sandboxWriteCapabilities[i]?.clientTools === !0 ? _i({ writeCapable: le?.writeCapable === !0 || g === "api-calls" }) : null, me = pe?.manifest ?? [], he = {
1602
1602
  conversationId: v,
1603
1603
  writeReply: ce === "proposal-v1" ? n.writeReplyBound?.writeReply ?? null : null,
1604
1604
  ...ue ? { uiCatalogue: ue } : {},
@@ -13,7 +13,9 @@ function t(t) {
13
13
  id: "global-nav-home",
14
14
  keys: ["g h"],
15
15
  description: "Go to Home",
16
+ descriptionKey: "nav.goHome",
16
17
  category: "Navigation",
18
+ categoryKey: "chrome.commandPalette.section.navigation",
17
19
  action: t.goHome,
18
20
  global: !0
19
21
  },
@@ -21,7 +23,9 @@ function t(t) {
21
23
  id: "global-search",
22
24
  keys: e("cmd+k"),
23
25
  description: "Open Search",
26
+ descriptionKey: "appShell.shortcuts.openSearch",
24
27
  category: "Search",
28
+ categoryKey: "common.search",
25
29
  action: t.openSearch,
26
30
  global: !0
27
31
  },
@@ -29,7 +33,9 @@ function t(t) {
29
33
  id: "global-toggle-sidebar",
30
34
  keys: e("cmd+\\", "ctrl+\\"),
31
35
  description: "Toggle Sidebar",
36
+ descriptionKey: "appShell.toggleSidebar",
32
37
  category: "UI",
38
+ categoryKey: "appShell.shortcuts.category.ui",
33
39
  action: t.toggleSidebar,
34
40
  global: !0
35
41
  },
@@ -37,7 +43,9 @@ function t(t) {
37
43
  id: "global-toggle-theme",
38
44
  keys: e("cmd+shift+d", "ctrl+shift+d"),
39
45
  description: "Toggle Dark Mode",
46
+ descriptionKey: "appShell.shortcuts.toggleDarkMode",
40
47
  category: "UI",
48
+ categoryKey: "appShell.shortcuts.category.ui",
41
49
  action: t.toggleTheme,
42
50
  global: !0
43
51
  },
@@ -45,7 +53,9 @@ function t(t) {
45
53
  id: "global-help",
46
54
  keys: e("cmd+/", "ctrl+/"),
47
55
  description: "Show Keyboard Shortcuts",
56
+ descriptionKey: "appShell.keyboardShortcuts",
48
57
  category: "Help",
58
+ categoryKey: "appShell.shortcuts.category.help",
49
59
  action: t.showHelp,
50
60
  global: !0
51
61
  }
@@ -54,7 +64,9 @@ function t(t) {
54
64
  id: "global-nav-settings",
55
65
  keys: ["g ,"],
56
66
  description: "Go to Settings",
67
+ descriptionKey: "appShell.shortcuts.goToSettings",
57
68
  category: "Navigation",
69
+ categoryKey: "chrome.commandPalette.section.navigation",
58
70
  action: t.openSettings,
59
71
  global: !0
60
72
  }), n;
@@ -29,8 +29,12 @@ export interface KeyboardShortcut {
29
29
  keys: string[] | PlatformKeys;
30
30
  /** Human-readable description */
31
31
  description: string;
32
+ /** Optional shell translation key; description remains the English fallback. */
33
+ descriptionKey?: string;
32
34
  /** Category for grouping shortcuts (e.g., 'navigation', 'editing') */
33
35
  category?: string;
36
+ /** Optional shell translation key; omit to preserve host-supplied category text. */
37
+ categoryKey?: string;
34
38
  /** Action to execute when shortcut is triggered */
35
39
  action: () => void;
36
40
  /** Whether this shortcut is enabled */
@@ -4,13 +4,13 @@ import { currentConfirmation as l, registerConfirmHost as u, requestAgentConfirm
4
4
  import { AGENT_TOOL_DESCRIPTION_MAX as h, AGENT_TOOL_NAME_MAX as g, AGENT_TOOL_NAME_PATTERN as _, AGENT_TOOL_OUTPUT_MAX_BYTES as v, AGENT_TOOL_OUTPUT_STRING_MAX as y, AGENT_TOOL_PARAM_DESCRIPTION_MAX as b } from "./shared/agent-tools/limits.js";
5
5
  import { capResult as x, jsonByteLength as S } from "./shared/agent-tools/outputBudget.js";
6
6
  import { runAgentTool as C } from "./shared/agent-tools/runAgentTool.js";
7
- import { CLIENT_TOOL_MANIFEST_MAX_BYTES as w, CLIENT_TOOL_MANIFEST_MAX_TOOLS as T, CLIENT_TOOL_ORPHAN_MS as E, buildClientToolManifest as D, createClientToolRunner as O, offerClientTools as k, parseClientToolRequests as A } from "./shared/agent-tools/adapters/assistant.js";
8
- import { defineAgentTool as j } from "./shared/agent-tools/defineAgentTool.js";
9
- import { useAgentTools as M } from "./shared/agent-tools/useAgentTools.js";
10
- import { AGENT_CONFIRM_TEST_ID_PREFIX as N, AgentConfirmHost as P } from "./shared/agent-tools/confirm/AgentConfirmHost.js";
11
- import { getWebMcpModelContext as F, startWebMcpBridge as I, toWebMcpDescriptor as L } from "./shared/agent-tools/adapters/webmcp.js";
12
- import { assertNoUnsavedChanges as R, createShellTools as z, resolveDestination as B } from "./shared/agent-tools/shell/shellTools.js";
13
- import { DENY_SELECTOR as V, INTERACTIVE_SELECTOR as H, accessibleName as U, isDenied as W, isSensitiveField as G, roleOf as K } from "./shared/agent-tools/ui/dom.js";
14
- import { createUiTools as q, isConsequentialControl as J } from "./shared/agent-tools/ui/uiTools.js";
15
- import { AgentToolsProvider as Y } from "./shared/agent-tools/AgentToolsProvider.js";
16
- export { N as AGENT_CONFIRM_TEST_ID_PREFIX, h as AGENT_TOOL_DESCRIPTION_MAX, g as AGENT_TOOL_NAME_MAX, _ as AGENT_TOOL_NAME_PATTERN, v as AGENT_TOOL_OUTPUT_MAX_BYTES, y as AGENT_TOOL_OUTPUT_STRING_MAX, b as AGENT_TOOL_PARAM_DESCRIPTION_MAX, P as AgentConfirmHost, e as AgentToolError, Y as AgentToolsProvider, w as CLIENT_TOOL_MANIFEST_MAX_BYTES, T as CLIENT_TOOL_MANIFEST_MAX_TOOLS, E as CLIENT_TOOL_ORPHAN_MS, V as DENY_SELECTOR, H as INTERACTIVE_SELECTOR, U as accessibleName, t as agentToolFailure, R as assertNoUnsavedChanges, D as buildClientToolManifest, x as capResult, i as clearAgentTools, O as createClientToolRunner, z as createShellTools, q as createUiTools, l as currentConfirmation, j as defineAgentTool, a as getAgentTool, F as getWebMcpModelContext, n as isAgentToolError, J as isConsequentialControl, W as isDenied, G as isSensitiveField, S as jsonByteLength, o as listAgentTools, k as offerClientTools, A as parseClientToolRequests, s as registerAgentTool, u as registerConfirmHost, d as requestAgentConfirmation, f as resetConfirmations, B as resolveDestination, K as roleOf, C as runAgentTool, p as settle, I as startWebMcpBridge, c as subscribeAgentTools, m as subscribeConfirmations, r as toAgentToolFailure, L as toWebMcpDescriptor, M as useAgentTools };
7
+ import { CLIENT_TOOL_MANIFEST_MAX_BYTES as w, CLIENT_TOOL_MANIFEST_MAX_TOOLS as T, CLIENT_TOOL_ORPHAN_MS as E, buildClientToolManifest as D, createClientToolRunner as O, isReadOnlyModeTool as k, offerClientTools as A, parseClientToolRequests as j } from "./shared/agent-tools/adapters/assistant.js";
8
+ import { defineAgentTool as M } from "./shared/agent-tools/defineAgentTool.js";
9
+ import { useAgentTools as N } from "./shared/agent-tools/useAgentTools.js";
10
+ import { AGENT_CONFIRM_TEST_ID_PREFIX as P, AgentConfirmHost as F } from "./shared/agent-tools/confirm/AgentConfirmHost.js";
11
+ import { getWebMcpModelContext as I, startWebMcpBridge as L, toWebMcpDescriptor as R } from "./shared/agent-tools/adapters/webmcp.js";
12
+ import { assertNoUnsavedChanges as z, createShellTools as B, resolveDestination as V } from "./shared/agent-tools/shell/shellTools.js";
13
+ import { DENY_SELECTOR as H, INTERACTIVE_SELECTOR as U, accessibleName as W, isDenied as G, isSensitiveField as K, roleOf as q } from "./shared/agent-tools/ui/dom.js";
14
+ import { createUiTools as J, isConsequentialControl as Y } from "./shared/agent-tools/ui/uiTools.js";
15
+ import { AgentToolsProvider as X } from "./shared/agent-tools/AgentToolsProvider.js";
16
+ export { P as AGENT_CONFIRM_TEST_ID_PREFIX, h as AGENT_TOOL_DESCRIPTION_MAX, g as AGENT_TOOL_NAME_MAX, _ as AGENT_TOOL_NAME_PATTERN, v as AGENT_TOOL_OUTPUT_MAX_BYTES, y as AGENT_TOOL_OUTPUT_STRING_MAX, b as AGENT_TOOL_PARAM_DESCRIPTION_MAX, F as AgentConfirmHost, e as AgentToolError, X as AgentToolsProvider, w as CLIENT_TOOL_MANIFEST_MAX_BYTES, T as CLIENT_TOOL_MANIFEST_MAX_TOOLS, E as CLIENT_TOOL_ORPHAN_MS, H as DENY_SELECTOR, U as INTERACTIVE_SELECTOR, W as accessibleName, t as agentToolFailure, z as assertNoUnsavedChanges, D as buildClientToolManifest, x as capResult, i as clearAgentTools, O as createClientToolRunner, B as createShellTools, J as createUiTools, l as currentConfirmation, M as defineAgentTool, a as getAgentTool, I as getWebMcpModelContext, n as isAgentToolError, Y as isConsequentialControl, G as isDenied, k as isReadOnlyModeTool, K as isSensitiveField, S as jsonByteLength, o as listAgentTools, A as offerClientTools, j as parseClientToolRequests, s as registerAgentTool, u as registerConfirmHost, d as requestAgentConfirmation, f as resetConfirmations, V as resolveDestination, q as roleOf, C as runAgentTool, p as settle, L as startWebMcpBridge, c as subscribeAgentTools, m as subscribeConfirmations, r as toAgentToolFailure, R as toWebMcpDescriptor, N as useAgentTools };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.1027.8",
3
+ "version": "2026.1027.10",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {