@agentproto/apps 0.11.0 → 0.13.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.
Files changed (70) hide show
  1. package/dist/agents-overview/panel.mjs +39 -67
  2. package/dist/agents-overview/panel.mjs.map +1 -1
  3. package/dist/agents-overview.mjs +38 -68
  4. package/dist/agents-overview.mjs.map +1 -1
  5. package/dist/bin/sync.d.ts.map +1 -1
  6. package/dist/bin/sync.mjs +2374 -2
  7. package/dist/bin/sync.mjs.map +1 -1
  8. package/dist/bureau-sessions/panel.mjs +39 -67
  9. package/dist/bureau-sessions/panel.mjs.map +1 -1
  10. package/dist/bureau-sessions.mjs +38 -68
  11. package/dist/bureau-sessions.mjs.map +1 -1
  12. package/dist/config/fragment.d.ts +50 -0
  13. package/dist/config/fragment.d.ts.map +1 -0
  14. package/dist/config/index.d.ts +23 -0
  15. package/dist/config/index.d.ts.map +1 -0
  16. package/dist/config/ui.d.ts +43 -0
  17. package/dist/config/ui.d.ts.map +1 -0
  18. package/dist/config.mjs +2373 -0
  19. package/dist/config.mjs.map +1 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.mjs +3200 -150
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/live-session/logic.d.ts +2 -0
  25. package/dist/live-session/logic.d.ts.map +1 -1
  26. package/dist/live-session/panel.d.ts.map +1 -1
  27. package/dist/live-session/panel.mjs +52 -74
  28. package/dist/live-session/panel.mjs.map +1 -1
  29. package/dist/live-session.mjs +51 -75
  30. package/dist/live-session.mjs.map +1 -1
  31. package/dist/panel-bridge.d.ts +11 -6
  32. package/dist/panel-bridge.d.ts.map +1 -1
  33. package/dist/session-chat/blob-embed.d.ts +53 -0
  34. package/dist/session-chat/blob-embed.d.ts.map +1 -0
  35. package/dist/session-chat/index.d.ts +13 -0
  36. package/dist/session-chat/index.d.ts.map +1 -1
  37. package/dist/session-chat/panel.d.ts +38 -2
  38. package/dist/session-chat/panel.d.ts.map +1 -1
  39. package/dist/session-chat/panel.mjs +556 -6
  40. package/dist/session-chat/panel.mjs.map +1 -1
  41. package/dist/session-chat.mjs +591 -14
  42. package/dist/session-chat.mjs.map +1 -1
  43. package/dist/session-story/panel.mjs +39 -67
  44. package/dist/session-story/panel.mjs.map +1 -1
  45. package/dist/session-story.mjs +38 -68
  46. package/dist/session-story.mjs.map +1 -1
  47. package/dist/session-viewer/ui.d.ts.map +1 -1
  48. package/dist/session-viewer.mjs +10 -0
  49. package/dist/session-viewer.mjs.map +1 -1
  50. package/dist/sessions-panel/panel.d.ts.map +1 -1
  51. package/dist/sessions-panel/panel.mjs +40 -67
  52. package/dist/sessions-panel/panel.mjs.map +1 -1
  53. package/dist/sessions-panel.mjs +39 -68
  54. package/dist/sessions-panel.mjs.map +1 -1
  55. package/dist/work-board/panel.d.ts +1 -1
  56. package/dist/work-board/panel.d.ts.map +1 -1
  57. package/dist/work-board/panel.generated.d.ts +1 -1
  58. package/dist/work-board/panel.generated.d.ts.map +1 -1
  59. package/dist/work-board/panel.mjs +318 -62
  60. package/dist/work-board/panel.mjs.map +1 -1
  61. package/dist/work-board.mjs +318 -62
  62. package/dist/work-board.mjs.map +1 -1
  63. package/package.json +12 -4
  64. package/repo-maintenance/.agentproto/APP.md +22 -0
  65. package/repo-maintenance/.agentproto/agents/repo-maintenance-reviewer/AGENT.md +142 -0
  66. package/repo-maintenance/.agentproto/workflows/maintain/WORKFLOW.md +262 -0
  67. package/repo-maintenance/.agentproto/workflows/maintain/entry.mjs +672 -0
  68. package/repo-maintenance/README.md +79 -0
  69. package/repo-maintenance/routines/repo-maintenance-daily/ROUTINE.md +77 -0
  70. package/repo-maintenance/routines/repo-maintenance-weekly/ROUTINE.md +82 -0
