@particle-academy/agent-integrations 0.47.1 → 0.49.0

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.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.49.0] - 2026-10-27
15
+
16
+ ### Added
17
+
18
+ - **Screens bridge acknowledges host commits.** `setActive`, `createScreen`,
19
+ `destroyScreen` and `updateScreenContent` on `ScreensBridgeAdapter` may now
20
+ return a Promise resolving to `void` or the new exported
21
+ `ScreensAdapterResult` (`{ ok: true } | { ok: false; error; code? }`). The
22
+ bridge awaits it: success, and the `AgentActivity` event, are reported only
23
+ after the host commits; a rejection or `{ ok: false }` becomes an error result
24
+ with the host's message (never a stack) and emits no activity.
25
+ `ScreensAdapterCommit` and `ScreenCreateSpec` are now exported from the root
26
+ as well as `…/bridges/screens`. **Nothing to do:** sync `void` adapters behave
27
+ exactly as before.
28
+ - `screens_create` no longer calls `setActive` when `createScreen` fails. When
29
+ the create commits but activation fails, it returns an error stating the
30
+ screen was created but not activated (`structuredContent` carries
31
+ `created: true, activated: false`) and still emits one activity event for the
32
+ committed create.
33
+
34
+ ### Fixed
35
+
36
+ - **Screens bridge reported success before an async host had committed.** The
37
+ adapter methods were typed `void` and called without `await`, so a host with a
38
+ persisted or refusable commit got "success" and an activity event first, and a
39
+ rejection surfaced as an unhandled promise rejection instead of a tool error.
40
+ The bridge pushes no undo entries; that is unchanged and now documented.
41
+
42
+ ### Security
43
+
44
+ - Update Vitest to 4.1.12 for security fixes; dev toolchain only, nothing to do.
45
+
46
+ ## [0.48.0] - 2026-10-24
47
+
48
+ ### Added
49
+
50
+ - **Scoped screen-content bridge:** import `registerScreenDocBridge`,
51
+ `SCREEN_TEXT_NODE_TYPE`, `DocAdapter`, `ScreenDocBridgeOptions`, `ScreenDocNode`
52
+ and `ScreenDocOp` from `@particle-academy/agent-integrations/bridges/screen-doc`.
53
+ Both ESM and CommonJS builds include their type declarations. This complements
54
+ `bridges/screens`, which navigates between screens rather than editing their
55
+ contents. **Nothing to do: the root import still works.** The other 22 bridge
56
+ modules already have scoped exports; no additional missing entry was found.
57
+ - Built-package coverage for every discovered bridge: Node loads all scoped
58
+ entries through both ESM and CommonJS, and TypeScript checks ESM/CommonJS
59
+ declaration consumers. `npm test` builds fresh output first, so stale `dist`
60
+ files cannot hide a missing export.
61
+
62
+ ### Security
63
+
64
+ - **Dev toolchain only — no runtime change, nothing for consumers to do.** The
65
+ lockfile now resolves `vite` 8.3.5 (was 8.2.2), whose bundled
66
+ `launch-editor` fixes GHSA-587h-7wmj-h5pm (HIGH, `fs.existsSync()` check
67
+ bypassable on Windows), GHSA-698v-4r3x-3vcv (`escapeCmdArgs()` omits `%`
68
+ escaping on Windows) and GHSA-r825-w8mm-5pxw (editor CLI flag
69
+ misinterpretation). `vite` is only reached through the test/dev toolchain
70
+ and the published package does not ship our lockfile, so the exposure was
71
+ this repo's own dev server.
72
+
14
73
  ## [0.47.1] - 2026-10-21
15
74
 
16
75
  **What to do — nothing, unless you call `DomNavigationAgent.read()` yourself**
package/README.md CHANGED
@@ -87,12 +87,65 @@ Every bridge has a matching subpath if you'd rather import surgically:
87
87
  | charts | `…/bridges/charts` |
88
88
  | scene | `…/bridges/scene` |
89
89
  | screens | `…/bridges/screens` |
90
+ | screen contents | `…/bridges/screen-doc` |
90
91
  | passkeys | `…/bridges/passkeys` |
