@agentproto/apps 0.10.0 → 0.12.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 (44) hide show
  1. package/dist/agents-overview/panel.mjs +43 -1
  2. package/dist/agents-overview/panel.mjs.map +1 -1
  3. package/dist/agents-overview.mjs +43 -1
  4. package/dist/agents-overview.mjs.map +1 -1
  5. package/dist/bureau-sessions/panel.mjs +43 -1
  6. package/dist/bureau-sessions/panel.mjs.map +1 -1
  7. package/dist/bureau-sessions.mjs +43 -1
  8. package/dist/bureau-sessions.mjs.map +1 -1
  9. package/dist/index.mjs +1116 -300
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/live-session/panel.mjs +43 -1
  12. package/dist/live-session/panel.mjs.map +1 -1
  13. package/dist/live-session.mjs +43 -1
  14. package/dist/live-session.mjs.map +1 -1
  15. package/dist/panel-bridge.d.ts +48 -0
  16. package/dist/panel-bridge.d.ts.map +1 -1
  17. package/dist/session-chat/blob-embed.d.ts +53 -0
  18. package/dist/session-chat/blob-embed.d.ts.map +1 -0
  19. package/dist/session-chat/index.d.ts +13 -0
  20. package/dist/session-chat/index.d.ts.map +1 -1
  21. package/dist/session-chat/panel.d.ts +38 -2
  22. package/dist/session-chat/panel.d.ts.map +1 -1
  23. package/dist/session-chat/panel.mjs +609 -6
  24. package/dist/session-chat/panel.mjs.map +1 -1
  25. package/dist/session-chat.mjs +631 -13
  26. package/dist/session-chat.mjs.map +1 -1
  27. package/dist/session-story/panel.mjs +43 -1
  28. package/dist/session-story/panel.mjs.map +1 -1
  29. package/dist/session-story.mjs +43 -1
  30. package/dist/session-story.mjs.map +1 -1
  31. package/dist/sessions-panel/panel.mjs +43 -1
  32. package/dist/sessions-panel/panel.mjs.map +1 -1
  33. package/dist/sessions-panel.mjs +43 -1
  34. package/dist/sessions-panel.mjs.map +1 -1
  35. package/dist/work-board/index.d.ts.map +1 -1
  36. package/dist/work-board/panel.d.ts +39 -6
  37. package/dist/work-board/panel.d.ts.map +1 -1
  38. package/dist/work-board/panel.generated.d.ts +13 -0
  39. package/dist/work-board/panel.generated.d.ts.map +1 -0
  40. package/dist/work-board/panel.mjs +494 -296
  41. package/dist/work-board/panel.mjs.map +1 -1
  42. package/dist/work-board.mjs +496 -298
  43. package/dist/work-board.mjs.map +1 -1
  44. package/package.json +12 -8
@@ -3,14 +3,44 @@
3
3
  * Ready-made agentproto apps (teams of agents + workflows) built on app-kit.
4
4
  */
5
5
 