@@ -22,12 +22,29 @@ var WORK_BOARD_HTML_GENERATED = `<!DOCTYPE html>
22
22
  // JSON-RPC 2.0 over window.parent.postMessage \xB7 spec 2026-01-26
23
23
  var _nextId = 1, _pending = {}, _notifyHandlers = [];
24
24
  var _hostContext = null, _hostContextHandlers = [];
25
+ var _hostCaps = null;
25
26
  var _standaloneApp = null;
26
27
  function _isStandalone(){
27
28
  return window.parent === window && !!window.McpApp && typeof window.McpApp.connect === 'function';
28
29
  }
30
+ // \u2500\u2500 Per-boot embed token (runtime embed-tokens.ts) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
31
+ // Panels that iframe the daemon's standalone app host carry this proof so
32
+ // GET /apps/:appId/ui?embed=1&et=... can drop its anti-framing headers for
33
+ // MCP-Apps hosts whose widget context is an opaque origin (Claude Desktop).
34
+ // registerMcpApps replaces the placeholder with a real per-boot token when
35
+ // serving the panel as a resource; every other render (tests, standalone
36
+ // tab, VS Code HTTP-iframe panel \u2014 the latter two already pass
37
+ // iframeEmbedAllowed's origin checks) keeps the literal, and
38
+ // withEmbedToken() then degrades to an identity function.
39
+ window.__AGENPROTO_EMBED_TOKEN__ = "__AGENPROTO_EMBED_TOKEN__";
40
+ function withEmbedToken(url){
41
+ var t = window.__AGENPROTO_EMBED_TOKEN__;
42
+ if (!t || t === '__AGENPROTO_EMBED_TOKEN__') return url;
43
+ return url + (url.indexOf('?') >= 0 ? '&' : '?') + 'et=' + encodeURIComponent(t);
44
+ }
29
45
  function post(msg){ window.parent.postMessage(msg, '*'); }
30
46
  function getHostContext(){ return _hostContext; }
47
+ function getHostCapabilities(){ return _hostCaps; }
31
48
  function onHostContext(cb){
32
49
  _hostContextHandlers.push(cb);
33
50
  // Replay the last context so a late subscriber isn't stuck blind.
@@ -76,7 +93,7 @@ function initBridge(){
76
93
  return window.McpApp.connect().then(function(conn){
77
94
  _standaloneApp = conn;
78
95
  // No host to advertise a hostContext \u2014 default to inline with no
79
- // other modes available, which keeps the #dm/#pin toggle buttons
96
+ // other modes available, which keeps the display-mode toggle buttons
80
97
  // hidden (they only show for modes hostContext.availableDisplayModes
81
98
  // lists) instead of rendering two buttons with nothing to switch to.
82
99
  _setHostContext({displayMode: 'inline', availableDisplayModes: []});
@@ -90,12 +107,20 @@ function initBridge(){
90
107
  // The initialize result carries the initial hostContext (displayMode +
91
108
  // availableDisplayModes) \u2014 capture it before notifying the host.
92
109
  if (result && result.hostContext) _setHostContext(result.hostContext);
110
+ // McpUiInitializeResult.hostCapabilities (ext-apps spec) \u2014 e.g. openLinks,
111
+ // "Host supports opening external URLs". Captured once, read via
112
+ // getHostCapabilities() by panels that need to branch on it.
113
+ if (result && result.hostCapabilities) _hostCaps = result.hostCapabilities;
93
114
  rpcNotify('ui/notifications/initialized', {});
94
115
  });
95
116
  }
96
117
  function requestDisplayMode(mode){
97
118
  return rpcRequest('ui/request-display-mode', {mode: mode});
98
119
  }
120
+ function openLink(url){
121
+ if (_standaloneApp && typeof _standaloneApp.openLink === 'function') return _standaloneApp.openLink(url);
122
+ return rpcRequest('ui/open-link', {url: url});
123
+ }
99
124
  function callTool(name, args){
100
125
  var raw = _standaloneApp
101
126
  ? _standaloneApp.callTool(name, args || {})
@@ -110,71 +135,302 @@ function callTool(name, args){
110
135
  });
111
136
  }
112
137
 
113
- // \u2500\u2500 Display-mode toggle buttons (NO auto-request) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
114
- // Injected by the shared bridge so every panel gets them without touching
115
- // its own markup. Mirrors guilde canvas.app.ts canvasShellHtml(): the
116
- // panel stays inline by default; the user expands on demand. Buttons only
117
- // appear for modes the host advertises in hostContext.availableDisplayModes.
118
- (function(){
119
- function mount(){
120
- var style = document.createElement('style');
121
- style.textContent = '#dm,#pin{display:none;position:fixed;top:8px;z-index:10;'
122
- + 'border:1px solid #d0d0d0;background:#fff;color:#1a1a1a;'
123
- + 'font:600 13px/1 system-ui,sans-serif;padding:7px 12px;border-radius:6px;'
124
- + 'cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.18)}'
125
- + '#dm{right:8px}#pin{right:118px}'
126
- + '#dm:hover,#pin:hover{background:#f2f2f2;border-color:#b0b0b0}'
127
- + '@media (prefers-color-scheme:dark){'
128
- + '#dm,#pin{border-color:#555;background:#2a2a2a;color:#f0f0f0;box-shadow:0 1px 4px rgba(0,0,0,.5)}'
129
- + '#dm:hover,#pin:hover{background:#333;border-color:#777}}';
138
+ // \u2500\u2500 Display-mode toggle (shared: @agentproto/app-client/display-mode) \u2500\u2500
139
+ // The installer is inlined verbatim (it is self-contained and idempotent),
140
+ // then handed this panel's own JSON-RPC plumbing. NO auto-request: the
141
+ // panel stays inline until the user clicks, and the buttons only appear for
142
+ // the modes the host advertises in hostContext.availableDisplayModes.
143
+ (function () {
144
+ if (window.AgentprotoUI && window.AgentprotoUI.installDisplayMode) return;
145
+ window.AgentprotoUI = window.AgentprotoUI || {};
146
+
147
+ var STYLE_ID = "agentproto-display-mode-style";
148
+ var BAR_ID = "agentproto-display-mode-bar";
149
+ // Gap between the host's safe area (or the viewport edge) and the button.
150
+ var EDGE_GAP = 8;
151
+
152
+ // Glyph + accessible label per mode. The button is an icon; the words go
153
+ // to title/aria-label so the control stays compact without going mute to
154
+ // a screen reader.
155
+ var LABELS = {
156
+ fullscreen: {
157
+ on: "\\u2921", off: "\\u2922",
158
+ onLabel: "R\\u00e9duire", offLabel: "Agrandir"
159
+ },
160
+ pip: {
161
+ on: "\\u2921", off: "\\uD83D\\uDCCC",
162
+ onLabel: "D\\u00e9tacher", offLabel: "\\u00c9pingler sur le c\\u00f4t\\u00e9 (pip)"
163
+ }
164
+ };
165
+
166
+ // Every colour and both offsets resolve through an app-overridable custom
167
+ // property first, so an app can restyle/reposition the button with a
168
+ // stylesheet and never touch this script. The safe-area vars are what the
169
+ // host's own chrome feeds (see applyInsets), sitting UNDER the app's.
170
+ function ensureStyle() {
171
+ if (!document.head || document.getElementById(STYLE_ID)) return;
172
+ var style = document.createElement("style");
173
+ style.id = STYLE_ID;
174
+ style.textContent =
175
+ "#" + BAR_ID + "{position:fixed;display:flex;gap:6px;align-items:center;z-index:10;" +
176
+ "top:var(--agentproto-dm-top,var(--agentproto-dm-safe-top,8px));" +
177
+ "right:var(--agentproto-dm-right,var(--agentproto-dm-safe-right,8px))}" +
178
+ ".agentproto-dm-btn{display:inline-flex;align-items:center;justify-content:center;" +
179
+ "box-sizing:border-box;width:28px;height:28px;padding:0;margin:0;border-radius:6px;" +
180
+ "cursor:pointer;-webkit-appearance:none;appearance:none;" +
181
+ "font:600 14px/1 system-ui,-apple-system,sans-serif;" +
182
+ "border:1px solid var(--agentproto-dm-border,#d0d0d0);" +
183
+ "background:var(--agentproto-dm-bg,#fff);" +
184
+ "color:var(--agentproto-dm-fg,#1a1a1a);" +
185
+ "box-shadow:0 1px 4px rgba(0,0,0,.18)}" +
186
+ ".agentproto-dm-btn:hover{filter:brightness(.95)}" +
187
+ ".agentproto-dm-btn:focus-visible{outline:2px solid var(--agentproto-dm-fg,#1a1a1a);outline-offset:2px}" +
188
+ ".agentproto-dm-btn[data-theme=\\"dark\\"]{border-color:var(--agentproto-dm-border,#555);background:var(--agentproto-dm-bg,#2a2a2a);color:var(--agentproto-dm-fg,#f0f0f0);box-shadow:0 1px 4px rgba(0,0,0,.5)}" +
189
+ "@media (prefers-color-scheme:dark){" +
190
+ ".agentproto-dm-btn:not([data-theme=\\"light\\"]){border-color:var(--agentproto-dm-border,#555);background:var(--agentproto-dm-bg,#2a2a2a);color:var(--agentproto-dm-fg,#f0f0f0);box-shadow:0 1px 4px rgba(0,0,0,.5)}}";
130
191
  document.head.appendChild(style);
192
+ }
131
193
 
132
- var btn = document.createElement('button');
133
- btn.id = 'dm'; btn.type = 'button'; btn.title = "Basculer l'affichage";
134
- var pin = document.createElement('button');
135
- pin.id = 'pin'; pin.type = 'button'; pin.title = '\xC9pingler sur le c\xF4t\xE9 (pip)';
136
- document.body.appendChild(pin);
137
- document.body.appendChild(btn);
138
-
139
- function has(avail, m){ return !!avail && avail.indexOf(m) >= 0; }
140
-
141
- // Re-sync button visibility + label from the current host context.
142
- function syncBtn(ctx){
143
- ctx = ctx || {};
144
- var avail = ctx.availableDisplayModes;
145
- // Diagnostic: what does THIS host actually advertise? (inline/fullscreen/pip)
146
- console.log('[mcp-app] displayMode=', ctx.displayMode,
147
- 'availableDisplayModes=', avail);
148
-
149
- // Fullscreen toggle button.
150
- if (has(avail, 'fullscreen')){
151
- btn.style.display = 'block';
152
- btn.textContent = (ctx.displayMode === 'fullscreen') ? '\u2921 R\xE9duire' : '\u2922 Agrandir';
153
- } else { btn.style.display = 'none'; }
154
-
155
- // Dedicated pip ("pinned on side") button \u2014 only if the host advertises pip.
156
- if (has(avail, 'pip')){
157
- pin.style.display = 'block';
158
- pin.textContent = (ctx.displayMode === 'pip') ? '\u2921 D\xE9tacher' : '\u{1F4CC} \xC9pingler';
159
- } else { pin.style.display = 'none'; }
160
- }
161
-
162
- onHostContext(syncBtn);
163
-
164
- btn.addEventListener('click', function(){
165
- var ctx = getHostContext() || {};
166
- var inPanel = (ctx.displayMode === 'fullscreen' || ctx.displayMode === 'pip');
167
- requestDisplayMode(inPanel ? 'inline' : 'fullscreen').catch(function(){});
168
- });
194
+ function ready(fn) {
195
+ if (document.body) fn();
196
+ else document.addEventListener("DOMContentLoaded", fn);
197
+ }
169
198
 
170
- pin.addEventListener('click', function(){
171
- var ctx = getHostContext() || {};
172
- requestDisplayMode(ctx.displayMode === 'pip' ? 'inline' : 'pip').catch(function(){});
173
- });
199
+ function metaToggle() {
200
+ var el = document.querySelector('meta[name="agentproto-display-toggle"]');
201
+ if (!el) return "";
202
+ return (el.getAttribute("content") || "").toLowerCase().replace(/^\\s+|\\s+$/g, "");
174
203
  }
175
- if (document.body) mount();
176
- else document.addEventListener('DOMContentLoaded', mount);
177
- })();</script>
204
+
205
+ function ensureBar() {
206
+ var bar = document.getElementById(BAR_ID);
207
+ if (bar) return bar;
208
+ bar = document.createElement("div");
209
+ bar.id = BAR_ID;
210
+ document.body.appendChild(bar);
211
+ return bar;
212
+ }
213
+
214
+ function px(v) {
215
+ return typeof v === "number" && isFinite(v) && v > 0 ? v : 0;
216
+ }
217
+
218
+ window.AgentprotoUI.installDisplayMode = function (api, opts) {
219
+ api = api || {};
220
+ opts = opts || {};
221
+
222
+ var ctx = {};
223
+ var listeners = [];
224
+ var buttons = [];
225
+ // Modes the host has refused once. Checked before availability, so a
226
+ // later host-context-changed can never resurrect a dead button.
227
+ var refused = {};
228
+ var loggedKeys = false;
229
+
230
+ var setting = opts.toggle || metaToggle() || "auto";
231
+ var optimistic = setting === "optimistic";
232
+ var autoMount = setting !== "none";
233
+
234
+ function get() {
235
+ return ctx.displayMode || "inline";
236
+ }
237
+
238
+ function available() {
239
+ var a = ctx.availableDisplayModes;
240
+ return a && a.slice ? a.slice() : [];
241
+ }
242
+
243
+ function supports(mode) {
244
+ if (refused[mode]) return false;
245
+ var a = ctx.availableDisplayModes;
246
+ if (a && a.indexOf && a.indexOf(mode) >= 0) return true;
247
+ // Optimistic: the host advertises nothing (Claude Desktop, some
248
+ // embedders) but may still honour the request. Fullscreen only \u2014 pip
249
+ // is niche enough that guessing at it is just noise.
250
+ return optimistic && mode === "fullscreen";
251
+ }
252
+
253
+ // Host chrome to stay clear of. Published as custom properties rather
254
+ // than inline styles so an app's own --agentproto-dm-top/right still
255
+ // wins (an inline style would beat any stylesheet it could write).
256
+ function applyInsets() {
257
+ var insets = ctx.safeAreaInsets || {};
258
+ var root = document.documentElement;
259
+ if (!root || !root.style || !root.style.setProperty) return;
260
+ root.style.setProperty("--agentproto-dm-safe-top", (px(insets.top) + EDGE_GAP) + "px");
261
+ root.style.setProperty("--agentproto-dm-safe-right", (px(insets.right) + EDGE_GAP) + "px");
262
+ }
263
+
264
+ function syncButton(btn) {
265
+ var mode = btn.getAttribute("data-agentproto-dm") || "fullscreen";
266
+ var labels = LABELS[mode] || LABELS.fullscreen;
267
+ var active = get() === mode;
268
+ btn.style.display = supports(mode) ? "" : "none";
269
+ btn.textContent = active ? labels.on : labels.off;
270
+ var label = active ? labels.onLabel : labels.offLabel;
271
+ btn.title = label;
272
+ btn.setAttribute("aria-label", label);
273
+ btn.setAttribute("aria-pressed", active ? "true" : "false");
274
+ if (ctx.theme === "dark" || ctx.theme === "light") btn.setAttribute("data-theme", ctx.theme);
275
+ else btn.removeAttribute("data-theme");
276
+ }
277
+
278
+ function sync() {
279
+ applyInsets();
280
+ for (var i = buttons.length - 1; i >= 0; i--) {
281
+ // An app that removed its inline toggle stops being our problem.
282
+ if (buttons[i].isConnected === false) { buttons.splice(i, 1); continue; }
283
+ syncButton(buttons[i]);
284
+ }
285
+ }
286
+
287
+ function emit() {
288
+ var mode = get();
289
+ for (var i = 0; i < listeners.length; i++) {
290
+ try { listeners[i](mode, ctx); } catch (_) {}
291
+ }
292
+ }
293
+
294
+ // host-context-changed carries only the changed keys \u2014 merge, matching
295
+ // the official ext-apps App behaviour.
296
+ function setContext(next) {
297
+ if (!next || typeof next !== "object") return;
298
+ for (var k in next) {
299
+ if (Object.prototype.hasOwnProperty.call(next, k)) ctx[k] = next[k];
300
+ }
301
+ sync();
302
+ emit();
303
+ }
304
+
305
+ function retire(mode) {
306
+ refused[mode] = true;
307
+ sync();
308
+ }
309
+
310
+ function request(mode) {
311
+ var pending;
312
+ try {
313
+ pending = api.requestDisplayMode
314
+ ? api.requestDisplayMode(mode)
315
+ : Promise.reject(new Error("display mode: no host"));
316
+ } catch (err) {
317
+ pending = Promise.reject(err);
318
+ }
319
+ return Promise.resolve(pending).then(function (result) {
320
+ // McpUiRequestDisplayModeResult.mode is the mode actually set, and
321
+ // the spec lets it differ from the one asked for \u2014 so a host that
322
+ // answers "still inline" is a refusal, not a success.
323
+ var settled = (result && result.mode) || mode;
324
+ if (optimistic && mode !== "inline" && settled !== mode) {
325
+ retire(mode);
326
+ throw new Error("host refused display mode: " + mode);
327
+ }
328
+ // Hosts are not required to follow up with host-context-changed \u2014
329
+ // trust the result so the glyph flips either way.
330
+ setContext({ displayMode: settled });
331
+ return settled;
332
+ }, function (err) {
333
+ if (optimistic && mode !== "inline") retire(mode);
334
+ throw err;
335
+ });
336
+ }
337
+
338
+ function makeButton(mode) {
339
+ var btn = document.createElement("button");
340
+ btn.type = "button";
341
+ btn.className = "agentproto-dm-btn";
342
+ // Stable id for the floating pair (styling hooks, tests). An app that
343
+ // mounts extra inline toggles gets unnamed ones rather than duplicates.
344
+ var id = mode === "fullscreen"
345
+ ? "agentproto-display-mode"
346
+ : "agentproto-display-mode-" + mode;
347
+ if (!document.getElementById(id)) btn.id = id;
348
+ btn.setAttribute("data-agentproto-dm", mode);
349
+ btn.style.display = "none";
350
+ btn.addEventListener("click", function () {
351
+ var cur = get();
352
+ // Same rule both copies had: anything that isn't inline collapses
353
+ // back to inline, so the button is always an escape hatch.
354
+ var inPanel = cur === "fullscreen" || cur === "pip";
355
+ request(inPanel ? "inline" : mode)["catch"](function () {});
356
+ });
357
+ return btn;
358
+ }
359
+
360
+ function mountToggle(el, mode) {
361
+ ensureStyle();
362
+ mode = mode || "fullscreen";
363
+ var btn = makeButton(mode);
364
+ if (el) el.appendChild(btn);
365
+ else ensureBar().appendChild(btn);
366
+ buttons.push(btn);
367
+ syncButton(btn);
368
+ return btn;
369
+ }
370
+
371
+ function onChange(cb) {
372
+ if (typeof cb !== "function") return function () {};
373
+ listeners.push(cb);
374
+ try { cb(get(), ctx); } catch (_) {}
375
+ return function () {
376
+ var i = listeners.indexOf(cb);
377
+ if (i >= 0) listeners.splice(i, 1);
378
+ };
379
+ }
380
+
381
+ ready(function () {
382
+ ensureStyle();
383
+ // Only the first installer in a document owns the floating bar: a
384
+ // built-in panel served standalone gets the injected window.McpApp
385
+ // bridge's copy AND its own, and two stacked buttons is a bug.
386
+ if (autoMount && !document.getElementById(BAR_ID)) {
387
+ ensureBar();
388
+ mountToggle(null, "pip");
389
+ mountToggle(null, "fullscreen");
390
+ }
391
+ sync();
392
+ });
393
+
394
+ // The diagnostic both copies had, now on BOTH paths: it used to exist
395
+ // only in the built-in panel bridge, so an installed app debugged in
396
+ // Claude Desktop (which advertises no fullscreen, hence no button)
397
+ // printed nothing at all and left the user with no way to tell "the
398
+ // host refuses" from "the toggle is broken".
399
+ function observe(next) {
400
+ console.log('[mcp-app] displayMode=', next && next.displayMode,
401
+ 'availableDisplayModes=', next && next.availableDisplayModes);
402
+ setContext(next);
403
+ // What ELSE does this host send? Keys only, once \u2014 a host context can
404
+ // carry toolInfo (the instantiating tool call's arguments) and other
405
+ // host-private fields, so the names are the diagnostic and the values
406
+ // stay out of the console.
407
+ if (!loggedKeys && next && typeof next === "object") {
408
+ loggedKeys = true;
409
+ var keys = [];
410
+ for (var k in ctx) {
411
+ if (Object.prototype.hasOwnProperty.call(ctx, k)) keys.push(k);
412
+ }
413
+ console.log('[mcp-app] hostContext keys=', keys.sort().join(','));
414
+ }
415
+ }
416
+
417
+ if (typeof api.onHostContext === "function") api.onHostContext(observe);
418
+ else if (typeof api.getHostContext === "function") observe(api.getHostContext());
419
+
420
+ return {
421
+ get: get,
422
+ available: available,
423
+ request: request,
424
+ onChange: onChange,
425
+ mountToggle: mountToggle
426
+ };
427
+ };
428
+ })();
429
+ var displayMode = window.AgentprotoUI.installDisplayMode({
430
+ getHostContext: getHostContext,
431
+ onHostContext: onHostContext,
432
+ requestDisplayMode: requestDisplayMode
433
+ });</script>
178
434
  <script>(function() {
179
435
  "use strict";
180
436
  const COLUMN_STATUSES = ["pending", "in_progress", "done", "failed"];