91
92
  | sheets adapter hook | `…/sheets-adapter` |
92
93
  | presence | `…/presence` |
93
94
  | undo | `…/undo` |
94
95
  | sharing | `…/sharing` |
95
96
 
97
+ `registerScreensBridge` handles navigation between screens.
98
+
99
+ ### Screens bridge: acknowledge after the host commits
100
+
101
+ Every mutating screens adapter method (`setActive`, `createScreen`,
102
+ `destroyScreen`, `updateScreenContent`) may return `void` **or** a Promise. A
103
+ sync `void` adapter behaves exactly as it always has. When the host's commit is
104
+ asynchronous and authoritative (persisted state, a possible refusal or
105
+ conflict), return a Promise and the bridge waits for it:
106
+
107
+ - the tool reports success, and emits its `AgentActivity` event, only after the
108
+ promise resolves;
109
+ - a rejection, or a resolved `{ ok: false, error }`, becomes an error result
110
+ carrying the host's `error` text (its message only, never a stack), and emits
111
+ **no** activity;
112
+ - `screens_create` calls `createScreen` and then `setActive`. If `createScreen`
113
+ fails, `setActive` is never called. If `setActive` fails after a successful
114
+ create, the result is an **error** saying the screen was created but not
115
+ activated (with `structuredContent: { id, kind, created: true, activated: false }`),
116
+ and one activity event is still emitted because the create did commit.
117
+
118
+ ```ts
119
+ import {
120
+ registerScreensBridge,
121
+ type ScreensAdapterResult,
122
+ } from "@particle-academy/agent-integrations/bridges/screens";
123
+
124
+ registerScreensBridge(host, {
125
+ adapter: {
126
+ listScreens: () => store.screens,
127
+ getActive: () => store.activeId,
128
+ // Resolve once the server has persisted it; refuse with a reason the agent can act on.
129
+ setActive: async (id): Promise<ScreensAdapterResult> => {
130
+ const res = await api.post(`/screens/${id}/activate`);
131
+ return res.ok ? { ok: true } : { ok: false, error: res.reason, code: "conflict" };
132
+ },
133
+ createScreen: async (spec) => {
134
+ await api.post("/screens", spec); // a throw becomes an error result
135
+ },
136
+ },
137
+ });
138
+ ```
139
+
140
+ The screens bridge pushes **no undo entries**: `agent_undo` cannot revert a
141
+ screens tool. Keep your own history if you need to.
142
+
143
+ `registerScreenDocBridge` edits the nodes inside one screen and exports its
144
+ `DocAdapter`, `ScreenDocBridgeOptions`, `ScreenDocNode` and `ScreenDocOp` types
145
+ from `@particle-academy/agent-integrations/bridges/screen-doc`. The existing
146
+ root import still works; nothing to do for existing consumers. Use a distinct
147
+ `surface` tool prefix for each concurrently bridged document.
148
+
96
149
  ## When you don't need an MCP server
97
150
 
98
151
  If your agent runs in the same JS process as the surfaces it drives (an embedded chat widget, an in-page worker, an SSR-time tool loop), you don't need any of MCP's JSON-RPC framing. Use **`ToolRegistry`** — a plain in-memory tool host that the bridges register against. Agents drive the surface with `host.callTool("sheet_set_cell", { … })` directly.