6
- // src/panel-bridge.ts
7
- function panelBridgeScript(appName) {
8
- return `// \u2500\u2500 MCP Apps bridge (shared: panel-bridge.ts) \u2500\u2500
6
+ // src/work-board/panel.generated.ts
7
+ var WORK_BOARD_HTML_GENERATED = `<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
12
+ <title>agentproto work board</title>
13
+ <!-- PANEL_BRIDGE: replaced with the inline panelBridgeScript() output by
14
+ vite.config.ts's injectPanelBridge plugin \u2014 a classic (non-module)
15
+ script so its \`initBridge\`/\`callTool\` land as globals before the
16
+ deferred module script below runs. -->
17
+ <script>// \u2500\u2500 MCP Apps bridge (shared: panel-bridge.ts) \u2500\u2500
9
18
  // JSON-RPC 2.0 over window.parent.postMessage \xB7 spec 2026-01-26
10
19
  var _nextId = 1, _pending = {}, _notifyHandlers = [];
11
20
  var _hostContext = null, _hostContextHandlers = [];
21
+ var _hostCaps = null;
22
+ var _standaloneApp = null;
23
+ function _isStandalone(){
24
+ return window.parent === window && !!window.McpApp && typeof window.McpApp.connect === 'function';
25
+ }
26
+ // \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
27
+ // Panels that iframe the daemon's standalone app host carry this proof so
28
+ // GET /apps/:appId/ui?embed=1&et=... can drop its anti-framing headers for
29
+ // MCP-Apps hosts whose widget context is an opaque origin (Claude Desktop).
30
+ // registerMcpApps replaces the placeholder with a real per-boot token when
31
+ // serving the panel as a resource; every other render (tests, standalone
32
+ // tab, VS Code HTTP-iframe panel \u2014 the latter two already pass
33
+ // iframeEmbedAllowed's origin checks) keeps the literal, and
34
+ // withEmbedToken() then degrades to an identity function.
35
+ window.__AGENPROTO_EMBED_TOKEN__ = "__AGENPROTO_EMBED_TOKEN__";
36
+ function withEmbedToken(url){
37
+ var t = window.__AGENPROTO_EMBED_TOKEN__;
38
+ if (!t || t === '__AGENPROTO_EMBED_TOKEN__') return url;
39
+ return url + (url.indexOf('?') >= 0 ? '&' : '?') + 'et=' + encodeURIComponent(t);
40
+ }
12
41
  function post(msg){ window.parent.postMessage(msg, '*'); }
13
42
  function getHostContext(){ return _hostContext; }
43
+ function getHostCapabilities(){ return _hostCaps; }
14
44
  function onHostContext(cb){
15
45
  _hostContextHandlers.push(cb);
16
46
  // Replay the last context so a late subscriber isn't stuck blind.
@@ -55,22 +85,43 @@ window.addEventListener('message', function(evt){
55
85
  }
56
86
  });
57
87
  function initBridge(){
88
+ if (_isStandalone()){
89
+ return window.McpApp.connect().then(function(conn){
90
+ _standaloneApp = conn;
91
+ // No host to advertise a hostContext \u2014 default to inline with no
92
+ // other modes available, which keeps the #dm/#pin toggle buttons
93
+ // hidden (they only show for modes hostContext.availableDisplayModes
94
+ // lists) instead of rendering two buttons with nothing to switch to.
95
+ _setHostContext({displayMode: 'inline', availableDisplayModes: []});
96
+ });
97
+ }
58
98
  return rpcRequest('ui/initialize', {
59
- appInfo: {name: ${JSON.stringify(appName)}, version: '0.1.0'},
99
+ appInfo: {name: "agentproto-work-board-panel", version: '0.1.0'},
60
100
  appCapabilities: {availableDisplayModes: ['inline', 'fullscreen', 'pip']},
61
101
  protocolVersion: '2026-01-26'
62
102
  }).then(function(result){
63
103
  // The initialize result carries the initial hostContext (displayMode +
64
104
  // availableDisplayModes) \u2014 capture it before notifying the host.
65
105
  if (result && result.hostContext) _setHostContext(result.hostContext);
106
+ // McpUiInitializeResult.hostCapabilities (ext-apps spec) \u2014 e.g. openLinks,
107
+ // "Host supports opening external URLs". Captured once, read via
108
+ // getHostCapabilities() by panels that need to branch on it.
109
+ if (result && result.hostCapabilities) _hostCaps = result.hostCapabilities;
66
110
  rpcNotify('ui/notifications/initialized', {});
67
111
  });
68
112
  }
69
113
  function requestDisplayMode(mode){
70
114
  return rpcRequest('ui/request-display-mode', {mode: mode});
71
115
  }
116
+ function openLink(url){
117
+ if (_standaloneApp && typeof _standaloneApp.openLink === 'function') return _standaloneApp.openLink(url);
118
+ return rpcRequest('ui/open-link', {url: url});
119
+ }
72
120
  function callTool(name, args){
73
- return rpcRequest('tools/call', {name: name, arguments: args || {}}).then(function(result){
121
+ var raw = _standaloneApp
122
+ ? _standaloneApp.callTool(name, args || {})
123
+ : rpcRequest('tools/call', {name: name, arguments: args || {}});
124
+ return raw.then(function(result){
74
125
  if (result.isError){
75
126
  var e = (result.content && result.content[0] && result.content[0].text) || 'tool error';
76
127
  throw new Error(e);
@@ -144,321 +195,468 @@ function callTool(name, args){
144
195
  }
145
196
  if (document.body) mount();
146
197
  else document.addEventListener('DOMContentLoaded', mount);
147
- })();`;
148
- }
149
-
150
- // src/work-board/panel.ts
151
- var WORK_BOARD_HTML = `<!DOCTYPE html>
152
- <html lang="en">
153
- <head>
154
- <meta charset="UTF-8">
155
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
156
- <title>agentproto work board</title>
157
- <style>
158
- *{box-sizing:border-box;margin:0;padding:0}
159
- :root{
160
- --bg:#0d1117;--bg2:#161b22;--bg3:#21262d;--border:#30363d;
161
- --text:#e6edf3;--text2:#8b949e;
162
- --green:#3fb950;--yellow:#d29922;--red:#f85149;--blue:#58a6ff;--purple:#bc8cff;
163
- }
164
- html,body{height:100%;font-family:Menlo,Monaco,'Courier New',monospace;font-size:13px;background:var(--bg);color:var(--text);overflow:hidden}
165
- #app{display:flex;flex-direction:column;height:100%}
166
- #toolbar{padding:8px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;background:var(--bg2);flex-shrink:0}
167
- #board-label{font-size:11px;color:var(--text2)}
168
- #board-id{font-weight:600;color:var(--text);font-size:12px}
169
- #board-input{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 8px;border-radius:4px;font-size:12px;font-family:inherit;width:220px}
170
- .tbtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 10px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}
171
- .tbtn:hover{background:var(--border)}
172
- #new-title{flex:1;min-width:120px;background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:4px 8px;border-radius:4px;font-size:12px;font-family:inherit}
173
- #columns{flex:1;display:flex;overflow-x:auto;overflow-y:hidden}
174
- .col{width:260px;min-width:220px;flex-shrink:0;display:flex;flex-direction:column;border-right:1px solid var(--border)}
175
- .col-hdr{padding:8px 10px;border-bottom:1px solid var(--border);background:var(--bg2);font-size:11px;font-weight:600;color:var(--text2);text-transform:uppercase;letter-spacing:.05em;display:flex;justify-content:space-between}
176
- .col-body{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:8px}
177
- .card{background:var(--bg2);border:1px solid var(--border);border-radius:6px;padding:8px 10px}
178
- .c-title{font-size:12px;font-weight:500;margin-bottom:4px;word-break:break-word}
179
- .c-id{font-size:10px;color:var(--text2);margin-bottom:6px}
180
- .c-row{display:flex;flex-wrap:wrap;gap:4px;align-items:center;margin-bottom:6px}
181
- .tag{display:inline-block;padding:1px 6px;border-radius:10px;font-size:10px;font-weight:600;white-space:nowrap}
182
- .t-unclaimed{background:rgba(139,148,158,.15);color:var(--text2);border:1px dashed var(--text2)}
183
- .t-owner{background:rgba(88,166,255,.15);color:var(--blue)}
184
- .t-gate{background:rgba(63,185,80,.18);color:var(--green)}
185
- .t-self{background:rgba(210,153,34,.18);color:var(--yellow)}
186
- .t-human{background:rgba(88,166,255,.18);color:var(--blue)}
187
- .t-gated{background:rgba(139,148,158,.12);color:var(--text2)}
188
- .t-cancelled{background:rgba(139,148,158,.12);color:var(--text2)}
189
- .c-err{font-size:10px;color:var(--red);margin-bottom:6px;word-break:break-word}
190
- .c-actions{display:flex;flex-wrap:wrap;gap:4px}
191
- .abtn{background:var(--bg3);border:1px solid var(--border);color:var(--text);padding:3px 8px;border-radius:4px;cursor:pointer;font-size:11px;font-family:inherit}
192
- .abtn:hover{background:var(--border)}
193
- .abtn.danger{border-color:var(--red);color:var(--red)}
194
- .abtn.primary{border-color:var(--green);color:var(--green)}
195
- #statusbar{padding:4px 12px;font-size:11px;color:var(--text2);border-top:1px solid var(--border);background:var(--bg2);flex-shrink:0}
196
- .empty{padding:16px 8px;color:var(--text2);font-size:11px;text-align:center}
197
- </style>
198
- </head>
199
- <body>
200
- <div id="app">
201
- <div id="toolbar">
202
- <span id="board-label">Board:</span>
203
- <span id="board-id">\u2014</span>
204
- <input id="board-input" type="text" placeholder="tree:&lt;sessionId&gt; or ws:&lt;slug&gt;">
205
- <button class="tbtn" id="go-btn">Go</button>
206
- <button class="tbtn" id="refresh-btn" title="Refresh">&#8635;</button>
207
- <input id="new-title" type="text" placeholder="New task title&#8230;">
208
- <button class="tbtn" id="add-btn">+ Add</button>
209
- </div>
210
- <div id="columns"></div>
211
- <div id="statusbar">Connecting to bridge&#8230;</div>
212
- </div>
213
- <script>
214
- ${panelBridgeScript("agentproto-work-board-panel")}
215
-
216
- // ============================================================
217
- // State
218
- // ============================================================
219
-
220
- var COLUMNS = [
221
- {key: 'pending', label: 'Pending'},
222
- {key: 'in_progress', label: 'In Progress'},
223
- {key: 'done', label: 'Done'},
224
- {key: 'failed', label: 'Failed'}
225
- ];
226
-
227
- var boardId = null;
228
- var tasks = [];
229
- var pollTimer = null;
230
- var pollActive = false;
231
-
232
- function setStatus(msg) {
233
- document.getElementById('statusbar').textContent = msg;
234
- }
235
-
236
- function esc(s) {
237
- return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
238
- }
239
-
240
- // A cancelled task folds into the Failed column, tagged distinctly \u2014 v1
241
- // treats both as terminal scrap, and a fifth column for a rarely-used
242
- // status isn't worth the width.
243
- function columnOf(status) {
244
- if (status === 'cancelled') return 'failed';
245
- return status;
246
- }
247
-
248
- // ============================================================
249
- // Verification tell \u2014 never render an unverified done as gate-passed.
250
- // ============================================================
251
-
252
- function verificationTag(t) {
253
- var v = t.verification;
254
- if (v) {
255
- if (v.kind === 'gate') return '<span class="tag t-gate">&#10003; gate</span>';
256
- if (v.kind === 'self-report') return '<span class="tag t-self">self-report</span>';
257
- if (v.kind === 'human') return '<span class="tag t-human">human</span>';
198
+ })();</script>
199
+ <script>(function() {
200
+ "use strict";
201
+ const COLUMN_STATUSES = ["pending", "in_progress", "done", "failed"];
202
+ const COLUMN_LABELS = {
203
+ pending: "Pending",
204
+ in_progress: "In Progress",
205
+ done: "Done",
206
+ failed: "Failed"
207
+ };
208
+ function esc(value) {
209
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
258
210
  }
259
- if (t.status === 'in_progress' && t.verify) {
260
- return '<span class="tag t-gated">gated</span>';
211
+ function columnOf(status) {
212
+ return status === "cancelled" ? "failed" : status;
261
213
  }
262
- return '';
263
- }
264
-
265
- function ownerTag(t) {
266
- if (!t.owner) return '<span class="tag t-unclaimed">Unclaimed</span>';
267
- return '<span class="tag t-owner">' + esc(t.owner) + '</span>';
268
- }
269
-
270
- // ============================================================
271
- // Card actions \u2014 explicit status moves, no drag-and-drop (read-only-safe
272
- // first: see module docblock).
273
- // ============================================================
274
-
275
- function actionsFor(t) {
276
- var a = [];
277
- if (t.status === 'pending') {
278
- if (!t.owner) a.push({label: 'Claim', action: 'claim', cls: 'primary'});
279
- else a.push({label: 'Start', action: 'start', cls: 'primary'});
280
- a.push({label: 'Cancel', action: 'cancel', cls: 'danger'});
281
- } else if (t.status === 'in_progress') {
282
- a.push({label: 'Done', action: 'done', cls: 'primary'});
283
- a.push({label: 'Fail', action: 'fail', cls: 'danger'});
284
- a.push({label: 'Release', action: 'release', cls: ''});
285
- a.push({label: 'Cancel', action: 'cancel', cls: 'danger'});
286
- } else if (t.status === 'done') {
287
- a.push({label: 'Reopen', action: 'reopen', cls: ''});
214
+ function verificationTag(task) {
215
+ const v = task.verification;
216
+ if (v) {
217
+ if (v.kind === "gate") return '<span class="tag t-gate">&#10003; gate</span>';
218
+ if (v.kind === "self-report") return '<span class="tag t-self">self-report</span>';
219
+ if (v.kind === "human") return '<span class="tag t-human">human</span>';
220
+ }
221
+ if (task.status === "in_progress" && task.verify) {
222
+ return '<span class="tag t-gated">gated</span>';
223
+ }
224
+ return "";
288
225
  }
289
- return a;
290
- }
291
-
292
- function applyAction(taskId, action) {
293
- var t = null;
294
- for (var i = 0; i < tasks.length; i++) {
295
- if (tasks[i].taskId === taskId) { t = tasks[i]; break; }
226
+ function ownerTag(task) {
227
+ if (!task.owner) return '<span class="tag t-unclaimed">Unclaimed</span>';
228
+ return \`<span class="tag t-owner">\${esc(task.owner)}</span>\`;
296
229
  }
297
- if (!t) return;
298
- var p;
299
- if (action === 'claim') {
300
- p = callTool('task_claim', {taskId: t.taskId, rev: t.rev});
301
- } else if (action === 'start') {
302
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, status: 'in_progress'});
303
- } else if (action === 'done') {
304
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, status: 'done'});
305
- } else if (action === 'fail') {
306
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, status: 'failed'});
307
- } else if (action === 'release') {
308
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, owner: null});
309
- } else if (action === 'cancel') {
310
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, status: 'cancelled'});
311
- } else if (action === 'reopen') {
312
- p = callTool('task_update', {taskId: t.taskId, rev: t.rev, status: 'pending'});
313
- } else {
314
- return;
230
+ function actionsFor(task) {
231
+ if (task.status === "pending") {
232
+ return [
233
+ task.owner ? { label: "Start", action: "start", cls: "primary" } : { label: "Claim", action: "claim", cls: "primary" },
234
+ { label: "Cancel", action: "cancel", cls: "danger" }
235
+ ];
236
+ }
237
+ if (task.status === "in_progress") {
238
+ return [
239
+ { label: "Done", action: "done", cls: "primary" },
240
+ { label: "Fail", action: "fail", cls: "danger" },
241
+ { label: "Release", action: "release", cls: "" },
242
+ { label: "Cancel", action: "cancel", cls: "danger" }
243
+ ];
244
+ }
245
+ if (task.status === "done") {
246
+ return [{ label: "Reopen", action: "reopen", cls: "" }];
247
+ }
248
+ return [];
315
249
  }
316
- p.then(function (result) {
317
- if (result && result.conflict) {
318
- setStatus('Someone else moved "' + t.title + '" first \u2014 refreshed.');
319
- } else if (result && result.error) {
320
- setStatus('Action failed: ' + result.error);
321
- } else {
322
- setStatus('Updated "' + t.title + '".');
250
+ function renderCard(task) {
251
+ let html = \`<div class="card" data-taskid="\${esc(task.taskId)}">\`;
252
+ html += \`<div class="c-title">\${esc(task.title)}</div>\`;
253
+ html += \`<div class="c-id">#\${esc(task.taskId)}</div>\`;
254
+ html += \`<div class="c-row">\${ownerTag(task)}\${verificationTag(task)}\`;
255
+ if (task.status === "cancelled") html += '<span class="tag t-cancelled">cancelled</span>';
256
+ html += "</div>";
257
+ if (task.lastVerifyError) {
258
+ html += \`<div class="c-err">verify failed: \${esc(task.lastVerifyError)}</div>\`;
323
259
  }
324
- return loadBoard();
325
- }).catch(function (e) {
326
- setStatus('Action failed: ' + e.message);
327
- });
328
- }
329
-
330
- // ============================================================
331
- // Render
332
- // ============================================================
333
-
334
- function renderCard(t) {
335
- var html = '<div class="card" data-taskid="' + esc(t.taskId) + '">';
336
- html += '<div class="c-title">' + esc(t.title) + '</div>';
337
- html += '<div class="c-id">#' + esc(t.taskId) + '</div>';
338
- html += '<div class="c-row">' + ownerTag(t) + verificationTag(t);
339
- if (t.status === 'cancelled') html += '<span class="tag t-cancelled">cancelled</span>';
340
- html += '</div>';
341
- if (t.lastVerifyError) {
342
- html += '<div class="c-err">verify failed: ' + esc(t.lastVerifyError) + '</div>';
260
+ const actions = actionsFor(task);
261
+ if (actions.length > 0) {
262
+ html += '<div class="c-actions">';
263
+ for (const a of actions) {
264
+ html += \`<button class="abtn \${a.cls}" data-taskid="\${esc(task.taskId)}" data-action="\${a.action}">\${a.label}</button>\`;
265
+ }
266
+ html += "</div>";
267
+ }
268
+ html += "</div>";
269
+ return html;
343
270
  }
344
- var actions = actionsFor(t);
345
- if (actions.length > 0) {
346
- html += '<div class="c-actions">';
347
- for (var i = 0; i < actions.length; i++) {
348
- var a = actions[i];
349
- html += '<button class="abtn ' + a.cls + '" data-taskid="' + esc(t.taskId) + '" data-action="' + a.action + '">' + a.label + '</button>';
271
+ function renderColumns(tasks2) {
272
+ let html = "";
273
+ for (const status of COLUMN_STATUSES) {
274
+ const rows = tasks2.filter((t) => columnOf(t.status) === status);
275
+ html += \`<div class="col"><div class="col-hdr"><span>\${COLUMN_LABELS[status]}</span><span>\${rows.length}</span></div><div class="col-body">\`;
276
+ html += rows.length === 0 ? '<div class="empty">No tasks</div>' : rows.map(renderCard).join("");
277
+ html += "</div></div>";
350
278
  }
351
- html += '</div>';
279
+ return html;
352
280
  }
353
- html += '</div>';
354
- return html;
355
- }
356
-
357
- function render() {
358
- document.getElementById('board-id').textContent = boardId || '\u2014';
359
- if (!document.getElementById('board-input').value) {
360
- document.getElementById('board-input').value = boardId || '';
281
+ function getEl(id) {
282
+ const el = document.getElementById(id);
283
+ if (!el) throw new Error(\`work board: missing #\${id}\`);
284
+ return el;
285
+ }
286
+ function getInput(id) {
287
+ const el = document.getElementById(id);
288
+ if (!(el instanceof HTMLInputElement)) throw new Error(\`work board: missing input #\${id}\`);
289
+ return el;
290
+ }
291
+ function isCardAction(value) {
292
+ switch (value) {
293
+ case "claim":
294
+ case "start":
295
+ case "done":
296
+ case "fail":
297
+ case "release":
298
+ case "cancel":
299
+ case "reopen":
300
+ return true;
301
+ default:
302
+ return false;
303
+ }
361
304
  }
362
- var el = document.getElementById('columns');
363
- var html = '';
364
- for (var c = 0; c < COLUMNS.length; c++) {
365
- var col = COLUMNS[c];
366
- var rows = tasks.filter(function (t) { return columnOf(t.status) === col.key; });
367
- html += '<div class="col"><div class="col-hdr"><span>' + col.label + '</span><span>' + rows.length + '</span></div><div class="col-body">';
368
- if (rows.length === 0) {
369
- html += '<div class="empty">No tasks</div>';
370
- } else {
371
- for (var i = 0; i < rows.length; i++) html += renderCard(rows[i]);
305
+ let boardId = null;
306
+ let tasks = [];
307
+ let pollActive = false;
308
+ function boot() {
309
+ const columnsEl = getEl("columns");
310
+ const statusbarEl = getEl("statusbar");
311
+ const boardIdEl = getEl("board-id");
312
+ const boardInputEl = getInput("board-input");
313
+ const newTitleEl = getInput("new-title");
314
+ function setStatus(msg) {
315
+ statusbarEl.textContent = msg;
316
+ }
317
+ function render() {
318
+ boardIdEl.textContent = boardId ?? "\u2014";
319
+ if (!boardInputEl.value) boardInputEl.value = boardId ?? "";
320
+ columnsEl.innerHTML = renderColumns(tasks);
321
+ }
322
+ function findTask(taskId) {
323
+ return tasks.find((t) => t.taskId === taskId);
324
+ }
325
+ function loadBoard() {
326
+ const args = {
327
+ includeClosed: true,
328
+ full: true,
329
+ ...boardId ? { boardId } : {}
330
+ };
331
+ return callTool("task_list", args).then((data) => {
332
+ boardId = data.boardId ?? boardId;
333
+ tasks = data.tasks ?? [];
334
+ render();
335
+ setStatus(\`\${tasks.length} task\${tasks.length === 1 ? "" : "s"} \xB7 \${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\`);
336
+ }).catch((e) => {
337
+ setStatus(\`Error: \${e.message}\`);
338
+ });
372
339
  }
373
- html += '</div></div>';
340
+ function applyAction(taskId, action) {
341
+ const task = findTask(taskId);
342
+ if (!task) return;
343
+ const base = { taskId: task.taskId, rev: task.rev };
344
+ let promise;
345
+ if (action === "claim") {
346
+ promise = callTool("task_claim", base);
347
+ } else if (action === "start") {
348
+ promise = callTool("task_update", { ...base, status: "in_progress" });
349
+ } else if (action === "done") {
350
+ promise = callTool("task_update", { ...base, status: "done" });
351
+ } else if (action === "fail") {
352
+ promise = callTool("task_update", { ...base, status: "failed" });
353
+ } else if (action === "release") {
354
+ promise = callTool("task_update", { ...base, owner: null });
355
+ } else if (action === "cancel") {
356
+ promise = callTool("task_update", { ...base, status: "cancelled" });
357
+ } else {
358
+ promise = callTool("task_update", { ...base, status: "pending" });
359
+ }
360
+ promise.then((result) => {
361
+ if ("conflict" in result) {
362
+ setStatus(\`Someone else moved "\${task.title}" first \u2014 refreshed.\`);
363
+ } else if ("error" in result) {
364
+ setStatus(\`Action failed: \${result.error}\`);
365
+ } else {
366
+ setStatus(\`Updated "\${task.title}".\`);
367
+ }
368
+ return loadBoard();
369
+ }).catch((e) => {
370
+ setStatus(\`Action failed: \${e.message}\`);
371
+ });
372
+ }
373
+ function doPoll() {
374
+ if (pollActive) return;
375
+ pollActive = true;
376
+ loadBoard().finally(() => {
377
+ pollActive = false;
378
+ setTimeout(doPoll, 4e3);
379
+ });
380
+ }
381
+ columnsEl.addEventListener("click", (evt) => {
382
+ if (!(evt.target instanceof Element)) return;
383
+ const btn = evt.target.closest("button[data-action]");
384
+ if (!(btn instanceof HTMLButtonElement)) return;
385
+ const taskId = btn.getAttribute("data-taskid");
386
+ const action = btn.getAttribute("data-action");
387
+ if (!taskId || !action || !isCardAction(action)) return;
388
+ applyAction(taskId, action);
389
+ });
390
+ getEl("refresh-btn").addEventListener("click", () => {
391
+ void loadBoard();
392
+ });
393
+ getEl("go-btn").addEventListener("click", () => {
394
+ const value = boardInputEl.value.trim();
395
+ if (!value) return;
396
+ boardId = value;
397
+ void loadBoard();
398
+ });
399
+ getEl("add-btn").addEventListener("click", () => {
400
+ const title = newTitleEl.value.trim();
401
+ if (!title) return;
402
+ const args = boardId ? { title, boardId } : { title };
403
+ callTool("task_create", args).then((result) => {
404
+ if (result.error) {
405
+ setStatus(\`Create failed: \${result.error}\`);
406
+ return void 0;
407
+ }
408
+ newTitleEl.value = "";
409
+ setStatus(\`Created "\${title}".\`);
410
+ return loadBoard();
411
+ }).catch((e) => {
412
+ setStatus(\`Create failed: \${e.message}\`);
413
+ });
414
+ });
415
+ initBridge().then(() => loadBoard()).then(() => {
416
+ setTimeout(doPoll, 4e3);
417
+ }).catch((e) => {
418
+ setStatus(\`Bridge error: \${e.message}\`);
419
+ });
374
420
  }
375
- el.innerHTML = html;
421
+ if (document.readyState === "loading") {
422
+ document.addEventListener("DOMContentLoaded", boot);
423
+ } else {
424
+ boot();
425
+ }
426
+ })();</script>
427
+ <style rel="stylesheet" crossorigin>/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
428
+ Semantic tokens + runtime mapping sheets.
429
+
430
+ Every rule below the next section reads ONLY the --wb-* custom
431
+ properties. Each one resolves through TWO mapping sheets, chosen at
432
+ PAINT time via plain CSS var() fallback \u2014 no build step, no JS, so a
433
+ host that swaps its theme live (VS Code) is never frozen at whatever
434
+ theme happened to be active when this bundle was built:
435
+
436
+ 1. embedded \u2014 VS Code injects a live --vscode-* custom-property set
437
+ into the webview and swaps it on theme change. Whenever the
438
+ relevant --vscode-* variable exists, it wins \u2014 in a VS Code
439
+ \`srcdoc\` relay panel AND (structurally identically) any other host
440
+ that happened to expose the same names, though none is known to.
441
+ 2. standalone \u2014 the agentproto brand tokens (see
442
+ projects/agentproto/site/src/app/brand.css), used whenever no
443
+ --vscode-* variable is set \u2014 a plain MCP-Apps host or the daemon's
444
+ own \`/apps/.../ui\` tab. \`--phos\`/\`--amber\`/\`--crimson\` are
445
+ THEME-INVARIANT in brand.css (the phosphor terminal palette), so
446
+ they're identical in both blocks below; the rest follow brand.css's
447
+ light/dark split via \`prefers-color-scheme\` \u2014 a standalone tab has
448
+ no host to pick a theme for it, unlike VS Code's own.
449
+ \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
450
+
451
+ :root {
452
+ --wb-bg: var(--vscode-editor-background, #f5f6f3);
453
+ --wb-surface: var(--vscode-sideBar-background, #ffffff);
454
+ --wb-ink: var(--vscode-editor-foreground, #171a1e);
455
+ --wb-ink-muted: var(--vscode-descriptionForeground, #5c6470);
456
+ --wb-border: var(--vscode-panel-border, #dde0da);
457
+ --wb-accent: var(--vscode-button-background, #0f62fe);
458
+
459
+ /* Status colors \u2014 phos/amber/crimson are the theme-invariant terminal
460
+ palette in brand.css, so light and dark standalone fallbacks match. */
461
+ --wb-status-pending: var(--vscode-descriptionForeground, #5c6470);
462
+ --wb-status-in-progress: var(--vscode-charts-yellow, #f0b429);
463
+ --wb-status-done: var(--vscode-charts-green, #3edc81);
464
+ --wb-status-failed: var(--vscode-charts-red, #fa4d56);
465
+ --wb-status-cancelled: var(--vscode-descriptionForeground, #5c6470);
466
+ --wb-status-cancelled-weak: color-mix(in srgb, var(--wb-status-cancelled) 55%, transparent);
376
467
  }
377
468
 
378
- // ============================================================
379
- // Data
380
- // ============================================================
381
-
382
- function loadBoard() {
383
- var args = {includeClosed: true, full: true};
384
- if (boardId) args.boardId = boardId;
385
- return callTool('task_list', args).then(function (data) {
386
- boardId = data.boardId || boardId;
387
- tasks = data.tasks || [];
388
- render();
389
- setStatus(tasks.length + ' task' + (tasks.length === 1 ? '' : 's') + ' \xB7 ' + new Date().toLocaleTimeString());
390
- }).catch(function (e) {
391
- setStatus('Error: ' + e.message);
392
- });
469
+ @media (prefers-color-scheme: dark) {
470
+ :root {
471
+ --wb-bg: var(--vscode-editor-background, #101214);
472
+ --wb-surface: var(--vscode-sideBar-background, #16191d);
473
+ --wb-ink: var(--vscode-editor-foreground, #e8eaed);
474
+ --wb-ink-muted: var(--vscode-descriptionForeground, #99a2ad);
475
+ --wb-border: var(--vscode-panel-border, #272c33);
476
+ --wb-accent: var(--vscode-button-background, #4589ff);
477
+
478
+ --wb-status-pending: var(--vscode-descriptionForeground, #99a2ad);
479
+ --wb-status-in-progress: var(--vscode-charts-yellow, #f0b429);
480
+ --wb-status-done: var(--vscode-charts-green, #3edc81);
481
+ --wb-status-failed: var(--vscode-charts-red, #fa4d56);
482
+ --wb-status-cancelled: var(--vscode-descriptionForeground, #99a2ad);
483
+ --wb-status-cancelled-weak: color-mix(in srgb, var(--wb-status-cancelled) 55%, transparent);
484
+ }
393
485
  }
394
486
 
395
- function doPoll() {
396
- if (pollActive) return;
397
- pollActive = true;
398
- loadBoard().then(function () {
399
- pollActive = false;
400
- pollTimer = setTimeout(doPoll, 4000);
401
- }).catch(function () {
402
- pollActive = false;
403
- pollTimer = setTimeout(doPoll, 4000);
404
- });
405
- }
487
+ /* \u2500\u2500 Base \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
406
488
 
407
- // ============================================================
408
- // Toolbar wiring
409
- // ============================================================
489
+ * { box-sizing: border-box; margin: 0; padding: 0; }
410
490
 
411
- document.getElementById('columns').addEventListener('click', function (evt) {
412
- var btn = evt.target.closest ? evt.target.closest('button[data-action]') : null;
413
- if (!btn) return;
414
- applyAction(btn.getAttribute('data-taskid'), btn.getAttribute('data-action'));
415
- });
491
+ html, body {
492
+ height: 100%;
493
+ font-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
494
+ font-size: 13px;
495
+ background: var(--wb-bg);
496
+ color: var(--wb-ink);
497
+ overflow: hidden;
498
+ }
416
499
 
417
- document.getElementById('refresh-btn').addEventListener('click', function () {
418
- loadBoard();
419
- });
500
+ .mono {
501
+ font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
502
+ }
420
503
 
421
- document.getElementById('go-btn').addEventListener('click', function () {
422
- var v = document.getElementById('board-input').value.trim();
423
- if (!v) return;
424
- boardId = v;
425
- loadBoard();
426
- });
504
+ #app { display: flex; flex-direction: column; height: 100%; }
427
505
 
428
- document.getElementById('add-btn').addEventListener('click', function () {
429
- var input = document.getElementById('new-title');
430
- var title = input.value.trim();
431
- if (!title) return;
432
- var args = {title: title};
433
- if (boardId) args.boardId = boardId;
434
- callTool('task_create', args).then(function (result) {
435
- if (result && result.error) {
436
- setStatus('Create failed: ' + result.error);
437
- return;
438
- }
439
- input.value = '';
440
- setStatus('Created "' + title + '".');
441
- return loadBoard();
442
- }).catch(function (e) {
443
- setStatus('Create failed: ' + e.message);
444
- });
445
- });
506
+ #toolbar {
507
+ padding: 8px 12px;
508
+ border-bottom: 1px solid var(--wb-border);
509
+ display: flex;
510
+ align-items: center;
511
+ gap: 8px;
512
+ background: var(--wb-surface);
513
+ flex-shrink: 0;
514
+ }
515
+ #board-label { font-size: 11px; color: var(--wb-ink-muted); }
516
+ #board-id { font-weight: 600; color: var(--wb-ink); font-size: 12px; font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace; }
517
+ #board-input {
518
+ background: var(--wb-bg);
519
+ border: 1px solid var(--wb-border);
520
+ color: var(--wb-ink);
521
+ padding: 4px 8px;
522
+ border-radius: 4px;
523
+ font-size: 12px;
524
+ font-family: inherit;
525
+ width: 220px;
526
+ }
527
+ .tbtn {
528
+ background: var(--wb-bg);
529
+ border: 1px solid var(--wb-border);
530
+ color: var(--wb-ink);
531
+ padding: 4px 10px;
532
+ border-radius: 4px;
533
+ cursor: pointer;
534
+ font-size: 12px;
535
+ font-family: inherit;
536
+ }
537
+ .tbtn:hover { background: var(--wb-border); }
538
+ #new-title {
539
+ flex: 1;
540
+ min-width: 120px;
541
+ background: var(--wb-bg);
542
+ border: 1px solid var(--wb-border);
543
+ color: var(--wb-ink);
544
+ padding: 4px 8px;
545
+ border-radius: 4px;
546
+ font-size: 12px;
547
+ font-family: inherit;
548
+ }
446
549
 
447
- // ============================================================
448
- // Boot
449
- // ============================================================
550
+ #columns { flex: 1; display: flex; overflow-x: auto; overflow-y: hidden; }
551
+ .col {
552
+ width: 260px;
553
+ min-width: 220px;
554
+ flex-shrink: 0;
555
+ display: flex;
556
+ flex-direction: column;
557
+ border-right: 1px solid var(--wb-border);
558
+ }
559
+ .col-hdr {
560
+ padding: 8px 10px;
561
+ border-bottom: 1px solid var(--wb-border);
562
+ background: var(--wb-surface);
563
+ font-size: 11px;
564
+ font-weight: 600;
565
+ color: var(--wb-ink-muted);
566
+ text-transform: uppercase;
567
+ letter-spacing: 0.05em;
568
+ display: flex;
569
+ justify-content: space-between;
570
+ }
571
+ .col-body { flex: 1; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 8px; }
450
572
 
451
- initBridge().then(function () {
452
- return loadBoard();
453
- }).then(function () {
454
- pollTimer = setTimeout(doPoll, 4000);
455
- }).catch(function (e) {
456
- setStatus('Bridge error: ' + e.message);
457
- });
458
- </script>
573
+ .card {
574
+ background: var(--wb-surface);
575
+ border: 1px solid var(--wb-border);
576
+ border-radius: 6px;
577
+ padding: 8px 10px;
578
+ }
579
+ .c-title { font-size: 12px; font-weight: 500; margin-bottom: 4px; word-break: break-word; }
580
+ .c-id {
581
+ font-size: 10px;
582
+ color: var(--wb-ink-muted);
583
+ margin-bottom: 6px;
584
+ font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
585
+ }
586
+ .c-row { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin-bottom: 6px; }
587
+
588
+ .tag { display: inline-block; padding: 1px 6px; border-radius: 10px; font-size: 10px; font-weight: 600; white-space: nowrap; }
589
+ .t-unclaimed { background: color-mix(in srgb, var(--wb-ink-muted) 15%, transparent); color: var(--wb-ink-muted); border: 1px dashed var(--wb-ink-muted); }
590
+ .t-owner { background: color-mix(in srgb, var(--wb-accent) 15%, transparent); color: var(--wb-accent); }
591
+
592
+ /* Verification tell: gate solid green, self-report amber outline, human
593
+ blue, a declared verify with no verification yet grey "gated" \u2014 never
594
+ render an unverified done as gate-passed (see render.ts's verificationTag). */
595
+ .t-gate { background: color-mix(in srgb, var(--wb-status-done) 18%, transparent); color: var(--wb-status-done); }
596
+ .t-self { background: transparent; color: var(--wb-status-in-progress); border: 1px solid var(--wb-status-in-progress); }
597
+ .t-human { background: color-mix(in srgb, var(--wb-accent) 18%, transparent); color: var(--wb-accent); }
598
+ .t-gated { background: color-mix(in srgb, var(--wb-ink-muted) 12%, transparent); color: var(--wb-ink-muted); }
599
+ .t-cancelled {
600
+ background: color-mix(in srgb, var(--wb-status-cancelled) 10%, transparent);
601
+ color: var(--wb-status-cancelled-weak);
602
+ border: 1px dashed color-mix(in srgb, var(--wb-status-cancelled) 40%, transparent);
603
+ }
604
+
605
+ .c-err { font-size: 10px; color: var(--wb-status-failed); margin-bottom: 6px; word-break: break-word; }
606
+ .c-actions { display: flex; flex-wrap: wrap; gap: 4px; }
607
+ .abtn {
608
+ background: var(--wb-bg);
609
+ border: 1px solid var(--wb-border);
610
+ color: var(--wb-ink);
611
+ padding: 3px 8px;
612
+ border-radius: 4px;
613
+ cursor: pointer;
614
+ font-size: 11px;
615
+ font-family: inherit;
616
+ }
617
+ .abtn:hover { background: var(--wb-border); }
618
+ .abtn.danger { border-color: var(--wb-status-failed); color: var(--wb-status-failed); }
619
+ .abtn.primary { border-color: var(--wb-status-done); color: var(--wb-status-done); }
620
+
621
+ #statusbar {
622
+ padding: 4px 12px;
623
+ font-size: 11px;
624
+ color: var(--wb-ink-muted);
625
+ border-top: 1px solid var(--wb-border);
626
+ background: var(--wb-surface);
627
+ flex-shrink: 0;
628
+ }
629
+ .empty { padding: 16px 8px; color: var(--wb-ink-muted); font-size: 11px; text-align: center; }</style>
630
+ </head>
631
+ <body>
632
+ <div id="app">
633
+ <div id="toolbar">
634
+ <span id="board-label">Board:</span>
635
+ <span id="board-id">\u2014</span>
636
+ <input id="board-input" type="text" placeholder="tree:&lt;sessionId&gt; or ws:&lt;slug&gt;">
637
+ <button class="tbtn" id="go-btn">Go</button>
638
+ <button class="tbtn" id="refresh-btn" title="Refresh">&#8635;</button>
639
+ <input id="new-title" type="text" placeholder="New task title&#8230;">
640
+ <button class="tbtn" id="add-btn">+ Add</button>
641
+ </div>
642
+ <div id="columns"></div>
643
+ <div id="statusbar">Connecting to bridge&#8230;</div>
644
+ </div>
459
645
  </body>
460
- </html>`;
646
+ </html>
647
+ `;
648
+
649
+ // src/work-board/panel.ts
650
+ var WORK_BOARD_APP_ID = "@agentproto/work-board";
651
+ var WORK_BOARD_TOOL_ID = "agentproto_work_board";
652
+ var WORK_BOARD_UI_TOOLS = [
653
+ "task_list",
654
+ "task_claim",
655
+ "task_update",
656
+ "task_create"
657
+ ];
658
+ var WORK_BOARD_HTML = WORK_BOARD_HTML_GENERATED;
461
659
 
462
- export { WORK_BOARD_HTML };
660
+ export { WORK_BOARD_APP_ID, WORK_BOARD_HTML, WORK_BOARD_TOOL_ID, WORK_BOARD_UI_TOOLS };
463
661
  //# sourceMappingURL=panel.mjs.map
464
662
  //# sourceMappingURL=panel.mjs.map