@agentproto/apps 0.9.3 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/agents-overview/panel.mjs +18 -1
- package/dist/agents-overview/panel.mjs.map +1 -1
- package/dist/agents-overview.mjs +18 -1
- package/dist/agents-overview.mjs.map +1 -1
- package/dist/bureau-sessions/panel.mjs +18 -1
- package/dist/bureau-sessions/panel.mjs.map +1 -1
- package/dist/bureau-sessions.mjs +18 -1
- package/dist/bureau-sessions.mjs.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +777 -2
- package/dist/index.mjs.map +1 -1
- package/dist/live-session/panel.mjs +18 -1
- package/dist/live-session/panel.mjs.map +1 -1
- package/dist/live-session.mjs +18 -1
- package/dist/live-session.mjs.map +1 -1
- package/dist/mcp-app-types.d.ts +4 -0
- package/dist/mcp-app-types.d.ts.map +1 -1
- package/dist/panel-bridge.d.ts +48 -0
- package/dist/panel-bridge.d.ts.map +1 -1
- package/dist/session-chat/index.d.ts +72 -0
- package/dist/session-chat/index.d.ts.map +1 -0
- package/dist/session-chat/panel.d.ts +28 -0
- package/dist/session-chat/panel.d.ts.map +1 -0
- package/dist/session-chat/panel.mjs +65 -0
- package/dist/session-chat/panel.mjs.map +1 -0
- package/dist/session-chat.mjs +113 -0
- package/dist/session-chat.mjs.map +1 -0
- package/dist/session-story/panel.mjs +18 -1
- package/dist/session-story/panel.mjs.map +1 -1
- package/dist/session-story.mjs +18 -1
- package/dist/session-story.mjs.map +1 -1
- package/dist/sessions-panel/panel.mjs +18 -1
- package/dist/sessions-panel/panel.mjs.map +1 -1
- package/dist/sessions-panel.mjs +18 -1
- package/dist/sessions-panel.mjs.map +1 -1
- package/dist/work-board/index.d.ts +67 -0
- package/dist/work-board/index.d.ts.map +1 -0
- package/dist/work-board/panel.d.ts +72 -0
- package/dist/work-board/panel.d.ts.map +1 -0
- package/dist/work-board/panel.generated.d.ts +13 -0
- package/dist/work-board/panel.generated.d.ts.map +1 -0
- package/dist/work-board/panel.mjs +637 -0
- package/dist/work-board/panel.mjs.map +1 -0
- package/dist/work-board.mjs +669 -0
- package/dist/work-board.mjs.map +1 -0
- package/package.json +32 -8
package/dist/index.mjs
CHANGED
|
@@ -2888,6 +2888,10 @@ function panelBridgeScript(appName) {
|
|
|
2888
2888
|
// JSON-RPC 2.0 over window.parent.postMessage \xB7 spec 2026-01-26
|
|
2889
2889
|
var _nextId = 1, _pending = {}, _notifyHandlers = [];
|
|
2890
2890
|
var _hostContext = null, _hostContextHandlers = [];
|
|
2891
|
+
var _standaloneApp = null;
|
|
2892
|
+
function _isStandalone(){
|
|
2893
|
+
return window.parent === window && !!window.McpApp && typeof window.McpApp.connect === 'function';
|
|
2894
|
+
}
|
|
2891
2895
|
function post(msg){ window.parent.postMessage(msg, '*'); }
|
|
2892
2896
|
function getHostContext(){ return _hostContext; }
|
|
2893
2897
|
function onHostContext(cb){
|
|
@@ -2934,6 +2938,16 @@ window.addEventListener('message', function(evt){
|
|
|
2934
2938
|
}
|
|
2935
2939
|
});
|
|
2936
2940
|
function initBridge(){
|
|
2941
|
+
if (_isStandalone()){
|
|
2942
|
+
return window.McpApp.connect().then(function(conn){
|
|
2943
|
+
_standaloneApp = conn;
|
|
2944
|
+
// No host to advertise a hostContext \u2014 default to inline with no
|
|
2945
|
+
// other modes available, which keeps the #dm/#pin toggle buttons
|
|
2946
|
+
// hidden (they only show for modes hostContext.availableDisplayModes
|
|
2947
|
+
// lists) instead of rendering two buttons with nothing to switch to.
|
|
2948
|
+
_setHostContext({displayMode: 'inline', availableDisplayModes: []});
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2937
2951
|
return rpcRequest('ui/initialize', {
|
|
2938
2952
|
appInfo: {name: ${JSON.stringify(appName)}, version: '0.1.0'},
|
|
2939
2953
|
appCapabilities: {availableDisplayModes: ['inline', 'fullscreen', 'pip']},
|
|
@@ -2949,7 +2963,10 @@ function requestDisplayMode(mode){
|
|
|
2949
2963
|
return rpcRequest('ui/request-display-mode', {mode: mode});
|
|
2950
2964
|
}
|
|
2951
2965
|
function callTool(name, args){
|
|
2952
|
-
|
|
2966
|
+
var raw = _standaloneApp
|
|
2967
|
+
? _standaloneApp.callTool(name, args || {})
|
|
2968
|
+
: rpcRequest('tools/call', {name: name, arguments: args || {}});
|
|
2969
|
+
return raw.then(function(result){
|
|
2953
2970
|
if (result.isError){
|
|
2954
2971
|
var e = (result.content && result.content[0] && result.content[0].text) || 'tool error';
|
|
2955
2972
|
throw new Error(e);
|
|
@@ -5490,6 +5507,764 @@ var liveSessionApp = defineApp({
|
|
|
5490
5507
|
}
|
|
5491
5508
|
});
|
|
5492
5509
|
|
|
5493
|
-
|
|
5510
|
+
// src/session-chat/panel.ts
|
|
5511
|
+
function escapeAttr(text) {
|
|
5512
|
+
return text.replace(/&/g, "&").replace(/"/g, """);
|
|
5513
|
+
}
|
|
5514
|
+
function sessionChatEmbedHtml(initData) {
|
|
5515
|
+
const url = initData.url;
|
|
5516
|
+
const link = url != null ? `<a href="${escapeAttr(url)}" target="_blank" rel="noreferrer">open in a tab</a>` : "app not installed";
|
|
5517
|
+
const iframe = url != null ? `<iframe id="chat" title="Session Chat" src="${escapeAttr(url)}"></iframe>` : "";
|
|
5518
|
+
return `<!DOCTYPE html>
|
|
5519
|
+
<html lang="en">
|
|
5520
|
+
<head>
|
|
5521
|
+
<meta charset="UTF-8">
|
|
5522
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5523
|
+
<title>Session Chat</title>
|
|
5524
|
+
<style>
|
|
5525
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
5526
|
+
html,body{height:100%;font-family:system-ui,-apple-system,sans-serif;background:#0d1117;color:#e6edf3;overflow:hidden}
|
|
5527
|
+
#bar{padding:6px 12px;font-size:12px;color:#8b949e;background:#161b22;border-bottom:1px solid #30363d}
|
|
5528
|
+
#bar a{color:#58a6ff}
|
|
5529
|
+
#chat{border:0;display:block;width:100%;height:calc(100% - 29px)}
|
|
5530
|
+
</style>
|
|
5531
|
+
</head>
|
|
5532
|
+
<body>
|
|
5533
|
+
<div id="bar">Session Chat · ${link}</div>
|
|
5534
|
+
${iframe}
|
|
5535
|
+
</body>
|
|
5536
|
+
</html>`;
|
|
5537
|
+
}
|
|
5538
|
+
var SESSION_CHAT_FALLBACK_HTML = `<!DOCTYPE html>
|
|
5539
|
+
<html lang="en">
|
|
5540
|
+
<head>
|
|
5541
|
+
<meta charset="UTF-8">
|
|
5542
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5543
|
+
<title>Session Chat \u2014 not installed</title>
|
|
5544
|
+
<style>
|
|
5545
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
5546
|
+
html,body{height:100%;font-family:system-ui,-apple-system,sans-serif;background:#0d1117;color:#e6edf3}
|
|
5547
|
+
#wrap{max-width:560px;margin:0 auto;padding:48px 24px;line-height:1.6}
|
|
5548
|
+
h1{font-size:18px;margin-bottom:12px}
|
|
5549
|
+
code{background:#161b22;border:1px solid #30363d;border-radius:4px;padding:2px 6px;font-size:13px}
|
|
5550
|
+
p{margin:8px 0;color:#8b949e;font-size:13px}
|
|
5551
|
+
</style>
|
|
5552
|
+
</head>
|
|
5553
|
+
<body>
|
|
5554
|
+
<div id="wrap">
|
|
5555
|
+
<h1>Session Chat is not installed</h1>
|
|
5556
|
+
<p>This panel is a thin launcher for the <code>@agentik/session-chat</code> app —
|
|
5557
|
+
it does not bundle the chat UI itself.</p>
|
|
5558
|
+
<p>To use it, install the app into this daemon:</p>
|
|
5559
|
+
<p><code>agentproto app install @agentik/session-chat</code></p>
|
|
5560
|
+
<p>Once installed, call <code>agentproto_session_chat</code> again (optionally with a
|
|
5561
|
+
<code>sessionId</code>) and the panel will embed the app's chat UI deep-linked to that
|
|
5562
|
+
session.</p>
|
|
5563
|
+
</div>
|
|
5564
|
+
</body>
|
|
5565
|
+
</html>`;
|
|
5566
|
+
|
|
5567
|
+
// src/session-chat/index.ts
|
|
5568
|
+
var SESSION_CHAT_APP_ID = "@agentik/session-chat";
|
|
5569
|
+
var sessionChatInputSchema = z.object({
|
|
5570
|
+
sessionId: z.string().optional().describe(
|
|
5571
|
+
"Deep-link the chat to an existing session by id (e.g. `sess_xxx`). Omit to open the app with its session picker."
|
|
5572
|
+
)
|
|
5573
|
+
});
|
|
5574
|
+
function sessionChatAppUrl(httpBaseUrl, sessionId) {
|
|
5575
|
+
const base = `${httpBaseUrl.replace(/\/+$/, "")}/apps/${SESSION_CHAT_APP_ID}/ui`;
|
|
5576
|
+
const params = new URLSearchParams();
|
|
5577
|
+
if (sessionId) params.set("session", sessionId);
|
|
5578
|
+
params.set("embed", "1");
|
|
5579
|
+
return `${base}?${params.toString()}`;
|
|
5580
|
+
}
|
|
5581
|
+
function frameOrigin(httpBaseUrl) {
|
|
5582
|
+
return new URL(httpBaseUrl).origin;
|
|
5583
|
+
}
|
|
5584
|
+
function makeSessionChatApp(ops) {
|
|
5585
|
+
const installed = ops.isSessionChatInstalled?.() ?? false;
|
|
5586
|
+
return {
|
|
5587
|
+
id: "agentproto_session_chat",
|
|
5588
|
+
title: "Session Chat",
|
|
5589
|
+
description: "Open the Session Chat app \u2014 a chat-thread view of any agentproto daemon session: full transcript replay, live follow-over SSE, and a composer that sends prompts into the same session. Requires the `@agentik/session-chat` app to be installed; without it the panel shows install instructions. Pass `sessionId` to deep-link straight into a known session.",
|
|
5590
|
+
inputSchema: sessionChatInputSchema,
|
|
5591
|
+
execute: async (input) => ({
|
|
5592
|
+
installed,
|
|
5593
|
+
url: installed ? sessionChatAppUrl(ops.httpBaseUrl, input.sessionId) : null
|
|
5594
|
+
}),
|
|
5595
|
+
html: (initData) => sessionChatEmbedHtml(initData),
|
|
5596
|
+
csp: { frameDomains: [frameOrigin(ops.httpBaseUrl)] }
|
|
5597
|
+
};
|
|
5598
|
+
}
|
|
5599
|
+
var sessionChatApp = defineApp({
|
|
5600
|
+
id: "@agentproto/session-chat-widget",
|
|
5601
|
+
name: "Session Chat",
|
|
5602
|
+
description: "Open the Session Chat app \u2014 a chat-thread view of any agentproto daemon session, deep-linked by sessionId. A thin builtin launcher for the installed @agentik/session-chat app.",
|
|
5603
|
+
agents: [],
|
|
5604
|
+
ui: {
|
|
5605
|
+
html: SESSION_CHAT_FALLBACK_HTML,
|
|
5606
|
+
title: "Session Chat",
|
|
5607
|
+
tools: ["session_list", "agent_start", "adapter_list", "conversation_read"]
|
|
5608
|
+
}
|
|
5609
|
+
});
|
|
5610
|
+
|
|
5611
|
+
// src/work-board/panel.generated.ts
|
|
5612
|
+
var WORK_BOARD_HTML_GENERATED = `<!DOCTYPE html>
|
|
5613
|
+
<html lang="en">
|
|
5614
|
+
<head>
|
|
5615
|
+
<meta charset="UTF-8" />
|
|
5616
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5617
|
+
<title>agentproto work board</title>
|
|
5618
|
+
<!-- PANEL_BRIDGE: replaced with the inline panelBridgeScript() output by
|
|
5619
|
+
vite.config.ts's injectPanelBridge plugin \u2014 a classic (non-module)
|
|
5620
|
+
script so its \`initBridge\`/\`callTool\` land as globals before the
|
|
5621
|
+
deferred module script below runs. -->
|
|
5622
|
+
<script>// \u2500\u2500 MCP Apps bridge (shared: panel-bridge.ts) \u2500\u2500
|
|
5623
|
+
// JSON-RPC 2.0 over window.parent.postMessage \xB7 spec 2026-01-26
|
|
5624
|
+
var _nextId = 1, _pending = {}, _notifyHandlers = [];
|
|
5625
|
+
var _hostContext = null, _hostContextHandlers = [];
|
|
5626
|
+
var _standaloneApp = null;
|
|
5627
|
+
function _isStandalone(){
|
|
5628
|
+
return window.parent === window && !!window.McpApp && typeof window.McpApp.connect === 'function';
|
|
5629
|
+
}
|
|
5630
|
+
function post(msg){ window.parent.postMessage(msg, '*'); }
|
|
5631
|
+
function getHostContext(){ return _hostContext; }
|
|
5632
|
+
function onHostContext(cb){
|
|
5633
|
+
_hostContextHandlers.push(cb);
|
|
5634
|
+
// Replay the last context so a late subscriber isn't stuck blind.
|
|
5635
|
+
if (_hostContext){ try { cb(_hostContext); } catch(_) {} }
|
|
5636
|
+
}
|
|
5637
|
+
function _setHostContext(ctx){
|
|
5638
|
+
if (!ctx || typeof ctx !== 'object') return;
|
|
5639
|
+
// ui/notifications/host-context-changed carries only the changed keys \u2014
|
|
5640
|
+
// merge, matching the official ext-apps App behaviour.
|
|
5641
|
+
_hostContext = Object.assign({}, _hostContext || {}, ctx);
|
|
5642
|
+
for (var i = 0; i < _hostContextHandlers.length; i++){
|
|
5643
|
+
try { _hostContextHandlers[i](_hostContext); } catch(_) {}
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
function rpcRequest(method, params){
|
|
5647
|
+
return new Promise(function(resolve, reject){
|
|
5648
|
+
var id = _nextId++;
|
|
5649
|
+
_pending[id] = {resolve: resolve, reject: reject};
|
|
5650
|
+
post({jsonrpc: '2.0', id: id, method: method, params: params || {}});
|
|
5651
|
+
});
|
|
5652
|
+
}
|
|
5653
|
+
function rpcNotify(method, params){ post({jsonrpc: '2.0', method: method, params: params || {}}); }
|
|
5654
|
+
function onHostNotification(cb){ _notifyHandlers.push(cb); }
|
|
5655
|
+
window.addEventListener('message', function(evt){
|
|
5656
|
+
var msg = evt.data;
|
|
5657
|
+
if (!msg || typeof msg !== 'object' || msg.jsonrpc !== '2.0') return;
|
|
5658
|
+
if (msg.id != null && msg.method == null){
|
|
5659
|
+
var p = _pending[msg.id];
|
|
5660
|
+
if (!p) return;
|
|
5661
|
+
delete _pending[msg.id];
|
|
5662
|
+
if (msg.error) p.reject(new Error(msg.error.message || ('rpc error ' + msg.error.code)));
|
|
5663
|
+
else p.resolve(msg.result);
|
|
5664
|
+
return;
|
|
5665
|
+
}
|
|
5666
|
+
if (msg.method){
|
|
5667
|
+
if (msg.method === 'ui/notifications/host-context-changed'){
|
|
5668
|
+
_setHostContext(msg.params || {});
|
|
5669
|
+
}
|
|
5670
|
+
for (var i = 0; i < _notifyHandlers.length; i++){
|
|
5671
|
+
try { _notifyHandlers[i](msg.method, msg.params || {}); } catch(_) {}
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
5674
|
+
});
|
|
5675
|
+
function initBridge(){
|
|
5676
|
+
if (_isStandalone()){
|
|
5677
|
+
return window.McpApp.connect().then(function(conn){
|
|
5678
|
+
_standaloneApp = conn;
|
|
5679
|
+
// No host to advertise a hostContext \u2014 default to inline with no
|
|
5680
|
+
// other modes available, which keeps the #dm/#pin toggle buttons
|
|
5681
|
+
// hidden (they only show for modes hostContext.availableDisplayModes
|
|
5682
|
+
// lists) instead of rendering two buttons with nothing to switch to.
|
|
5683
|
+
_setHostContext({displayMode: 'inline', availableDisplayModes: []});
|
|
5684
|
+
});
|
|
5685
|
+
}
|
|
5686
|
+
return rpcRequest('ui/initialize', {
|
|
5687
|
+
appInfo: {name: "agentproto-work-board-panel", version: '0.1.0'},
|
|
5688
|
+
appCapabilities: {availableDisplayModes: ['inline', 'fullscreen', 'pip']},
|
|
5689
|
+
protocolVersion: '2026-01-26'
|
|
5690
|
+
}).then(function(result){
|
|
5691
|
+
// The initialize result carries the initial hostContext (displayMode +
|
|
5692
|
+
// availableDisplayModes) \u2014 capture it before notifying the host.
|
|
5693
|
+
if (result && result.hostContext) _setHostContext(result.hostContext);
|
|
5694
|
+
rpcNotify('ui/notifications/initialized', {});
|
|
5695
|
+
});
|
|
5696
|
+
}
|
|
5697
|
+
function requestDisplayMode(mode){
|
|
5698
|
+
return rpcRequest('ui/request-display-mode', {mode: mode});
|
|
5699
|
+
}
|
|
5700
|
+
function callTool(name, args){
|
|
5701
|
+
var raw = _standaloneApp
|
|
5702
|
+
? _standaloneApp.callTool(name, args || {})
|
|
5703
|
+
: rpcRequest('tools/call', {name: name, arguments: args || {}});
|
|
5704
|
+
return raw.then(function(result){
|
|
5705
|
+
if (result.isError){
|
|
5706
|
+
var e = (result.content && result.content[0] && result.content[0].text) || 'tool error';
|
|
5707
|
+
throw new Error(e);
|
|
5708
|
+
}
|
|
5709
|
+
var text = (result.content && result.content[0] && result.content[0].text) || '{}';
|
|
5710
|
+
return JSON.parse(text);
|
|
5711
|
+
});
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
// \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
|
|
5715
|
+
// Injected by the shared bridge so every panel gets them without touching
|
|
5716
|
+
// its own markup. Mirrors guilde canvas.app.ts canvasShellHtml(): the
|
|
5717
|
+
// panel stays inline by default; the user expands on demand. Buttons only
|
|
5718
|
+
// appear for modes the host advertises in hostContext.availableDisplayModes.
|
|
5719
|
+
(function(){
|
|
5720
|
+
function mount(){
|
|
5721
|
+
var style = document.createElement('style');
|
|
5722
|
+
style.textContent = '#dm,#pin{display:none;position:fixed;top:8px;z-index:10;'
|
|
5723
|
+
+ 'border:1px solid #d0d0d0;background:#fff;color:#1a1a1a;'
|
|
5724
|
+
+ 'font:600 13px/1 system-ui,sans-serif;padding:7px 12px;border-radius:6px;'
|
|
5725
|
+
+ 'cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.18)}'
|
|
5726
|
+
+ '#dm{right:8px}#pin{right:118px}'
|
|
5727
|
+
+ '#dm:hover,#pin:hover{background:#f2f2f2;border-color:#b0b0b0}'
|
|
5728
|
+
+ '@media (prefers-color-scheme:dark){'
|
|
5729
|
+
+ '#dm,#pin{border-color:#555;background:#2a2a2a;color:#f0f0f0;box-shadow:0 1px 4px rgba(0,0,0,.5)}'
|
|
5730
|
+
+ '#dm:hover,#pin:hover{background:#333;border-color:#777}}';
|
|
5731
|
+
document.head.appendChild(style);
|
|
5732
|
+
|
|
5733
|
+
var btn = document.createElement('button');
|
|
5734
|
+
btn.id = 'dm'; btn.type = 'button'; btn.title = "Basculer l'affichage";
|
|
5735
|
+
var pin = document.createElement('button');
|
|
5736
|
+
pin.id = 'pin'; pin.type = 'button'; pin.title = '\xC9pingler sur le c\xF4t\xE9 (pip)';
|
|
5737
|
+
document.body.appendChild(pin);
|
|
5738
|
+
document.body.appendChild(btn);
|
|
5739
|
+
|
|
5740
|
+
function has(avail, m){ return !!avail && avail.indexOf(m) >= 0; }
|
|
5741
|
+
|
|
5742
|
+
// Re-sync button visibility + label from the current host context.
|
|
5743
|
+
function syncBtn(ctx){
|
|
5744
|
+
ctx = ctx || {};
|
|
5745
|
+
var avail = ctx.availableDisplayModes;
|
|
5746
|
+
// Diagnostic: what does THIS host actually advertise? (inline/fullscreen/pip)
|
|
5747
|
+
console.log('[mcp-app] displayMode=', ctx.displayMode,
|
|
5748
|
+
'availableDisplayModes=', avail);
|
|
5749
|
+
|
|
5750
|
+
// Fullscreen toggle button.
|
|
5751
|
+
if (has(avail, 'fullscreen')){
|
|
5752
|
+
btn.style.display = 'block';
|
|
5753
|
+
btn.textContent = (ctx.displayMode === 'fullscreen') ? '\u2921 R\xE9duire' : '\u2922 Agrandir';
|
|
5754
|
+
} else { btn.style.display = 'none'; }
|
|
5755
|
+
|
|
5756
|
+
// Dedicated pip ("pinned on side") button \u2014 only if the host advertises pip.
|
|
5757
|
+
if (has(avail, 'pip')){
|
|
5758
|
+
pin.style.display = 'block';
|
|
5759
|
+
pin.textContent = (ctx.displayMode === 'pip') ? '\u2921 D\xE9tacher' : '\u{1F4CC} \xC9pingler';
|
|
5760
|
+
} else { pin.style.display = 'none'; }
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
onHostContext(syncBtn);
|
|
5764
|
+
|
|
5765
|
+
btn.addEventListener('click', function(){
|
|
5766
|
+
var ctx = getHostContext() || {};
|
|
5767
|
+
var inPanel = (ctx.displayMode === 'fullscreen' || ctx.displayMode === 'pip');
|
|
5768
|
+
requestDisplayMode(inPanel ? 'inline' : 'fullscreen').catch(function(){});
|
|
5769
|
+
});
|
|
5770
|
+
|
|
5771
|
+
pin.addEventListener('click', function(){
|
|
5772
|
+
var ctx = getHostContext() || {};
|
|
5773
|
+
requestDisplayMode(ctx.displayMode === 'pip' ? 'inline' : 'pip').catch(function(){});
|
|
5774
|
+
});
|
|
5775
|
+
}
|
|
5776
|
+
if (document.body) mount();
|
|
5777
|
+
else document.addEventListener('DOMContentLoaded', mount);
|
|
5778
|
+
})();</script>
|
|
5779
|
+
<script>(function() {
|
|
5780
|
+
"use strict";
|
|
5781
|
+
const COLUMN_STATUSES = ["pending", "in_progress", "done", "failed"];
|
|
5782
|
+
const COLUMN_LABELS = {
|
|
5783
|
+
pending: "Pending",
|
|
5784
|
+
in_progress: "In Progress",
|
|
5785
|
+
done: "Done",
|
|
5786
|
+
failed: "Failed"
|
|
5787
|
+
};
|
|
5788
|
+
function esc(value) {
|
|
5789
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5790
|
+
}
|
|
5791
|
+
function columnOf(status) {
|
|
5792
|
+
return status === "cancelled" ? "failed" : status;
|
|
5793
|
+
}
|
|
5794
|
+
function verificationTag(task) {
|
|
5795
|
+
const v = task.verification;
|
|
5796
|
+
if (v) {
|
|
5797
|
+
if (v.kind === "gate") return '<span class="tag t-gate">✓ gate</span>';
|
|
5798
|
+
if (v.kind === "self-report") return '<span class="tag t-self">self-report</span>';
|
|
5799
|
+
if (v.kind === "human") return '<span class="tag t-human">human</span>';
|
|
5800
|
+
}
|
|
5801
|
+
if (task.status === "in_progress" && task.verify) {
|
|
5802
|
+
return '<span class="tag t-gated">gated</span>';
|
|
5803
|
+
}
|
|
5804
|
+
return "";
|
|
5805
|
+
}
|
|
5806
|
+
function ownerTag(task) {
|
|
5807
|
+
if (!task.owner) return '<span class="tag t-unclaimed">Unclaimed</span>';
|
|
5808
|
+
return \`<span class="tag t-owner">\${esc(task.owner)}</span>\`;
|
|
5809
|
+
}
|
|
5810
|
+
function actionsFor(task) {
|
|
5811
|
+
if (task.status === "pending") {
|
|
5812
|
+
return [
|
|
5813
|
+
task.owner ? { label: "Start", action: "start", cls: "primary" } : { label: "Claim", action: "claim", cls: "primary" },
|
|
5814
|
+
{ label: "Cancel", action: "cancel", cls: "danger" }
|
|
5815
|
+
];
|
|
5816
|
+
}
|
|
5817
|
+
if (task.status === "in_progress") {
|
|
5818
|
+
return [
|
|
5819
|
+
{ label: "Done", action: "done", cls: "primary" },
|
|
5820
|
+
{ label: "Fail", action: "fail", cls: "danger" },
|
|
5821
|
+
{ label: "Release", action: "release", cls: "" },
|
|
5822
|
+
{ label: "Cancel", action: "cancel", cls: "danger" }
|
|
5823
|
+
];
|
|
5824
|
+
}
|
|
5825
|
+
if (task.status === "done") {
|
|
5826
|
+
return [{ label: "Reopen", action: "reopen", cls: "" }];
|
|
5827
|
+
}
|
|
5828
|
+
return [];
|
|
5829
|
+
}
|
|
5830
|
+
function renderCard(task) {
|
|
5831
|
+
let html = \`<div class="card" data-taskid="\${esc(task.taskId)}">\`;
|
|
5832
|
+
html += \`<div class="c-title">\${esc(task.title)}</div>\`;
|
|
5833
|
+
html += \`<div class="c-id">#\${esc(task.taskId)}</div>\`;
|
|
5834
|
+
html += \`<div class="c-row">\${ownerTag(task)}\${verificationTag(task)}\`;
|
|
5835
|
+
if (task.status === "cancelled") html += '<span class="tag t-cancelled">cancelled</span>';
|
|
5836
|
+
html += "</div>";
|
|
5837
|
+
if (task.lastVerifyError) {
|
|
5838
|
+
html += \`<div class="c-err">verify failed: \${esc(task.lastVerifyError)}</div>\`;
|
|
5839
|
+
}
|
|
5840
|
+
const actions = actionsFor(task);
|
|
5841
|
+
if (actions.length > 0) {
|
|
5842
|
+
html += '<div class="c-actions">';
|
|
5843
|
+
for (const a of actions) {
|
|
5844
|
+
html += \`<button class="abtn \${a.cls}" data-taskid="\${esc(task.taskId)}" data-action="\${a.action}">\${a.label}</button>\`;
|
|
5845
|
+
}
|
|
5846
|
+
html += "</div>";
|
|
5847
|
+
}
|
|
5848
|
+
html += "</div>";
|
|
5849
|
+
return html;
|
|
5850
|
+
}
|
|
5851
|
+
function renderColumns(tasks2) {
|
|
5852
|
+
let html = "";
|
|
5853
|
+
for (const status of COLUMN_STATUSES) {
|
|
5854
|
+
const rows = tasks2.filter((t) => columnOf(t.status) === status);
|
|
5855
|
+
html += \`<div class="col"><div class="col-hdr"><span>\${COLUMN_LABELS[status]}</span><span>\${rows.length}</span></div><div class="col-body">\`;
|
|
5856
|
+
html += rows.length === 0 ? '<div class="empty">No tasks</div>' : rows.map(renderCard).join("");
|
|
5857
|
+
html += "</div></div>";
|
|
5858
|
+
}
|
|
5859
|
+
return html;
|
|
5860
|
+
}
|
|
5861
|
+
function getEl(id) {
|
|
5862
|
+
const el = document.getElementById(id);
|
|
5863
|
+
if (!el) throw new Error(\`work board: missing #\${id}\`);
|
|
5864
|
+
return el;
|
|
5865
|
+
}
|
|
5866
|
+
function getInput(id) {
|
|
5867
|
+
const el = document.getElementById(id);
|
|
5868
|
+
if (!(el instanceof HTMLInputElement)) throw new Error(\`work board: missing input #\${id}\`);
|
|
5869
|
+
return el;
|
|
5870
|
+
}
|
|
5871
|
+
function isCardAction(value) {
|
|
5872
|
+
switch (value) {
|
|
5873
|
+
case "claim":
|
|
5874
|
+
case "start":
|
|
5875
|
+
case "done":
|
|
5876
|
+
case "fail":
|
|
5877
|
+
case "release":
|
|
5878
|
+
case "cancel":
|
|
5879
|
+
case "reopen":
|
|
5880
|
+
return true;
|
|
5881
|
+
default:
|
|
5882
|
+
return false;
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
let boardId = null;
|
|
5886
|
+
let tasks = [];
|
|
5887
|
+
let pollActive = false;
|
|
5888
|
+
function boot() {
|
|
5889
|
+
const columnsEl = getEl("columns");
|
|
5890
|
+
const statusbarEl = getEl("statusbar");
|
|
5891
|
+
const boardIdEl = getEl("board-id");
|
|
5892
|
+
const boardInputEl = getInput("board-input");
|
|
5893
|
+
const newTitleEl = getInput("new-title");
|
|
5894
|
+
function setStatus(msg) {
|
|
5895
|
+
statusbarEl.textContent = msg;
|
|
5896
|
+
}
|
|
5897
|
+
function render() {
|
|
5898
|
+
boardIdEl.textContent = boardId ?? "\u2014";
|
|
5899
|
+
if (!boardInputEl.value) boardInputEl.value = boardId ?? "";
|
|
5900
|
+
columnsEl.innerHTML = renderColumns(tasks);
|
|
5901
|
+
}
|
|
5902
|
+
function findTask(taskId) {
|
|
5903
|
+
return tasks.find((t) => t.taskId === taskId);
|
|
5904
|
+
}
|
|
5905
|
+
function loadBoard() {
|
|
5906
|
+
const args = {
|
|
5907
|
+
includeClosed: true,
|
|
5908
|
+
full: true,
|
|
5909
|
+
...boardId ? { boardId } : {}
|
|
5910
|
+
};
|
|
5911
|
+
return callTool("task_list", args).then((data) => {
|
|
5912
|
+
boardId = data.boardId ?? boardId;
|
|
5913
|
+
tasks = data.tasks ?? [];
|
|
5914
|
+
render();
|
|
5915
|
+
setStatus(\`\${tasks.length} task\${tasks.length === 1 ? "" : "s"} \xB7 \${(/* @__PURE__ */ new Date()).toLocaleTimeString()}\`);
|
|
5916
|
+
}).catch((e) => {
|
|
5917
|
+
setStatus(\`Error: \${e.message}\`);
|
|
5918
|
+
});
|
|
5919
|
+
}
|
|
5920
|
+
function applyAction(taskId, action) {
|
|
5921
|
+
const task = findTask(taskId);
|
|
5922
|
+
if (!task) return;
|
|
5923
|
+
const base = { taskId: task.taskId, rev: task.rev };
|
|
5924
|
+
let promise;
|
|
5925
|
+
if (action === "claim") {
|
|
5926
|
+
promise = callTool("task_claim", base);
|
|
5927
|
+
} else if (action === "start") {
|
|
5928
|
+
promise = callTool("task_update", { ...base, status: "in_progress" });
|
|
5929
|
+
} else if (action === "done") {
|
|
5930
|
+
promise = callTool("task_update", { ...base, status: "done" });
|
|
5931
|
+
} else if (action === "fail") {
|
|
5932
|
+
promise = callTool("task_update", { ...base, status: "failed" });
|
|
5933
|
+
} else if (action === "release") {
|
|
5934
|
+
promise = callTool("task_update", { ...base, owner: null });
|
|
5935
|
+
} else if (action === "cancel") {
|
|
5936
|
+
promise = callTool("task_update", { ...base, status: "cancelled" });
|
|
5937
|
+
} else {
|
|
5938
|
+
promise = callTool("task_update", { ...base, status: "pending" });
|
|
5939
|
+
}
|
|
5940
|
+
promise.then((result) => {
|
|
5941
|
+
if ("conflict" in result) {
|
|
5942
|
+
setStatus(\`Someone else moved "\${task.title}" first \u2014 refreshed.\`);
|
|
5943
|
+
} else if ("error" in result) {
|
|
5944
|
+
setStatus(\`Action failed: \${result.error}\`);
|
|
5945
|
+
} else {
|
|
5946
|
+
setStatus(\`Updated "\${task.title}".\`);
|
|
5947
|
+
}
|
|
5948
|
+
return loadBoard();
|
|
5949
|
+
}).catch((e) => {
|
|
5950
|
+
setStatus(\`Action failed: \${e.message}\`);
|
|
5951
|
+
});
|
|
5952
|
+
}
|
|
5953
|
+
function doPoll() {
|
|
5954
|
+
if (pollActive) return;
|
|
5955
|
+
pollActive = true;
|
|
5956
|
+
loadBoard().finally(() => {
|
|
5957
|
+
pollActive = false;
|
|
5958
|
+
setTimeout(doPoll, 4e3);
|
|
5959
|
+
});
|
|
5960
|
+
}
|
|
5961
|
+
columnsEl.addEventListener("click", (evt) => {
|
|
5962
|
+
if (!(evt.target instanceof Element)) return;
|
|
5963
|
+
const btn = evt.target.closest("button[data-action]");
|
|
5964
|
+
if (!(btn instanceof HTMLButtonElement)) return;
|
|
5965
|
+
const taskId = btn.getAttribute("data-taskid");
|
|
5966
|
+
const action = btn.getAttribute("data-action");
|
|
5967
|
+
if (!taskId || !action || !isCardAction(action)) return;
|
|
5968
|
+
applyAction(taskId, action);
|
|
5969
|
+
});
|
|
5970
|
+
getEl("refresh-btn").addEventListener("click", () => {
|
|
5971
|
+
void loadBoard();
|
|
5972
|
+
});
|
|
5973
|
+
getEl("go-btn").addEventListener("click", () => {
|
|
5974
|
+
const value = boardInputEl.value.trim();
|
|
5975
|
+
if (!value) return;
|
|
5976
|
+
boardId = value;
|
|
5977
|
+
void loadBoard();
|
|
5978
|
+
});
|
|
5979
|
+
getEl("add-btn").addEventListener("click", () => {
|
|
5980
|
+
const title = newTitleEl.value.trim();
|
|
5981
|
+
if (!title) return;
|
|
5982
|
+
const args = boardId ? { title, boardId } : { title };
|
|
5983
|
+
callTool("task_create", args).then((result) => {
|
|
5984
|
+
if (result.error) {
|
|
5985
|
+
setStatus(\`Create failed: \${result.error}\`);
|
|
5986
|
+
return void 0;
|
|
5987
|
+
}
|
|
5988
|
+
newTitleEl.value = "";
|
|
5989
|
+
setStatus(\`Created "\${title}".\`);
|
|
5990
|
+
return loadBoard();
|
|
5991
|
+
}).catch((e) => {
|
|
5992
|
+
setStatus(\`Create failed: \${e.message}\`);
|
|
5993
|
+
});
|
|
5994
|
+
});
|
|
5995
|
+
initBridge().then(() => loadBoard()).then(() => {
|
|
5996
|
+
setTimeout(doPoll, 4e3);
|
|
5997
|
+
}).catch((e) => {
|
|
5998
|
+
setStatus(\`Bridge error: \${e.message}\`);
|
|
5999
|
+
});
|
|
6000
|
+
}
|
|
6001
|
+
if (document.readyState === "loading") {
|
|
6002
|
+
document.addEventListener("DOMContentLoaded", boot);
|
|
6003
|
+
} else {
|
|
6004
|
+
boot();
|
|
6005
|
+
}
|
|
6006
|
+
})();</script>
|
|
6007
|
+
<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
|
|
6008
|
+
Semantic tokens + runtime mapping sheets.
|
|
6009
|
+
|
|
6010
|
+
Every rule below the next section reads ONLY the --wb-* custom
|
|
6011
|
+
properties. Each one resolves through TWO mapping sheets, chosen at
|
|
6012
|
+
PAINT time via plain CSS var() fallback \u2014 no build step, no JS, so a
|
|
6013
|
+
host that swaps its theme live (VS Code) is never frozen at whatever
|
|
6014
|
+
theme happened to be active when this bundle was built:
|
|
6015
|
+
|
|
6016
|
+
1. embedded \u2014 VS Code injects a live --vscode-* custom-property set
|
|
6017
|
+
into the webview and swaps it on theme change. Whenever the
|
|
6018
|
+
relevant --vscode-* variable exists, it wins \u2014 in a VS Code
|
|
6019
|
+
\`srcdoc\` relay panel AND (structurally identically) any other host
|
|
6020
|
+
that happened to expose the same names, though none is known to.
|
|
6021
|
+
2. standalone \u2014 the agentproto brand tokens (see
|
|
6022
|
+
projects/agentproto/site/src/app/brand.css), used whenever no
|
|
6023
|
+
--vscode-* variable is set \u2014 a plain MCP-Apps host or the daemon's
|
|
6024
|
+
own \`/apps/.../ui\` tab. \`--phos\`/\`--amber\`/\`--crimson\` are
|
|
6025
|
+
THEME-INVARIANT in brand.css (the phosphor terminal palette), so
|
|
6026
|
+
they're identical in both blocks below; the rest follow brand.css's
|
|
6027
|
+
light/dark split via \`prefers-color-scheme\` \u2014 a standalone tab has
|
|
6028
|
+
no host to pick a theme for it, unlike VS Code's own.
|
|
6029
|
+
\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 */
|
|
6030
|
+
|
|
6031
|
+
:root {
|
|
6032
|
+
--wb-bg: var(--vscode-editor-background, #f5f6f3);
|
|
6033
|
+
--wb-surface: var(--vscode-sideBar-background, #ffffff);
|
|
6034
|
+
--wb-ink: var(--vscode-editor-foreground, #171a1e);
|
|
6035
|
+
--wb-ink-muted: var(--vscode-descriptionForeground, #5c6470);
|
|
6036
|
+
--wb-border: var(--vscode-panel-border, #dde0da);
|
|
6037
|
+
--wb-accent: var(--vscode-button-background, #0f62fe);
|
|
6038
|
+
|
|
6039
|
+
/* Status colors \u2014 phos/amber/crimson are the theme-invariant terminal
|
|
6040
|
+
palette in brand.css, so light and dark standalone fallbacks match. */
|
|
6041
|
+
--wb-status-pending: var(--vscode-descriptionForeground, #5c6470);
|
|
6042
|
+
--wb-status-in-progress: var(--vscode-charts-yellow, #f0b429);
|
|
6043
|
+
--wb-status-done: var(--vscode-charts-green, #3edc81);
|
|
6044
|
+
--wb-status-failed: var(--vscode-charts-red, #fa4d56);
|
|
6045
|
+
--wb-status-cancelled: var(--vscode-descriptionForeground, #5c6470);
|
|
6046
|
+
--wb-status-cancelled-weak: color-mix(in srgb, var(--wb-status-cancelled) 55%, transparent);
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
@media (prefers-color-scheme: dark) {
|
|
6050
|
+
:root {
|
|
6051
|
+
--wb-bg: var(--vscode-editor-background, #101214);
|
|
6052
|
+
--wb-surface: var(--vscode-sideBar-background, #16191d);
|
|
6053
|
+
--wb-ink: var(--vscode-editor-foreground, #e8eaed);
|
|
6054
|
+
--wb-ink-muted: var(--vscode-descriptionForeground, #99a2ad);
|
|
6055
|
+
--wb-border: var(--vscode-panel-border, #272c33);
|
|
6056
|
+
--wb-accent: var(--vscode-button-background, #4589ff);
|
|
6057
|
+
|
|
6058
|
+
--wb-status-pending: var(--vscode-descriptionForeground, #99a2ad);
|
|
6059
|
+
--wb-status-in-progress: var(--vscode-charts-yellow, #f0b429);
|
|
6060
|
+
--wb-status-done: var(--vscode-charts-green, #3edc81);
|
|
6061
|
+
--wb-status-failed: var(--vscode-charts-red, #fa4d56);
|
|
6062
|
+
--wb-status-cancelled: var(--vscode-descriptionForeground, #99a2ad);
|
|
6063
|
+
--wb-status-cancelled-weak: color-mix(in srgb, var(--wb-status-cancelled) 55%, transparent);
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6067
|
+
/* \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 */
|
|
6068
|
+
|
|
6069
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
6070
|
+
|
|
6071
|
+
html, body {
|
|
6072
|
+
height: 100%;
|
|
6073
|
+
font-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
6074
|
+
font-size: 13px;
|
|
6075
|
+
background: var(--wb-bg);
|
|
6076
|
+
color: var(--wb-ink);
|
|
6077
|
+
overflow: hidden;
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
.mono {
|
|
6081
|
+
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
|
|
6082
|
+
}
|
|
6083
|
+
|
|
6084
|
+
#app { display: flex; flex-direction: column; height: 100%; }
|
|
6085
|
+
|
|
6086
|
+
#toolbar {
|
|
6087
|
+
padding: 8px 12px;
|
|
6088
|
+
border-bottom: 1px solid var(--wb-border);
|
|
6089
|
+
display: flex;
|
|
6090
|
+
align-items: center;
|
|
6091
|
+
gap: 8px;
|
|
6092
|
+
background: var(--wb-surface);
|
|
6093
|
+
flex-shrink: 0;
|
|
6094
|
+
}
|
|
6095
|
+
#board-label { font-size: 11px; color: var(--wb-ink-muted); }
|
|
6096
|
+
#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; }
|
|
6097
|
+
#board-input {
|
|
6098
|
+
background: var(--wb-bg);
|
|
6099
|
+
border: 1px solid var(--wb-border);
|
|
6100
|
+
color: var(--wb-ink);
|
|
6101
|
+
padding: 4px 8px;
|
|
6102
|
+
border-radius: 4px;
|
|
6103
|
+
font-size: 12px;
|
|
6104
|
+
font-family: inherit;
|
|
6105
|
+
width: 220px;
|
|
6106
|
+
}
|
|
6107
|
+
.tbtn {
|
|
6108
|
+
background: var(--wb-bg);
|
|
6109
|
+
border: 1px solid var(--wb-border);
|
|
6110
|
+
color: var(--wb-ink);
|
|
6111
|
+
padding: 4px 10px;
|
|
6112
|
+
border-radius: 4px;
|
|
6113
|
+
cursor: pointer;
|
|
6114
|
+
font-size: 12px;
|
|
6115
|
+
font-family: inherit;
|
|
6116
|
+
}
|
|
6117
|
+
.tbtn:hover { background: var(--wb-border); }
|
|
6118
|
+
#new-title {
|
|
6119
|
+
flex: 1;
|
|
6120
|
+
min-width: 120px;
|
|
6121
|
+
background: var(--wb-bg);
|
|
6122
|
+
border: 1px solid var(--wb-border);
|
|
6123
|
+
color: var(--wb-ink);
|
|
6124
|
+
padding: 4px 8px;
|
|
6125
|
+
border-radius: 4px;
|
|
6126
|
+
font-size: 12px;
|
|
6127
|
+
font-family: inherit;
|
|
6128
|
+
}
|
|
6129
|
+
|
|
6130
|
+
#columns { flex: 1; display: flex; overflow-x: auto; overflow-y: hidden; }
|
|
6131
|
+
.col {
|
|
6132
|
+
width: 260px;
|
|
6133
|
+
min-width: 220px;
|
|
6134
|
+
flex-shrink: 0;
|
|
6135
|
+
display: flex;
|
|
6136
|
+
flex-direction: column;
|
|
6137
|
+
border-right: 1px solid var(--wb-border);
|
|
6138
|
+
}
|
|
6139
|
+
.col-hdr {
|
|
6140
|
+
padding: 8px 10px;
|
|
6141
|
+
border-bottom: 1px solid var(--wb-border);
|
|
6142
|
+
background: var(--wb-surface);
|
|
6143
|
+
font-size: 11px;
|
|
6144
|
+
font-weight: 600;
|
|
6145
|
+
color: var(--wb-ink-muted);
|
|
6146
|
+
text-transform: uppercase;
|
|
6147
|
+
letter-spacing: 0.05em;
|
|
6148
|
+
display: flex;
|
|
6149
|
+
justify-content: space-between;
|
|
6150
|
+
}
|
|
6151
|
+
.col-body { flex: 1; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 8px; }
|
|
6152
|
+
|
|
6153
|
+
.card {
|
|
6154
|
+
background: var(--wb-surface);
|
|
6155
|
+
border: 1px solid var(--wb-border);
|
|
6156
|
+
border-radius: 6px;
|
|
6157
|
+
padding: 8px 10px;
|
|
6158
|
+
}
|
|
6159
|
+
.c-title { font-size: 12px; font-weight: 500; margin-bottom: 4px; word-break: break-word; }
|
|
6160
|
+
.c-id {
|
|
6161
|
+
font-size: 10px;
|
|
6162
|
+
color: var(--wb-ink-muted);
|
|
6163
|
+
margin-bottom: 6px;
|
|
6164
|
+
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
|
|
6165
|
+
}
|
|
6166
|
+
.c-row { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin-bottom: 6px; }
|
|
6167
|
+
|
|
6168
|
+
.tag { display: inline-block; padding: 1px 6px; border-radius: 10px; font-size: 10px; font-weight: 600; white-space: nowrap; }
|
|
6169
|
+
.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); }
|
|
6170
|
+
.t-owner { background: color-mix(in srgb, var(--wb-accent) 15%, transparent); color: var(--wb-accent); }
|
|
6171
|
+
|
|
6172
|
+
/* Verification tell: gate solid green, self-report amber outline, human
|
|
6173
|
+
blue, a declared verify with no verification yet grey "gated" \u2014 never
|
|
6174
|
+
render an unverified done as gate-passed (see render.ts's verificationTag). */
|
|
6175
|
+
.t-gate { background: color-mix(in srgb, var(--wb-status-done) 18%, transparent); color: var(--wb-status-done); }
|
|
6176
|
+
.t-self { background: transparent; color: var(--wb-status-in-progress); border: 1px solid var(--wb-status-in-progress); }
|
|
6177
|
+
.t-human { background: color-mix(in srgb, var(--wb-accent) 18%, transparent); color: var(--wb-accent); }
|
|
6178
|
+
.t-gated { background: color-mix(in srgb, var(--wb-ink-muted) 12%, transparent); color: var(--wb-ink-muted); }
|
|
6179
|
+
.t-cancelled {
|
|
6180
|
+
background: color-mix(in srgb, var(--wb-status-cancelled) 10%, transparent);
|
|
6181
|
+
color: var(--wb-status-cancelled-weak);
|
|
6182
|
+
border: 1px dashed color-mix(in srgb, var(--wb-status-cancelled) 40%, transparent);
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
.c-err { font-size: 10px; color: var(--wb-status-failed); margin-bottom: 6px; word-break: break-word; }
|
|
6186
|
+
.c-actions { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
6187
|
+
.abtn {
|
|
6188
|
+
background: var(--wb-bg);
|
|
6189
|
+
border: 1px solid var(--wb-border);
|
|
6190
|
+
color: var(--wb-ink);
|
|
6191
|
+
padding: 3px 8px;
|
|
6192
|
+
border-radius: 4px;
|
|
6193
|
+
cursor: pointer;
|
|
6194
|
+
font-size: 11px;
|
|
6195
|
+
font-family: inherit;
|
|
6196
|
+
}
|
|
6197
|
+
.abtn:hover { background: var(--wb-border); }
|
|
6198
|
+
.abtn.danger { border-color: var(--wb-status-failed); color: var(--wb-status-failed); }
|
|
6199
|
+
.abtn.primary { border-color: var(--wb-status-done); color: var(--wb-status-done); }
|
|
6200
|
+
|
|
6201
|
+
#statusbar {
|
|
6202
|
+
padding: 4px 12px;
|
|
6203
|
+
font-size: 11px;
|
|
6204
|
+
color: var(--wb-ink-muted);
|
|
6205
|
+
border-top: 1px solid var(--wb-border);
|
|
6206
|
+
background: var(--wb-surface);
|
|
6207
|
+
flex-shrink: 0;
|
|
6208
|
+
}
|
|
6209
|
+
.empty { padding: 16px 8px; color: var(--wb-ink-muted); font-size: 11px; text-align: center; }</style>
|
|
6210
|
+
</head>
|
|
6211
|
+
<body>
|
|
6212
|
+
<div id="app">
|
|
6213
|
+
<div id="toolbar">
|
|
6214
|
+
<span id="board-label">Board:</span>
|
|
6215
|
+
<span id="board-id">\u2014</span>
|
|
6216
|
+
<input id="board-input" type="text" placeholder="tree:<sessionId> or ws:<slug>">
|
|
6217
|
+
<button class="tbtn" id="go-btn">Go</button>
|
|
6218
|
+
<button class="tbtn" id="refresh-btn" title="Refresh">↻</button>
|
|
6219
|
+
<input id="new-title" type="text" placeholder="New task title…">
|
|
6220
|
+
<button class="tbtn" id="add-btn">+ Add</button>
|
|
6221
|
+
</div>
|
|
6222
|
+
<div id="columns"></div>
|
|
6223
|
+
<div id="statusbar">Connecting to bridge…</div>
|
|
6224
|
+
</div>
|
|
6225
|
+
</body>
|
|
6226
|
+
</html>
|
|
6227
|
+
`;
|
|
6228
|
+
|
|
6229
|
+
// src/work-board/panel.ts
|
|
6230
|
+
var WORK_BOARD_APP_ID = "@agentproto/work-board";
|
|
6231
|
+
var WORK_BOARD_TOOL_ID = "agentproto_work_board";
|
|
6232
|
+
var WORK_BOARD_UI_TOOLS = [
|
|
6233
|
+
"task_list",
|
|
6234
|
+
"task_claim",
|
|
6235
|
+
"task_update",
|
|
6236
|
+
"task_create"
|
|
6237
|
+
];
|
|
6238
|
+
var WORK_BOARD_HTML = WORK_BOARD_HTML_GENERATED;
|
|
6239
|
+
|
|
6240
|
+
// src/work-board/index.ts
|
|
6241
|
+
var workBoardInputSchema = z.object({
|
|
6242
|
+
boardId: z.string().optional().describe(
|
|
6243
|
+
"Board to open \u2014 `tree:<rootSessionId>` (a supervisor and its executors) or `ws:<slug>` (an operator workspace). Omit \u2192 the caller's default board."
|
|
6244
|
+
)
|
|
6245
|
+
});
|
|
6246
|
+
function makeWorkBoardApp(ops) {
|
|
6247
|
+
return {
|
|
6248
|
+
id: WORK_BOARD_TOOL_ID,
|
|
6249
|
+
title: "Work Board",
|
|
6250
|
+
description: "Open the agentproto work board \u2014 a kanban over the daemon's Task ledger (columns: pending/in_progress/done/failed, cancelled folded into failed). Swimlane is the ledger's own board id (`tree:<root>` or `ws:<slug>`). Each card shows its owner (or \"Unclaimed\") and the verification tell \u2014 a gate-passed done reads visually distinct from a self-reported one. Pass `boardId` to open a specific board.",
|
|
6251
|
+
inputSchema: workBoardInputSchema,
|
|
6252
|
+
execute: async (input) => ops.listTasks(input.boardId),
|
|
6253
|
+
html: WORK_BOARD_HTML
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
var workBoardApp = defineApp({
|
|
6257
|
+
id: WORK_BOARD_APP_ID,
|
|
6258
|
+
name: "Work Board",
|
|
6259
|
+
description: "Open the agentproto work board \u2014 a kanban over the daemon's Task ledger, scoped by board id (`tree:<rootSessionId>` or `ws:<slug>`), with an explicit status action per card.",
|
|
6260
|
+
agents: [],
|
|
6261
|
+
ui: {
|
|
6262
|
+
html: WORK_BOARD_HTML,
|
|
6263
|
+
title: "Work Board",
|
|
6264
|
+
tools: [...WORK_BOARD_UI_TOOLS]
|
|
6265
|
+
}
|
|
6266
|
+
});
|
|
6267
|
+
|
|
6268
|
+
export { SESSION_CHAT_APP_ID, SESSION_CHAT_FALLBACK_HTML, agentsOverviewApp, bureauSessionsApp, codeTeam, contentTeam, liveSessionApp, mailTriage, makeAgentsOverviewApp, makeBureauSessionsApp, makeLiveSessionApp, makeSessionChatApp, makeSessionStoryPanelApp, makeSessionsPanelApp, makeWorkBoardApp, mediaViewer, opsPanel, panelBridgeScript, sessionChatApp, sessionChatAppUrl, sessionChatEmbedHtml, sessionStoryApp, sessionViewer, sessionsPanelApp, workBoardApp };
|
|
5494
6269
|
//# sourceMappingURL=index.mjs.map
|
|
5495
6270
|
//# sourceMappingURL=index.mjs.map
|