@@ -0,0 +1,47 @@
1
+ import { T as ToolHost } from '../tool-host-D67N2a2h.cjs';
2
+ import { B as Bridge } from '../types-DP5yQZQU.cjs';
3
+ import { DocAdapter } from './doc.cjs';
4
+ import { DocNode, StagePolicy, TreeOp } from '@particle-academy/fancy-doc-commons';
5
+ import '../types-gzIpsUDE.cjs';
6
+
7
+ /**
8
+ * Reserved node type for a literal string child. Must match fancy-screens'
9
+ * `TEXT_NODE_TYPE`; duplicated rather than imported to keep this bridge free of
10
+ * a fancy-screens dependency.
11
+ */
12
+ declare const SCREEN_TEXT_NODE_TYPE = "#text";
13
+ /** A screen document node — a `DocNode` plus the minted-id flag. */
14
+ interface ScreenDocNode extends DocNode {
15
+ /**
16
+ * True when fancy-screens minted this id because the author supplied none.
17
+ * A synthetic id is derived from position, so it is not a durable handle.
18
+ */
19
+ synthetic?: boolean;
20
+ }
21
+ type ScreenDocOp = TreeOp<ScreenDocNode>;
22
+ interface ScreenDocBridgeOptions {
23
+ adapter: DocAdapter<ScreenDocNode>;
24
+ /**
25
+ * Tool-name prefix. Default `"screen"` → `screen_add`, `screen_set_text`, …
26
+ *
27
+ * Tool names are global, so a host wiring more than one doc-driven screen at
28
+ * once must give each a distinct prefix (`"screen_checkout"`).
29
+ */
30
+ surface?: string;
31
+ agent?: {
32
+ id: string;
33
+ name?: string;
34
+ color?: string;
35
+ };
36
+ /** Auto-apply vs. confirm per op (trust-but-verify). Default: auto. */
37
+ stagePolicy?: StagePolicy<ScreenDocOp>;
38
+ onPending?: (staged: {
39
+ id: string;
40
+ op: ScreenDocOp;
41
+ label: string;
42
+ }) => void;
43
+ }
44
+ /** Register the screen-content bridge (prefix `screen_`) over a screen's `DocTree`. */
45
+ declare function registerScreenDocBridge(host: ToolHost, options: ScreenDocBridgeOptions): Bridge;
46
+
47
+ export { DocAdapter, SCREEN_TEXT_NODE_TYPE, type ScreenDocBridgeOptions, type ScreenDocNode, type ScreenDocOp, registerScreenDocBridge };
@@ -0,0 +1,47 @@
1
+ import { T as ToolHost } from '../tool-host-LYfdPE8u.js';
2
+ import { B as Bridge } from '../types-VX_9je7w.js';
3
+ import { DocAdapter } from './doc.js';
4
+ import { DocNode, StagePolicy, TreeOp } from '@particle-academy/fancy-doc-commons';
5
+ import '../types-gzIpsUDE.js';
6
+
7
+ /**
8
+ * Reserved node type for a literal string child. Must match fancy-screens'
9
+ * `TEXT_NODE_TYPE`; duplicated rather than imported to keep this bridge free of
10
+ * a fancy-screens dependency.
11
+ */
12
+ declare const SCREEN_TEXT_NODE_TYPE = "#text";
13
+ /** A screen document node — a `DocNode` plus the minted-id flag. */
14
+ interface ScreenDocNode extends DocNode {
15
+ /**
16
+ * True when fancy-screens minted this id because the author supplied none.
17
+ * A synthetic id is derived from position, so it is not a durable handle.
18
+ */
19
+ synthetic?: boolean;
20
+ }
21
+ type ScreenDocOp = TreeOp<ScreenDocNode>;
22
+ interface ScreenDocBridgeOptions {
23
+ adapter: DocAdapter<ScreenDocNode>;
24
+ /**
25
+ * Tool-name prefix. Default `"screen"` → `screen_add`, `screen_set_text`, …
26
+ *
27
+ * Tool names are global, so a host wiring more than one doc-driven screen at
28
+ * once must give each a distinct prefix (`"screen_checkout"`).
29
+ */
30
+ surface?: string;
31
+ agent?: {
32
+ id: string;
33
+ name?: string;
34
+ color?: string;
35
+ };
36
+ /** Auto-apply vs. confirm per op (trust-but-verify). Default: auto. */
37
+ stagePolicy?: StagePolicy<ScreenDocOp>;
38
+ onPending?: (staged: {
39
+ id: string;
40
+ op: ScreenDocOp;
41
+ label: string;
42
+ }) => void;
43
+ }
44
+ /** Register the screen-content bridge (prefix `screen_`) over a screen's `DocTree`. */
45
+ declare function registerScreenDocBridge(host: ToolHost, options: ScreenDocBridgeOptions): Bridge;
46
+
47
+ export { DocAdapter, SCREEN_TEXT_NODE_TYPE, type ScreenDocBridgeOptions, type ScreenDocNode, type ScreenDocOp, registerScreenDocBridge };
@@ -28,6 +28,28 @@ type ScreenCreateSpec = {
28
28
  /** Template-specific config. Form: { fields }. Sheet: { headers }. etc. */
29
29
  config?: Record<string, unknown>;
30
30
  };
