@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.
@@ -1,9 +1,33 @@
1
1
  import { ensureUndoToolsRegistered } from './chunk-R4XXOGQH.js';
2
2
  import { wrapToolWithActivity } from './chunk-2VENWGLZ.js';
3
+ import { emitActivity } from './chunk-C3TYI5TJ.js';
3
4
  import { textResult, errorResult } from './chunk-CZV3WUW5.js';
4
5
 
5
6
  // src/bridges/screens.ts
6
7
  var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
8
+ function hostErrorMessage(e, fallback) {
9
+ let msg;
10
+ if (e instanceof Error) msg = e.message;
11
+ else if (typeof e === "string") msg = e;
12
+ else if (e && typeof e === "object" && typeof e.message === "string") {
13
+ msg = e.message;
14
+ }
15
+ const firstFrame = msg ? msg.search(/\n\s+at\s/) : -1;
16
+ if (msg && firstFrame >= 0) msg = msg.slice(0, firstFrame);
17
+ msg = msg?.trim();
18
+ return msg ? msg : fallback;
19
+ }
20
+ async function commit(run, fallback) {
21
+ try {
22
+ const r = await run();
23
+ if (r && typeof r === "object" && r.ok === false) {
24
+ return { ok: false, error: hostErrorMessage(r.error, fallback) };
25
+ }
26
+ return { ok: true };
27
+ } catch (e) {
28
+ return { ok: false, error: hostErrorMessage(e, fallback) };
29
+ }
30
+ }
7
31
  function registerScreensBridge(host, options) {
8
32
  const { adapter } = options;
9
33
  const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
@@ -19,7 +43,7 @@ function registerScreensBridge(host, options) {
19
43
  try {
20
44
  return await handler(args);
21
45
  } catch (e) {
22
- return errorResult(e instanceof Error ? e.message : String(e));
46
+ return errorResult(hostErrorMessage(e, `${name} failed.`));
23
47
  }
24
48
  };
25
49
  const final = isMutation ? wrapToolWithActivity(wrapped, {
@@ -81,20 +105,40 @@ function registerScreensBridge(host, options) {
81
105
  config: { type: "object", description: "Template-specific config (e.g. { fields: [...] } for a form)." }
82
106
  },
83
107
  ["id", "kind"],
84
- (args) => {
85
- if (!adapter.createScreen) return errorResult("Host did not provide createScreen.");
108
+ async (args) => {
109
+ const createScreen = adapter.createScreen;
110
+ if (!createScreen) return errorResult("Host did not provide createScreen.");
86
111
  const id = String(args.id);
87
112
  const kind = String(args.kind);
88
113
  if (adapter.listScreens().find((s) => s.id === id)) {
89
114
  return errorResult(`Screen ${id} already exists. Use screens_destroy first or pick a fresh id.`);
90
115
  }
91
- adapter.createScreen({
116
+ const created = await commit(() => createScreen({
92
117
  id,
93
118
  title: typeof args.title === "string" ? args.title : void 0,
94
119
  kind,
95
120
  config: args.config && typeof args.config === "object" ? args.config : void 0
96
- });
97
- adapter.setActive(id);
121
+ }), `Host failed to create screen "${id}".`);
122
+ if (!created.ok) return errorResult(created.error);
123
+ const activated = await commit(() => adapter.setActive(id), `Host failed to activate screen "${id}".`);
124
+ if (!activated.ok) {
125
+ const state = { id, kind, created: true, activated: false };
126
+ emitActivity({
127
+ agentId: agent.id,
128
+ agentName: agent.name,
129
+ agentColor: agent.color,
130
+ target: target(id),
131
+ action: "screens_create",
132
+ timestamp: Date.now(),
133
+ meta: state
134
+ });
135
+ return {
136
+ ...errorResult(
137
+ `Screen "${id}" was created but not activated: ${activated.error} The screen exists; do not call screens_create again. Call screens_navigate to show it, or screens_destroy to remove it.`
138
+ ),
139
+ structuredContent: state
140
+ };
141
+ }
98
142
  return textResult(`Created ${kind} screen "${id}"`, { id, kind });
99
143
  },
100
144
  true,
@@ -105,13 +149,15 @@ function registerScreensBridge(host, options) {
105
149
  "Remove a previously-created screen. Active screen falls back to the first remaining one (or null).",
106
150
  { id: { type: "string" } },
107
151
  ["id"],
108
- (args) => {
109
- if (!adapter.destroyScreen) return errorResult("Host did not provide destroyScreen.");
152
+ async (args) => {
153
+ const destroyScreen = adapter.destroyScreen;
154
+ if (!destroyScreen) return errorResult("Host did not provide destroyScreen.");
110
155
  const id = String(args.id);
111
156
  if (!adapter.listScreens().find((s) => s.id === id)) {
112
157
  return errorResult(`No screen with id ${id}`);
113
158
  }
114
- adapter.destroyScreen(id);
159
+ const done = await commit(() => destroyScreen(id), `Host failed to destroy screen "${id}".`);
160
+ if (!done.ok) return errorResult(done.error);
115
161
  return textResult(`Destroyed screen ${id}`, { id });
116
162
  },
117
163
  true,
@@ -125,9 +171,15 @@ function registerScreensBridge(host, options) {
125
171
  layout: { type: "string", enum: ["single", "split-h", "split-v", "grid-2x2", "stack"] }
126
172
  },
127
173
  ["id", "layout"],
128
- (args) => {
129
- if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
130
- adapter.updateScreenContent(String(args.id), { layout: String(args.layout) });
174
+ async (args) => {
175
+ const updateScreenContent = adapter.updateScreenContent;
176
+ if (!updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
177
+ const id = String(args.id);
178
+ const done = await commit(
179
+ () => updateScreenContent(id, { layout: String(args.layout) }),
180
+ `Host failed to change the layout of screen "${id}".`
181
+ );
182
+ if (!done.ok) return errorResult(done.error);
131
183
  return textResult(`Layout of ${args.id} \u2192 ${args.layout}`, { id: args.id, layout: args.layout });
132
184
  },
133
185
  true,
@@ -141,11 +193,13 @@ function registerScreensBridge(host, options) {
141
193
  partial: { type: "object", description: "Shallow-merged into the screen's config." }
142
194
  },
143
195
  ["id", "partial"],
144
- (args) => {
145
- if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
196
+ async (args) => {
197
+ const updateScreenContent = adapter.updateScreenContent;
198
+ if (!updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
146
199
  const id = String(args.id);
147
200
  const partial = args.partial && typeof args.partial === "object" ? args.partial : {};
148
- adapter.updateScreenContent(id, partial);
201
+ const done = await commit(() => updateScreenContent(id, partial), `Host failed to update screen "${id}".`);
202
+ if (!done.ok) return errorResult(done.error);
149
203
  return textResult(`Updated content of ${id}`, { id });
150
204
  },
151
205
  true,
@@ -156,13 +210,14 @@ function registerScreensBridge(host, options) {
156
210
  "Switch the human's view to a different screen. The host updates its router / tab state and re-renders.",
157
211
  { screen: { type: "string", description: "Screen id to activate." } },
158
212
  ["screen"],
159
- (args) => {
213
+ async (args) => {
160
214
  const screenId = String(args.screen ?? "");
161
215
  const screens = adapter.listScreens();
162
216
  if (!screens.find((s) => s.id === screenId)) {
163
217
  return errorResult(`No screen registered with id "${screenId}". Call screens_list first.`);
164
218
  }
165
- adapter.setActive(screenId);
219
+ const done = await commit(() => adapter.setActive(screenId), `Host failed to navigate to screen "${screenId}".`);
220
+ if (!done.ok) return errorResult(done.error);
166
221
  return textResult(`Navigated to ${screenId}`, { screen: screenId });
167
222
  },
168
223
  true,
@@ -178,5 +233,5 @@ function registerScreensBridge(host, options) {
178
233
  }
179
234
 
180
235
  export { registerScreensBridge };
181
- //# sourceMappingURL=chunk-7NVATRLE.js.map
182
- //# sourceMappingURL=chunk-7NVATRLE.js.map
236
+ //# sourceMappingURL=chunk-HOZCDDRA.js.map
237
+ //# sourceMappingURL=chunk-HOZCDDRA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bridges/screens.ts"],"names":[],"mappings":";;;;;;AA8FA,IAAM,gBAAgB,EAAE,EAAA,EAAI,SAAS,IAAA,EAAM,OAAA,EAAS,OAAO,SAAA,EAAU;AASrE,SAAS,gBAAA,CAAiB,GAAY,QAAA,EAA0B;AAC9D,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI,CAAA,YAAa,KAAA,EAAO,GAAA,GAAM,CAAA,CAAE,OAAA;AAAA,OAAA,IACvB,OAAO,CAAA,KAAM,QAAA,EAAU,GAAA,GAAM,CAAA;AAAA,OAAA,IAC7B,KAAK,OAAO,CAAA,KAAM,YAAY,OAAQ,CAAA,CAA4B,YAAY,QAAA,EAAU;AAC/F,IAAA,GAAA,GAAO,CAAA,CAA0B,OAAA;AAAA,EACnC;AACA,EAAA,MAAM,UAAA,GAAa,GAAA,GAAM,GAAA,CAAI,MAAA,CAAO,WAAW,CAAA,GAAI,EAAA;AACnD,EAAA,IAAI,OAAO,UAAA,IAAc,CAAA,QAAS,GAAA,CAAI,KAAA,CAAM,GAAG,UAAU,CAAA;AACzD,EAAA,GAAA,GAAM,KAAK,IAAA,EAAK;AAChB,EAAA,OAAO,MAAM,GAAA,GAAM,QAAA;AACrB;AAKA,eAAe,MAAA,CAAO,KAAiC,QAAA,EAA0C;AAC/F,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,MAAM,GAAA,EAAI;AACpB,IAAA,IAAI,KAAK,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,CAAE,OAAO,KAAA,EAAO;AAChD,MAAA,OAAO,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,iBAAiB,CAAA,CAAE,KAAA,EAAO,QAAQ,CAAA,EAAE;AAAA,IACjE;AACA,IAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,EACpB,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,OAAO,gBAAA,CAAiB,CAAA,EAAG,QAAQ,CAAA,EAAE;AAAA,EAC3D;AACF;AAUO,SAAS,qBAAA,CACd,MACA,OAAA,EACQ;AACR,EAAA,MAAM,EAAE,SAAQ,GAAI,OAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,EAAE,GAAG,aAAA,EAAe,GAAI,OAAA,CAAQ,KAAA,IAAS,EAAC,EAAG;AAC3D,EAAA,MAAM,YAA+B,EAAC;AAItC,EAAA,yBAAA,CAA0B,IAAA,EAAM,EAAE,cAAA,EAAgB,KAAA,CAAM,IAAI,CAAA;AAE5D,EAAA,MAAM,MAAA,GAAS,CAAC,QAAA,MAAmC;AAAA,IACjD,IAAA,EAAM,SAAA;AAAA,IACN,QAAA;AAAA,IACA,KAAA,EAAO,UAAU,QAAQ,CAAA;AAAA,GAC3B,CAAA;AAEA,EAAA,MAAM,GAAA,GAAM,CACV,IAAA,EACA,WAAA,EACA,YACA,QAAA,EACA,OAAA,EACA,YACA,cAAA,KACG;AACH,IAAA,MAAM,OAAA,GAAU,OAAO,IAAA,KAAqB;AAC1C,MAAA,IAAI;AAAE,QAAA,OAAO,MAAM,QAAQ,IAAI,CAAA;AAAA,MAAG,SAC3B,CAAA,EAAG;AAAE,QAAA,OAAO,YAAY,gBAAA,CAAiB,CAAA,EAAG,CAAA,EAAG,IAAI,UAAU,CAAC,CAAA;AAAA,MAAG;AAAA,IAC1E,CAAA;AACA,IAAA,MAAM,KAAA,GAAQ,UAAA,GACV,oBAAA,CAAqB,OAAA,EAAS;AAAA,MAC5B,QAAA,EAAU,IAAA;AAAA,MAAM,KAAA;AAAA,MAAO,IAAA,EAAM,SAAA;AAAA,MAC7B,eAAe,CAAC,EAAE,MAAK,KAAM,cAAA,GAAiB,IAAI,CAAA,IAAK;AAAA,KACxD,CAAA,GACD,OAAA;AACJ,IAAA,SAAA,CAAU,IAAA;AAAA,MACR,IAAA,CAAK,YAAA;AAAA,QACH,EAAE,IAAA,EAAM,WAAA,EAAa,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAA+B,QAAA,EAAU,oBAAA,EAAsB,KAAA,EAAM,EAAE;AAAA,QAC3H;AAAA;AACF,KACF;AAAA,EACF,CAAA;AAEA,EAAA,GAAA;AAAA,IACE,cAAA;AAAA,IACA,+FAAA;AAAA,IACA,EAAC;AAAA,IACD,EAAC;AAAA,IACD,MAAM;AACJ,MAAA,MAAM,OAAA,GAAU,QAAQ,WAAA,EAAY;AACpC,MAAA,MAAM,IAAA,GAAO,OAAA,CACV,GAAA,CAAI,CAAC,MAAM,CAAA,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,GAAM,GAAG,CAAA,CAAA,EAAI,CAAA,CAAE,EAAE,GAAG,CAAA,CAAE,KAAA,GAAQ,CAAA,QAAA,EAAM,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAE,CAAA,EAAG,EAAE,IAAA,GAAO,CAAA,EAAA,EAAK,CAAA,CAAE,IAAI,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAE,CAAA,CAC5G,KAAK,IAAI,CAAA;AACZ,MAAA,OAAO,UAAA,CAAW,QAAQ,cAAA,EAAgB,EAAE,SAAS,MAAA,EAAQ,OAAA,CAAQ,SAAA,EAAU,EAAG,CAAA;AAAA,IACpF,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,yBAAA;AAAA,IACA,+CAAA;AAAA,IACA,EAAC;AAAA,IACD,EAAC;AAAA,IACD,MAAM;AACJ,MAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,EAAU;AACjC,MAAA,OAAO,UAAA,CAAW,MAAA,IAAU,QAAA,EAAU,EAAE,QAAQ,CAAA;AAAA,IAClD,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,oBAAA;AAAA,IACA,+HAAA;AAAA,IACA,EAAC;AAAA,IACD,EAAC;AAAA,IACD,MAAM;AACJ,MAAA,IAAI,CAAC,OAAA,CAAQ,SAAA,EAAW,OAAO,YAAY,0EAA0E,CAAA;AACrH,MAAA,MAAM,KAAA,GAAQ,QAAQ,SAAA,EAAU;AAChC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,EAAG,CAAA,CAAE,KAAA,GAAQ,CAAA,QAAA,EAAM,CAAA,CAAE,KAAK,CAAA,CAAA,GAAK,EAAE,CAAA,EAAG,CAAA,CAAE,WAAA,GAAc,CAAA,EAAA,EAAK,CAAA,CAAE,WAAW,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAClI,MAAA,OAAO,UAAA,CAAW,IAAA,IAAQ,uBAAA,EAAyB,KAAK,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,GAAA;AAAA,IACE,gBAAA;AAAA,IACA,qGAAA;AAAA,IACA;AAAA,MACE,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,mCAAA,EAAoC;AAAA,MACvE,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,MACxB,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,+DAAA,EAA2D;AAAA,MAChG,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,+DAAA;AAAgE,KACzG;AAAA,IACA,CAAC,MAAM,MAAM,CAAA;AAAA,IACb,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,eAAe,OAAA,CAAQ,YAAA;AAC7B,MAAA,IAAI,CAAC,YAAA,EAAc,OAAO,WAAA,CAAY,oCAAoC,CAAA;AAC1E,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AACzB,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AAC7B,MAAA,IAAI,OAAA,CAAQ,aAAY,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,EAAG;AAClD,QAAA,OAAO,WAAA,CAAY,CAAA,OAAA,EAAU,EAAE,CAAA,8DAAA,CAAgE,CAAA;AAAA,MACjG;AACA,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,MAAM,YAAA,CAAa;AAAA,QAC9C,EAAA;AAAA,QACA,OAAO,OAAO,IAAA,CAAK,KAAA,KAAU,QAAA,GAAW,KAAK,KAAA,GAAQ,KAAA,CAAA;AAAA,QACrD,IAAA;AAAA,QACA,MAAA,EAAS,KAAK,MAAA,IAAU,OAAO,KAAK,MAAA,KAAW,QAAA,GAAY,KAAK,MAAA,GAAoC,KAAA;AAAA,OACrG,CAAA,EAAG,CAAA,8BAAA,EAAiC,EAAE,CAAA,EAAA,CAAI,CAAA;AAE3C,MAAA,IAAI,CAAC,OAAA,CAAQ,EAAA,EAAI,OAAO,WAAA,CAAY,QAAQ,KAAK,CAAA;AAEjD,MAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,MAAM,OAAA,CAAQ,UAAU,EAAE,CAAA,EAAG,CAAA,gCAAA,EAAmC,EAAE,CAAA,EAAA,CAAI,CAAA;AACrG,MAAA,IAAI,CAAC,UAAU,EAAA,EAAI;AAMjB,QAAA,MAAM,QAAQ,EAAE,EAAA,EAAI,MAAM,OAAA,EAAS,IAAA,EAAM,WAAW,KAAA,EAAM;AAC1D,QAAA,YAAA,CAAa;AAAA,UACX,SAAS,KAAA,CAAM,EAAA;AAAA,UACf,WAAW,KAAA,CAAM,IAAA;AAAA,UACjB,YAAY,KAAA,CAAM,KAAA;AAAA,UAClB,MAAA,EAAQ,OAAO,EAAE,CAAA;AAAA,UACjB,MAAA,EAAQ,gBAAA;AAAA,UACR,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,UACpB,IAAA,EAAM;AAAA,SACP,CAAA;AACD,QAAA,OAAO;AAAA,UACL,GAAG,WAAA;AAAA,YACD,CAAA,QAAA,EAAW,EAAE,CAAA,iCAAA,EAAoC,SAAA,CAAU,KAAK,CAAA,wHAAA;AAAA,WAGlE;AAAA,UACA,iBAAA,EAAmB;AAAA,SACrB;AAAA,MACF;AACA,MAAA,OAAO,UAAA,CAAW,WAAW,IAAI,CAAA,SAAA,EAAY,EAAE,CAAA,CAAA,CAAA,EAAK,EAAE,EAAA,EAAI,IAAA,EAAM,CAAA;AAAA,IAClE,CAAA;AAAA,IACA,IAAA;AAAA,IACA,CAAC,IAAA,KAAS,MAAA,CAAO,OAAO,IAAA,CAAK,EAAA,IAAM,EAAE,CAAC;AAAA,GACxC;AAEA,EAAA,GAAA;AAAA,IACE,iBAAA;AAAA,IACA,oGAAA;AAAA,IACA,EAAE,EAAA,EAAI,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,IACzB,CAAC,IAAI,CAAA;AAAA,IACL,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,gBAAgB,OAAA,CAAQ,aAAA;AAC9B,MAAA,IAAI,CAAC,aAAA,EAAe,OAAO,WAAA,CAAY,qCAAqC,CAAA;AAC5E,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AACzB,MAAA,IAAI,CAAC,OAAA,CAAQ,WAAA,EAAY,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,EAAE,CAAA,EAAG;AACnD,QAAA,OAAO,WAAA,CAAY,CAAA,kBAAA,EAAqB,EAAE,CAAA,CAAE,CAAA;AAAA,MAC9C;AACA,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,MAAM,cAAc,EAAE,CAAA,EAAG,CAAA,+BAAA,EAAkC,EAAE,CAAA,EAAA,CAAI,CAAA;AAC3F,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAC3C,MAAA,OAAO,WAAW,CAAA,iBAAA,EAAoB,EAAE,CAAA,CAAA,EAAI,EAAE,IAAI,CAAA;AAAA,IACpD,CAAA;AAAA,IACA,IAAA;AAAA,IACA,CAAC,IAAA,KAAS,MAAA,CAAO,OAAO,IAAA,CAAK,EAAA,IAAM,EAAE,CAAC;AAAA,GACxC;AAEA,EAAA,GAAA;AAAA,IACE,oBAAA;AAAA,IACA,mJAAA;AAAA,IACA;AAAA,MACE,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,MACrB,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,IAAA,EAAM,CAAC,QAAA,EAAU,SAAA,EAAW,SAAA,EAAW,UAAA,EAAY,OAAO,CAAA;AAAE,KACxF;AAAA,IACA,CAAC,MAAM,QAAQ,CAAA;AAAA,IACf,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,sBAAsB,OAAA,CAAQ,mBAAA;AACpC,MAAA,IAAI,CAAC,mBAAA,EAAqB,OAAO,WAAA,CAAY,2CAA2C,CAAA;AACxF,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AACzB,MAAA,MAAM,OAAO,MAAM,MAAA;AAAA,QACjB,MAAM,oBAAoB,EAAA,EAAI,EAAE,QAAQ,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,EAAG,CAAA;AAAA,QAC7D,+CAA+C,EAAE,CAAA,EAAA;AAAA,OACnD;AACA,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAC3C,MAAA,OAAO,UAAA,CAAW,CAAA,UAAA,EAAa,IAAA,CAAK,EAAE,WAAM,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,EAAE,IAAI,IAAA,CAAK,EAAA,EAAI,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AAAA,IACjG,CAAA;AAAA,IACA,IAAA;AAAA,IACA,CAAC,IAAA,KAAS,MAAA,CAAO,OAAO,IAAA,CAAK,EAAA,IAAM,EAAE,CAAC;AAAA,GACxC;AAEA,EAAA,GAAA;AAAA,IACE,wBAAA;AAAA,IACA,oHAAA;AAAA,IACA;AAAA,MACE,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,MACrB,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,0CAAA;AAA2C,KACrF;AAAA,IACA,CAAC,MAAM,SAAS,CAAA;AAAA,IAChB,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,sBAAsB,OAAA,CAAQ,mBAAA;AACpC,MAAA,IAAI,CAAC,mBAAA,EAAqB,OAAO,WAAA,CAAY,2CAA2C,CAAA;AACxF,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,IAAA,CAAK,EAAE,CAAA;AACzB,MAAA,MAAM,OAAA,GAAW,KAAK,OAAA,IAAW,OAAO,KAAK,OAAA,KAAY,QAAA,GAAY,IAAA,CAAK,OAAA,GAAqC,EAAC;AAChH,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,MAAM,mBAAA,CAAoB,IAAI,OAAO,CAAA,EAAG,CAAA,8BAAA,EAAiC,EAAE,CAAA,EAAA,CAAI,CAAA;AACzG,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAC3C,MAAA,OAAO,WAAW,CAAA,mBAAA,EAAsB,EAAE,CAAA,CAAA,EAAI,EAAE,IAAI,CAAA;AAAA,IACtD,CAAA;AAAA,IACA,IAAA;AAAA,IACA,CAAC,IAAA,KAAS,MAAA,CAAO,OAAO,IAAA,CAAK,EAAA,IAAM,EAAE,CAAC;AAAA,GACxC;AAEA,EAAA,GAAA;AAAA,IACE,kBAAA;AAAA,IACA,wGAAA;AAAA,IACA,EAAE,MAAA,EAAQ,EAAE,MAAM,QAAA,EAAU,WAAA,EAAa,0BAAyB,EAAE;AAAA,IACpE,CAAC,QAAQ,CAAA;AAAA,IACT,OAAO,IAAA,KAAS;AACd,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,MAAA,IAAU,EAAE,CAAA;AACzC,MAAA,MAAM,OAAA,GAAU,QAAQ,WAAA,EAAY;AACpC,MAAA,IAAI,CAAC,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,QAAQ,CAAA,EAAG;AAC3C,QAAA,OAAO,WAAA,CAAY,CAAA,8BAAA,EAAiC,QAAQ,CAAA,2BAAA,CAA6B,CAAA;AAAA,MAC3F;AACA,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,MAAM,OAAA,CAAQ,UAAU,QAAQ,CAAA,EAAG,CAAA,mCAAA,EAAsC,QAAQ,CAAA,EAAA,CAAI,CAAA;AAC/G,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,EAAI,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAC3C,MAAA,OAAO,WAAW,CAAA,aAAA,EAAgB,QAAQ,IAAI,EAAE,MAAA,EAAQ,UAAU,CAAA;AAAA,IACpE,CAAA;AAAA,IACA,IAAA;AAAA,IACA,CAAC,IAAA,KAAS,MAAA,CAAO,OAAO,IAAA,CAAK,MAAA,IAAU,EAAE,CAAC;AAAA,GAC5C;AAEA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,SAAA;AAAA,IACJ,KAAA,EAAO,SAAA;AAAA,IACP,SAAS,MAAM;AAAE,MAAA,KAAA,MAAW,CAAA,IAAK,WAAW,CAAA,EAAE;AAAA,IAAG;AAAA,GACnD;AACF","file":"chunk-HOZCDDRA.js","sourcesContent":["import { textResult, errorResult } from \"../mcp/server\";\nimport type { ToolHost } from \"../mcp/tool-host\";\nimport type { JsonObject } from \"../mcp/types\";\nimport type { Bridge } from \"./types\";\nimport { wrapToolWithActivity } from \"../presence/wrap-tool-with-activity\";\nimport { ensureUndoToolsRegistered } from \"../undo/undo-tools\";\nimport { emitActivity } from \"../presence/registry\";\nimport type { AgentTarget } from \"../presence/types\";\n\n/**\n * Loose snapshot of a screen — what's in fancy-screens' ScreenMeta plus\n * the host's optional activity status. Kept here so this bridge has no\n * hard dep on fancy-screens.\n */\nexport type ScreenSnapshot = {\n id: string;\n title?: string;\n /** Whether this screen is the one the human / agent is looking at. */\n active: boolean;\n /** Optional category / kind (e.g. \"form\", \"whiteboard\"). */\n kind?: string;\n};\n\n/**\n * Spec for a dynamically-created screen. The host's `createScreen`\n * implementation looks up `kind` in its template registry and instantiates\n * the matching surface (form / whiteboard / sheet / chart / markdown / etc.).\n */\nexport type ScreenCreateSpec = {\n id: string;\n title?: string;\n /** Template kind. Hosts decide the catalog. */\n kind: string;\n /** Template-specific config. Form: { fields }. Sheet: { headers }. etc. */\n config?: Record<string, unknown>;\n};\n\n/**\n * What a mutating adapter method may resolve to, to acknowledge (or refuse)\n * a commit. `{ ok: false }` is a refusal the agent can act on: `error` is\n * shown to the agent verbatim, so keep it a human-readable sentence, never a\n * stack trace or an internal path. `code` is a machine-readable hint\n * (e.g. `\"conflict\"`) for the host's own use; it is not shown to the agent.\n */\nexport type ScreensAdapterResult = { ok: true } | { ok: false; error: string; code?: string };\n\n/**\n * Return type of every mutating adapter method. A sync `void` return keeps\n * the original behaviour. A Promise makes the bridge WAIT: the tool reports\n * success, and emits its AgentActivity event, only after the promise\n * resolves. A rejection or an `{ ok: false }` result becomes an error result\n * and emits no activity.\n */\nexport type ScreensAdapterCommit = void | Promise<void | ScreensAdapterResult>;\n\n/**\n * Adapter exposes the host's screen-navigation surface to the bridge.\n * Hosts wire this up against react-router, a custom tab state, or the\n * fancy-screens registry — wherever \"current screen\" lives.\n *\n * The optional create / destroy / update hooks let agents author screens\n * dynamically against a host-defined template catalog.\n *\n * Every mutating method may be sync (`void`) or async (`Promise`); see\n * {@link ScreensAdapterCommit}. Return a Promise when the host's commit is\n * authoritative — persisted, or able to refuse or conflict — so the agent is\n * told the truth instead of \"success\" before anything was committed.\n *\n * This bridge pushes NO undo entries: `agent_undo` cannot revert a screens\n * tool. A host that needs undo for navigation or screen authoring must keep\n * its own history.\n */\nexport type ScreensBridgeAdapter = {\n /** List every available screen. */\n listScreens: () => ScreenSnapshot[];\n /** Read which screen is currently active. */\n getActive: () => string | null;\n /** Navigate to a screen by id. Host updates router / tab state. */\n setActive: (screenId: string) => ScreensAdapterCommit;\n /** Optional: instantiate a new screen from a template + config. */\n createScreen?: (spec: ScreenCreateSpec) => ScreensAdapterCommit;\n /** Optional: remove a previously-created screen. */\n destroyScreen?: (screenId: string) => ScreensAdapterCommit;\n /** Optional: shallow-merge new config into an existing screen. */\n updateScreenContent?: (screenId: string, partial: Record<string, unknown>) => ScreensAdapterCommit;\n /** Optional: enumerate the kinds the host knows how to instantiate. */\n listKinds?: () => Array<{ kind: string; label?: string; description?: string; configSchema?: unknown }>;\n};\n\nexport type ScreensBridgeOptions = {\n adapter: ScreensBridgeAdapter;\n agent?: { id: string; name?: string; color?: string };\n};\n\nconst DEFAULT_AGENT = { id: \"agent\", name: \"Agent\", color: \"#a855f7\" };\n\n/**\n * The agent-facing text for a host failure. Only the message is used — never\n * `stack`, which carries file paths — matching the other bridges. A message\n * that itself embeds stack frames is cut at the first frame. A non-Error\n * rejection (a plain object, a response) has no reliable message, so it gets\n * the fallback rather than \"[object Object]\".\n */\nfunction hostErrorMessage(e: unknown, fallback: string): string {\n let msg: string | undefined;\n if (e instanceof Error) msg = e.message;\n else if (typeof e === \"string\") msg = e;\n else if (e && typeof e === \"object\" && typeof (e as { message?: unknown }).message === \"string\") {\n msg = (e as { message: string }).message;\n }\n const firstFrame = msg ? msg.search(/\\n\\s+at\\s/) : -1;\n if (msg && firstFrame >= 0) msg = msg.slice(0, firstFrame);\n msg = msg?.trim();\n return msg ? msg : fallback;\n}\n\ntype CommitOutcome = { ok: true } | { ok: false; error: string };\n\n/** Run one adapter commit, await it, and normalise every way it can fail. */\nasync function commit(run: () => ScreensAdapterCommit, fallback: string): Promise<CommitOutcome> {\n try {\n const r = await run();\n if (r && typeof r === \"object\" && r.ok === false) {\n return { ok: false, error: hostErrorMessage(r.error, fallback) };\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: hostErrorMessage(e, fallback) };\n }\n}\n\n/**\n * registerScreensBridge — top-level navigation bridge so agents can\n * switch between screens (`screens_navigate`) and discover what surfaces\n * exist (`screens_list`, `screens_describe_active`).\n *\n * Pair with the per-surface bridges (whiteboard, form, sheet, etc.) so\n * the agent has both navigation and per-screen control.\n */\nexport function registerScreensBridge(\n host: ToolHost,\n options: ScreensBridgeOptions,\n): Bridge {\n const { adapter } = options;\n const agent = { ...DEFAULT_AGENT, ...(options.agent ?? {}) };\n const disposers: Array<() => void> = [];\n\n // agent_undo / agent_redo / agent_history are registered whenever any bridge\n // mounts, so undo availability doesn't hinge on which bridges are co-present.\n ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });\n\n const target = (screenId: string): AgentTarget => ({\n kind: \"screens\",\n screenId,\n label: `Screen ${screenId}`,\n });\n\n const reg = (\n name: string,\n description: string,\n properties: Record<string, unknown>,\n required: string[],\n handler: (args: JsonObject) => Promise<any> | any,\n isMutation: boolean,\n targetFromArgs?: (args: JsonObject) => AgentTarget,\n ) => {\n const wrapped = async (args: JsonObject) => {\n try { return await handler(args); }\n catch (e) { return errorResult(hostErrorMessage(e, `${name} failed.`)); }\n };\n const final = isMutation\n ? wrapToolWithActivity(wrapped, {\n toolName: name, agent, kind: \"screens\",\n resolveTarget: ({ args }) => targetFromArgs?.(args) ?? null,\n })\n : wrapped;\n disposers.push(\n host.registerTool(\n { name, description, inputSchema: { type: \"object\", properties: properties as any, required, additionalProperties: false } },\n final as any,\n ),\n );\n };\n\n reg(\n \"screens_list\",\n \"List every screen the host has registered. Returns id, title, active flag, and optional kind.\",\n {},\n [],\n () => {\n const screens = adapter.listScreens();\n const text = screens\n .map((s) => `${s.active ? \"▸\" : \" \"} ${s.id}${s.title ? ` — ${s.title}` : \"\"}${s.kind ? ` [${s.kind}]` : \"\"}`)\n .join(\"\\n\");\n return textResult(text || \"(no screens)\", { screens, active: adapter.getActive() });\n },\n false,\n );\n\n reg(\n \"screens_describe_active\",\n \"Get the currently-active screen id (or null).\",\n {},\n [],\n () => {\n const active = adapter.getActive();\n return textResult(active ?? \"(none)\", { active });\n },\n false,\n );\n\n reg(\n \"screens_list_kinds\",\n \"List the screen kinds (templates) the host knows how to instantiate. Use this before screens_create to know what's available.\",\n {},\n [],\n () => {\n if (!adapter.listKinds) return errorResult(\"Host did not register a kind catalog. Cannot create screens dynamically.\");\n const kinds = adapter.listKinds();\n const text = kinds.map((k) => `${k.kind}${k.label ? ` — ${k.label}` : \"\"}${k.description ? ` (${k.description})` : \"\"}`).join(\"\\n\");\n return textResult(text || \"(no kinds registered)\", kinds);\n },\n false,\n );\n\n reg(\n \"screens_create\",\n \"Instantiate a new screen from a template kind + config. Switches the active view to the new screen.\",\n {\n id: { type: \"string\", description: \"Stable screen id. Must be unique.\" },\n title: { type: \"string\" },\n kind: { type: \"string\", description: \"Template kind — call screens_list_kinds for the catalog.\" },\n config: { type: \"object\", description: \"Template-specific config (e.g. { fields: [...] } for a form).\" },\n },\n [\"id\", \"kind\"],\n async (args) => {\n const createScreen = adapter.createScreen;\n if (!createScreen) return errorResult(\"Host did not provide createScreen.\");\n const id = String(args.id);\n const kind = String(args.kind);\n if (adapter.listScreens().find((s) => s.id === id)) {\n return errorResult(`Screen ${id} already exists. Use screens_destroy first or pick a fresh id.`);\n }\n const created = await commit(() => createScreen({\n id,\n title: typeof args.title === \"string\" ? args.title : undefined,\n kind,\n config: (args.config && typeof args.config === \"object\") ? args.config as Record<string, unknown> : undefined,\n }), `Host failed to create screen \"${id}\".`);\n // Nothing was created, so there is nothing to activate.\n if (!created.ok) return errorResult(created.error);\n\n const activated = await commit(() => adapter.setActive(id), `Host failed to activate screen \"${id}\".`);\n if (!activated.ok) {\n // Partial commit: the screen EXISTS but is not showing. Reported as an\n // error so an agent branching on isError does not go on to tell the\n // human \"look at the new screen\". The create DID commit, though, and\n // every committed mutation must be observable, so its activity is\n // emitted here by hand (wrapToolWithActivity skips error results).\n const state = { id, kind, created: true, activated: false };\n emitActivity({\n agentId: agent.id,\n agentName: agent.name,\n agentColor: agent.color,\n target: target(id),\n action: \"screens_create\",\n timestamp: Date.now(),\n meta: state,\n });\n return {\n ...errorResult(\n `Screen \"${id}\" was created but not activated: ${activated.error} ` +\n \"The screen exists; do not call screens_create again. \" +\n \"Call screens_navigate to show it, or screens_destroy to remove it.\",\n ),\n structuredContent: state,\n };\n }\n return textResult(`Created ${kind} screen \"${id}\"`, { id, kind });\n },\n true,\n (args) => target(String(args.id ?? \"\")),\n );\n\n reg(\n \"screens_destroy\",\n \"Remove a previously-created screen. Active screen falls back to the first remaining one (or null).\",\n { id: { type: \"string\" } },\n [\"id\"],\n async (args) => {\n const destroyScreen = adapter.destroyScreen;\n if (!destroyScreen) return errorResult(\"Host did not provide destroyScreen.\");\n const id = String(args.id);\n if (!adapter.listScreens().find((s) => s.id === id)) {\n return errorResult(`No screen with id ${id}`);\n }\n const done = await commit(() => destroyScreen(id), `Host failed to destroy screen \"${id}\".`);\n if (!done.ok) return errorResult(done.error);\n return textResult(`Destroyed screen ${id}`, { id });\n },\n true,\n (args) => target(String(args.id ?? \"\")),\n );\n\n reg(\n \"screens_set_layout\",\n \"Change the layout of an existing composite screen. Layouts: 'single', 'split-h' (left/right), 'split-v' (top/bottom), 'grid-2x2', 'stack' (tabs).\",\n {\n id: { type: \"string\" },\n layout: { type: \"string\", enum: [\"single\", \"split-h\", \"split-v\", \"grid-2x2\", \"stack\"] },\n },\n [\"id\", \"layout\"],\n async (args) => {\n const updateScreenContent = adapter.updateScreenContent;\n if (!updateScreenContent) return errorResult(\"Host did not provide updateScreenContent.\");\n const id = String(args.id);\n const done = await commit(\n () => updateScreenContent(id, { layout: String(args.layout) }),\n `Host failed to change the layout of screen \"${id}\".`,\n );\n if (!done.ok) return errorResult(done.error);\n return textResult(`Layout of ${args.id} → ${args.layout}`, { id: args.id, layout: args.layout });\n },\n true,\n (args) => target(String(args.id ?? \"\")),\n );\n\n reg(\n \"screens_update_content\",\n \"Merge new config into an existing screen (e.g. add a field to a form, append a sheet column, change chart series).\",\n {\n id: { type: \"string\" },\n partial: { type: \"object\", description: \"Shallow-merged into the screen's config.\" },\n },\n [\"id\", \"partial\"],\n async (args) => {\n const updateScreenContent = adapter.updateScreenContent;\n if (!updateScreenContent) return errorResult(\"Host did not provide updateScreenContent.\");\n const id = String(args.id);\n const partial = (args.partial && typeof args.partial === \"object\") ? args.partial as Record<string, unknown> : {};\n const done = await commit(() => updateScreenContent(id, partial), `Host failed to update screen \"${id}\".`);\n if (!done.ok) return errorResult(done.error);\n return textResult(`Updated content of ${id}`, { id });\n },\n true,\n (args) => target(String(args.id ?? \"\")),\n );\n\n reg(\n \"screens_navigate\",\n \"Switch the human's view to a different screen. The host updates its router / tab state and re-renders.\",\n { screen: { type: \"string\", description: \"Screen id to activate.\" } },\n [\"screen\"],\n async (args) => {\n const screenId = String(args.screen ?? \"\");\n const screens = adapter.listScreens();\n if (!screens.find((s) => s.id === screenId)) {\n return errorResult(`No screen registered with id \"${screenId}\". Call screens_list first.`);\n }\n const done = await commit(() => adapter.setActive(screenId), `Host failed to navigate to screen \"${screenId}\".`);\n if (!done.ok) return errorResult(done.error);\n return textResult(`Navigated to ${screenId}`, { screen: screenId });\n },\n true,\n (args) => target(String(args.screen ?? \"\")),\n );\n\n return {\n id: \"screens\",\n title: \"Screens\",\n dispose: () => { for (const d of disposers) d(); },\n };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1460,7 +1460,7 @@ function registerGridBridge(host, options) {
1460
1460
  throw new Error(`Unknown column "${id}". Available: ${known.join(", ")}`);
1461
1461
  }
1462
1462
  };
1463
- const commit = (next, action, label, previous) => {
1463
+ const commit2 = (next, action, label, previous) => {
1464
1464
  adapter.setState(next);
1465
1465
  fancyAutoCommon.pushUndoEntry(agent.id, {
1466
1466
  timestamp: Date.now(),
@@ -1521,7 +1521,7 @@ function registerGridBridge(host, options) {
1521
1521
  return { id: String(s.id), desc: Boolean(s.desc) };
1522
1522
  });
1523
1523
  const previous = adapter.getState();
1524
- commit({ ...previous, sorting }, "grid_sort", `Sorted by ${sorting.map((s) => s.id).join(", ") || "nothing"}`, previous);
1524
+ commit2({ ...previous, sorting }, "grid_sort", `Sorted by ${sorting.map((s) => s.id).join(", ") || "nothing"}`, previous);
1525
1525
  return textResult(`Sorted by ${sorting.map((s) => `${s.id}${s.desc ? " desc" : ""}`).join(", ") || "nothing"}.`);
1526
1526
  },
1527
1527
  true,
@@ -1550,7 +1550,7 @@ function registerGridBridge(host, options) {
1550
1550
  return { id: String(f.id), value: String(f.value) };
1551
1551
  });
1552
1552
  const previous = adapter.getState();
1553
- commit({ ...previous, filters }, "grid_filter", `Filtered ${filters.map((f) => f.id).join(", ") || "cleared"}`, previous);
1553
+ commit2({ ...previous, filters }, "grid_filter", `Filtered ${filters.map((f) => f.id).join(", ") || "cleared"}`, previous);
1554
1554
  return textResult(`Filtering on ${filters.map((f) => `${f.id}=${f.value}`).join(", ") || "nothing"}.`);
1555
1555
  },
1556
1556
  true,
@@ -1573,7 +1573,7 @@ function registerGridBridge(host, options) {
1573
1573
  const previous = adapter.getState();
1574
1574
  const rowSelection = {};
1575
1575
  for (const id of ids) rowSelection[id] = true;
1576
- commit({ ...previous, rowSelection }, "grid_select_rows", `Selected ${ids.length} row${ids.length === 1 ? "" : "s"}`, previous);
1576
+ commit2({ ...previous, rowSelection }, "grid_select_rows", `Selected ${ids.length} row${ids.length === 1 ? "" : "s"}`, previous);
1577
1577
  return textResult(`Selected ${ids.length} row${ids.length === 1 ? "" : "s"}.`);
1578
1578
  },
1579
1579
  true
@@ -1816,7 +1816,31 @@ function parseTriple(v) {
1816
1816
  }
1817
1817
 
1818
1818
  // src/bridges/screens.ts
1819
+ init_registry();
1819
1820
  var DEFAULT_AGENT7 = { id: "agent", name: "Agent", color: "#a855f7" };
1821
+ function hostErrorMessage(e, fallback) {
1822
+ let msg;
1823
+ if (e instanceof Error) msg = e.message;
1824
+ else if (typeof e === "string") msg = e;
1825
+ else if (e && typeof e === "object" && typeof e.message === "string") {
1826
+ msg = e.message;
1827
+ }
1828
+ const firstFrame = msg ? msg.search(/\n\s+at\s/) : -1;
1829
+ if (msg && firstFrame >= 0) msg = msg.slice(0, firstFrame);
1830
+ msg = msg?.trim();
1831
+ return msg ? msg : fallback;
1832
+ }
1833
+ async function commit(run, fallback) {
1834
+ try {
1835
+ const r = await run();
1836
+ if (r && typeof r === "object" && r.ok === false) {
1837
+ return { ok: false, error: hostErrorMessage(r.error, fallback) };
1838
+ }
1839
+ return { ok: true };
1840
+ } catch (e) {
1841
+ return { ok: false, error: hostErrorMessage(e, fallback) };
1842
+ }
1843
+ }
1820
1844
  function registerScreensBridge(host, options) {
1821
1845
  const { adapter } = options;
1822
1846
  const agent = { ...DEFAULT_AGENT7, ...options.agent ?? {} };
@@ -1832,7 +1856,7 @@ function registerScreensBridge(host, options) {
1832
1856
  try {
1833
1857
  return await handler(args);
1834
1858
  } catch (e) {
1835
- return errorResult(e instanceof Error ? e.message : String(e));
1859
+ return errorResult(hostErrorMessage(e, `${name} failed.`));
1836
1860
  }
1837
1861
  };
1838
1862
  const final = isMutation ? wrapToolWithActivity(wrapped, {
@@ -1894,20 +1918,40 @@ function registerScreensBridge(host, options) {
1894
1918
  config: { type: "object", description: "Template-specific config (e.g. { fields: [...] } for a form)." }
1895
1919
  },
1896
1920
  ["id", "kind"],
1897
- (args) => {
1898
- if (!adapter.createScreen) return errorResult("Host did not provide createScreen.");
1921
+ async (args) => {
1922
+ const createScreen = adapter.createScreen;
1923
+ if (!createScreen) return errorResult("Host did not provide createScreen.");
1899
1924
  const id = String(args.id);
1900
1925
  const kind = String(args.kind);
1901
1926
  if (adapter.listScreens().find((s) => s.id === id)) {
1902
1927
  return errorResult(`Screen ${id} already exists. Use screens_destroy first or pick a fresh id.`);
1903
1928
  }
1904
- adapter.createScreen({
1929
+ const created = await commit(() => createScreen({
1905
1930
  id,
1906
1931
  title: typeof args.title === "string" ? args.title : void 0,
1907
1932
  kind,
1908
1933
  config: args.config && typeof args.config === "object" ? args.config : void 0
1909
- });
1910
- adapter.setActive(id);
1934
+ }), `Host failed to create screen "${id}".`);
1935
+ if (!created.ok) return errorResult(created.error);
1936
+ const activated = await commit(() => adapter.setActive(id), `Host failed to activate screen "${id}".`);
1937
+ if (!activated.ok) {
1938
+ const state = { id, kind, created: true, activated: false };
1939
+ fancyAutoCommon.emitActivity({
1940
+ agentId: agent.id,
1941
+ agentName: agent.name,
1942
+ agentColor: agent.color,
1943
+ target: target(id),
1944
+ action: "screens_create",
1945
+ timestamp: Date.now(),
1946
+ meta: state
1947
+ });
1948
+ return {
1949
+ ...errorResult(
1950
+ `Screen "${id}" was created but not activated: ${activated.error} The screen exists; do not call screens_create again. Call screens_navigate to show it, or screens_destroy to remove it.`
1951
+ ),
1952
+ structuredContent: state
1953
+ };
1954
+ }
1911
1955
  return textResult(`Created ${kind} screen "${id}"`, { id, kind });
1912
1956
  },
1913
1957
  true,
@@ -1918,13 +1962,15 @@ function registerScreensBridge(host, options) {
1918
1962
  "Remove a previously-created screen. Active screen falls back to the first remaining one (or null).",
1919
1963
  { id: { type: "string" } },
1920
1964
  ["id"],
1921
- (args) => {
1922
- if (!adapter.destroyScreen) return errorResult("Host did not provide destroyScreen.");
1965
+ async (args) => {
1966
+ const destroyScreen = adapter.destroyScreen;
1967
+ if (!destroyScreen) return errorResult("Host did not provide destroyScreen.");
1923
1968
  const id = String(args.id);
1924
1969
  if (!adapter.listScreens().find((s) => s.id === id)) {
1925
1970
  return errorResult(`No screen with id ${id}`);
1926
1971
  }
1927
- adapter.destroyScreen(id);
1972
+ const done = await commit(() => destroyScreen(id), `Host failed to destroy screen "${id}".`);
1973
+ if (!done.ok) return errorResult(done.error);
1928
1974
  return textResult(`Destroyed screen ${id}`, { id });
1929
1975
  },
1930
1976
  true,
@@ -1938,9 +1984,15 @@ function registerScreensBridge(host, options) {
1938
1984
  layout: { type: "string", enum: ["single", "split-h", "split-v", "grid-2x2", "stack"] }
1939
1985
  },
1940
1986
  ["id", "layout"],
1941
- (args) => {
1942
- if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
1943
- adapter.updateScreenContent(String(args.id), { layout: String(args.layout) });
1987
+ async (args) => {
1988
+ const updateScreenContent = adapter.updateScreenContent;
1989
+ if (!updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
1990
+ const id = String(args.id);
1991
+ const done = await commit(
1992
+ () => updateScreenContent(id, { layout: String(args.layout) }),
1993
+ `Host failed to change the layout of screen "${id}".`
1994
+ );
1995
+ if (!done.ok) return errorResult(done.error);
1944
1996
  return textResult(`Layout of ${args.id} \u2192 ${args.layout}`, { id: args.id, layout: args.layout });
1945
1997
  },
1946
1998
  true,
@@ -1954,11 +2006,13 @@ function registerScreensBridge(host, options) {
1954
2006
  partial: { type: "object", description: "Shallow-merged into the screen's config." }
1955
2007
  },
1956
2008
  ["id", "partial"],
1957
- (args) => {
1958
- if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
2009
+ async (args) => {
2010
+ const updateScreenContent = adapter.updateScreenContent;
2011
+ if (!updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
1959
2012
  const id = String(args.id);
1960
2013
  const partial = args.partial && typeof args.partial === "object" ? args.partial : {};
1961
- adapter.updateScreenContent(id, partial);
2014
+ const done = await commit(() => updateScreenContent(id, partial), `Host failed to update screen "${id}".`);
2015
+ if (!done.ok) return errorResult(done.error);
1962
2016
  return textResult(`Updated content of ${id}`, { id });
1963
2017
  },
1964
2018
  true,
@@ -1969,13 +2023,14 @@ function registerScreensBridge(host, options) {
1969
2023
  "Switch the human's view to a different screen. The host updates its router / tab state and re-renders.",
1970
2024
  { screen: { type: "string", description: "Screen id to activate." } },
1971
2025
  ["screen"],
1972
- (args) => {
2026
+ async (args) => {
1973
2027
  const screenId = String(args.screen ?? "");
1974
2028
  const screens = adapter.listScreens();
1975
2029
  if (!screens.find((s) => s.id === screenId)) {
1976
2030
  return errorResult(`No screen registered with id "${screenId}". Call screens_list first.`);
1977
2031
  }
1978
- adapter.setActive(screenId);
2032
+ const done = await commit(() => adapter.setActive(screenId), `Host failed to navigate to screen "${screenId}".`);
2033
+ if (!done.ok) return errorResult(done.error);
1979
2034
  return textResult(`Navigated to ${screenId}`, { screen: screenId });
1980
2035
  },
1981
2036
  true,
@@ -4446,7 +4501,7 @@ function registerDocBridge(host, options) {
4446
4501
  };
4447
4502
  const target = (elementId) => ({ kind: surface, screenId: adapter.screenId, elementId });
4448
4503
  const pending = /* @__PURE__ */ new Map();
4449
- function commit(op, label, targetId) {
4504
+ function commit2(op, label, targetId) {
4450
4505
  const prev = adapter.get();
4451
4506
  const next = reducer.reduce(prev, op);
4452
4507
  adapter.set(next);
@@ -4467,7 +4522,7 @@ function registerDocBridge(host, options) {
4467
4522
  options.onPending?.({ id: pendingId, op, label });
4468
4523
  return { staged: true, id: targetId, pendingId };
4469
4524
  }
4470
- commit(op, label, targetId);
4525
+ commit2(op, label, targetId);
4471
4526
  return { staged: false, id: targetId };
4472
4527
  }
4473
4528
  const reg = (verb, description, properties, required, handler, isMutation, resolveTarget) => {
@@ -4647,7 +4702,7 @@ function registerDocBridge(host, options) {
4647
4702
  const p = pending.get(id);
4648
4703
  if (!p) throw new Error(`No pending op ${id}`);
4649
4704
  pending.delete(id);
4650
- commit(p.op, p.label, p.targetId);
4705
+ commit2(p.op, p.label, p.targetId);
4651
4706
  return { ok: true, applied: p.label, id: p.targetId, _text: `applied ${p.label}` };
4652
4707
  },
4653
4708
  true,