31
+ /**
32
+ * What a mutating adapter method may resolve to, to acknowledge (or refuse)
33
+ * a commit. `{ ok: false }` is a refusal the agent can act on: `error` is
34
+ * shown to the agent verbatim, so keep it a human-readable sentence, never a
35
+ * stack trace or an internal path. `code` is a machine-readable hint
36
+ * (e.g. `"conflict"`) for the host's own use; it is not shown to the agent.
37
+ */
38
+ type ScreensAdapterResult = {
39
+ ok: true;
40
+ } | {
41
+ ok: false;
42
+ error: string;
43
+ code?: string;
44
+ };
45
+ /**
46
+ * Return type of every mutating adapter method. A sync `void` return keeps
47
+ * the original behaviour. A Promise makes the bridge WAIT: the tool reports
48
+ * success, and emits its AgentActivity event, only after the promise
49
+ * resolves. A rejection or an `{ ok: false }` result becomes an error result
50
+ * and emits no activity.
51
+ */
52
+ type ScreensAdapterCommit = void | Promise<void | ScreensAdapterResult>;
31
53
  /**
32
54
  * Adapter exposes the host's screen-navigation surface to the bridge.
33
55
  * Hosts wire this up against react-router, a custom tab state, or the
@@ -35,6 +57,15 @@ type ScreenCreateSpec = {
35
57
  *
36
58
  * The optional create / destroy / update hooks let agents author screens
37
59
  * dynamically against a host-defined template catalog.
60
+ *
61
+ * Every mutating method may be sync (`void`) or async (`Promise`); see
62
+ * {@link ScreensAdapterCommit}. Return a Promise when the host's commit is
63
+ * authoritative — persisted, or able to refuse or conflict — so the agent is
64
+ * told the truth instead of "success" before anything was committed.
65
+ *
66
+ * This bridge pushes NO undo entries: `agent_undo` cannot revert a screens
67
+ * tool. A host that needs undo for navigation or screen authoring must keep
68
+ * its own history.
38
69
  */
39
70
  type ScreensBridgeAdapter = {
40
71
  /** List every available screen. */
@@ -42,13 +73,13 @@ type ScreensBridgeAdapter = {
42
73
  /** Read which screen is currently active. */
43
74
  getActive: () => string | null;
44
75
  /** Navigate to a screen by id. Host updates router / tab state. */
45
- setActive: (screenId: string) => void;
76
+ setActive: (screenId: string) => ScreensAdapterCommit;
46
77
  /** Optional: instantiate a new screen from a template + config. */
47
- createScreen?: (spec: ScreenCreateSpec) => void;
78
+ createScreen?: (spec: ScreenCreateSpec) => ScreensAdapterCommit;
48
79
  /** Optional: remove a previously-created screen. */
49
- destroyScreen?: (screenId: string) => void;
80
+ destroyScreen?: (screenId: string) => ScreensAdapterCommit;
50
81
  /** Optional: shallow-merge new config into an existing screen. */
51
- updateScreenContent?: (screenId: string, partial: Record<string, unknown>) => void;
82
+ updateScreenContent?: (screenId: string, partial: Record<string, unknown>) => ScreensAdapterCommit;
52
83
  /** Optional: enumerate the kinds the host knows how to instantiate. */
53
84
  listKinds?: () => Array<{
54
85
  kind: string;
@@ -75,4 +106,4 @@ type ScreensBridgeOptions = {
75
106
  */
76
107
  declare function registerScreensBridge(host: ToolHost, options: ScreensBridgeOptions): Bridge;
77
108
 
78
- export { type ScreenCreateSpec, type ScreenSnapshot, type ScreensBridgeAdapter, type ScreensBridgeOptions, registerScreensBridge };
109
+ export { type ScreenCreateSpec, type ScreenSnapshot, type ScreensAdapterCommit, type ScreensAdapterResult, type ScreensBridgeAdapter, type ScreensBridgeOptions, registerScreensBridge };
@@ -28,6 +28,28 @@ type ScreenCreateSpec = {
28
28
  /** Template-specific config. Form: { fields }. Sheet: { headers }. etc. */
29
29
  config?: Record<string, unknown>;
30
30
  };
31
+ /**
32
+ * What a mutating adapter method may resolve to, to acknowledge (or refuse)
33
+ * a commit. `{ ok: false }` is a refusal the agent can act on: `error` is
34
+ * shown to the agent verbatim, so keep it a human-readable sentence, never a
35
+ * stack trace or an internal path. `code` is a machine-readable hint
36
+ * (e.g. `"conflict"`) for the host's own use; it is not shown to the agent.
37
+ */
38
+ type ScreensAdapterResult = {
39
+ ok: true;
40
+ } | {
41
+ ok: false;
42
+ error: string;
43
+ code?: string;
44
+ };
45
+ /**
46
+ * Return type of every mutating adapter method. A sync `void` return keeps
47
+ * the original behaviour. A Promise makes the bridge WAIT: the tool reports
48
+ * success, and emits its AgentActivity event, only after the promise
49
+ * resolves. A rejection or an `{ ok: false }` result becomes an error result
50
+ * and emits no activity.
51
+ */
52
+ type ScreensAdapterCommit = void | Promise<void | ScreensAdapterResult>;
31
53
  /**
32
54
  * Adapter exposes the host's screen-navigation surface to the bridge.
33
55
  * Hosts wire this up against react-router, a custom tab state, or the
@@ -35,6 +57,15 @@ type ScreenCreateSpec = {
35
57
  *
36
58
  * The optional create / destroy / update hooks let agents author screens
37
59
  * dynamically against a host-defined template catalog.
60
+ *
61
+ * Every mutating method may be sync (`void`) or async (`Promise`); see
62
+ * {@link ScreensAdapterCommit}. Return a Promise when the host's commit is
63
+ * authoritative — persisted, or able to refuse or conflict — so the agent is
64
+ * told the truth instead of "success" before anything was committed.
65
+ *
66
+ * This bridge pushes NO undo entries: `agent_undo` cannot revert a screens
67
+ * tool. A host that needs undo for navigation or screen authoring must keep
68
+ * its own history.
38
69
  */
39
70
  type ScreensBridgeAdapter = {
40
71
  /** List every available screen. */
@@ -42,13 +73,13 @@ type ScreensBridgeAdapter = {
42
73
  /** Read which screen is currently active. */
43
74
  getActive: () => string | null;
44
75
  /** Navigate to a screen by id. Host updates router / tab state. */
45
- setActive: (screenId: string) => void;
76
+ setActive: (screenId: string) => ScreensAdapterCommit;
46
77
  /** Optional: instantiate a new screen from a template + config. */
47
- createScreen?: (spec: ScreenCreateSpec) => void;
78
+ createScreen?: (spec: ScreenCreateSpec) => ScreensAdapterCommit;
48
79
  /** Optional: remove a previously-created screen. */
49
- destroyScreen?: (screenId: string) => void;
80
+ destroyScreen?: (screenId: string) => ScreensAdapterCommit;
50
81
  /** Optional: shallow-merge new config into an existing screen. */
51
- updateScreenContent?: (screenId: string, partial: Record<string, unknown>) => void;
82
+ updateScreenContent?: (screenId: string, partial: Record<string, unknown>) => ScreensAdapterCommit;
52
83
  /** Optional: enumerate the kinds the host knows how to instantiate. */
53
84
  listKinds?: () => Array<{
54
85
  kind: string;
@@ -75,4 +106,4 @@ type ScreensBridgeOptions = {
75
106
  */
76
107
  declare function registerScreensBridge(host: ToolHost, options: ScreensBridgeOptions): Bridge;
77
108
 
78
- export { type ScreenCreateSpec, type ScreenSnapshot, type ScreensBridgeAdapter, type ScreensBridgeOptions, registerScreensBridge };
109
+ export { type ScreenCreateSpec, type ScreenSnapshot, type ScreensAdapterCommit, type ScreensAdapterResult, type ScreensBridgeAdapter, type ScreensBridgeOptions, registerScreensBridge };