@batalabs/virlow-mcp-core 3.84.2 → 3.85.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/dist/cjs/memories.d.ts +3 -0
- package/dist/cjs/memories.js +5 -0
- package/dist/cjs/memory-secrets.d.ts +7 -2
- package/dist/cjs/memory-secrets.js +52 -4
- package/dist/cjs/tool-annotations.d.ts +82 -0
- package/dist/cjs/tool-annotations.js +160 -0
- package/dist/cjs/widgets.d.ts +119 -0
- package/dist/cjs/widgets.js +425 -0
- package/dist/memories.d.ts +3 -0
- package/dist/memories.d.ts.map +1 -1
- package/dist/memories.js +5 -0
- package/dist/memories.js.map +1 -1
- package/dist/memory-secrets.d.ts +7 -2
- package/dist/memory-secrets.d.ts.map +1 -1
- package/dist/memory-secrets.js +52 -4
- package/dist/memory-secrets.js.map +1 -1
- package/dist/tool-annotations.d.ts +83 -0
- package/dist/tool-annotations.d.ts.map +1 -0
- package/dist/tool-annotations.js +156 -0
- package/dist/tool-annotations.js.map +1 -0
- package/dist/tools.d.ts +8 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +186 -63
- package/dist/tools.js.map +1 -1
- package/dist/widgets.d.ts +120 -0
- package/dist/widgets.d.ts.map +1 -0
- package/dist/widgets.js +420 -0
- package/dist/widgets.js.map +1 -0
- package/package.json +3 -3
package/dist/widgets.js
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/** The MCP Apps resource type. ChatGPT reads it too. */
|
|
2
|
+
export const WIDGET_MIME_TYPE = 'text/html;profile=mcp-app';
|
|
3
|
+
export const SAVED_MEMORY_URI = 'ui://widget/saved-memory.html';
|
|
4
|
+
export const MEMORY_CONTEXT_URI = 'ui://widget/memory-context.html';
|
|
5
|
+
/** Where "Open in Virlow" goes. The web app has no per-memory route, so it
|
|
6
|
+
* opens the app itself. */
|
|
7
|
+
export const VIRLOW_APP_URL = 'https://app.virlow.com';
|
|
8
|
+
/**
|
|
9
|
+
* The thirteen kind colours, light and dark. A copy of KIND_COLORS in the
|
|
10
|
+
* web app (src/lib/entityKinds.ts), which cannot be imported here: that
|
|
11
|
+
* module pulls in lucide-react. A test holds the two to the same values.
|
|
12
|
+
*/
|
|
13
|
+
export const KIND_COLORS = {
|
|
14
|
+
person: { light: '#4F6D8F', dark: '#8FB0D0' },
|
|
15
|
+
organization: { light: '#C2603E', dark: '#E08A67' },
|
|
16
|
+
project: { light: '#A27B17', dark: '#F2C66D' },
|
|
17
|
+
tool: { light: '#5E7B4A', dark: '#9DBB85' },
|
|
18
|
+
place: { light: '#7A5A8C', dark: '#B79BC8' },
|
|
19
|
+
event: { light: '#CB2A61', dark: '#F6638A' },
|
|
20
|
+
topic: { light: '#0C9184', dark: '#82DCCF' },
|
|
21
|
+
product: { light: '#9E2185', dark: '#EA78CD' },
|
|
22
|
+
media: { light: '#6E4A2E', dark: '#C8A07E' },
|
|
23
|
+
pet: { light: '#C16B10', dark: '#F59C4D' },
|
|
24
|
+
group: { light: '#10953A', dark: '#6AD177' },
|
|
25
|
+
goal: { light: '#5D47B2', dark: '#AC8FFA' },
|
|
26
|
+
other: { light: '#6B665C', dark: '#9A9488' },
|
|
27
|
+
};
|
|
28
|
+
/** How many facts the context card shows; the text result keeps them all. */
|
|
29
|
+
export const CONTEXT_CARD_FACTS = 5;
|
|
30
|
+
const VIEW_URI = {
|
|
31
|
+
saved: SAVED_MEMORY_URI,
|
|
32
|
+
context: MEMORY_CONTEXT_URI,
|
|
33
|
+
};
|
|
34
|
+
/** Which tools show which card. Tools not named here have no UI. */
|
|
35
|
+
export const TOOL_WIDGETS = {
|
|
36
|
+
add_memory: 'saved',
|
|
37
|
+
update_memory: 'saved',
|
|
38
|
+
memory_context: 'context',
|
|
39
|
+
};
|
|
40
|
+
/** The tool-side `_meta` linking a tool to its card: the MCP Apps key and
|
|
41
|
+
* ChatGPT's alias for it. add_memory's card calls delete_memory (Undo), so
|
|
42
|
+
* it is marked as allowed to call tools. */
|
|
43
|
+
export function widgetToolMeta(tool) {
|
|
44
|
+
const view = TOOL_WIDGETS[tool];
|
|
45
|
+
if (!view)
|
|
46
|
+
return undefined;
|
|
47
|
+
const uri = VIEW_URI[view];
|
|
48
|
+
return {
|
|
49
|
+
ui: { resourceUri: uri },
|
|
50
|
+
'openai/outputTemplate': uri,
|
|
51
|
+
...(tool === 'add_memory' && { 'openai/widgetAccessible': true }),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The resource-side `_meta`. Empty CSP lists are the secure default (no
|
|
56
|
+
* network); they are stated rather than omitted so a reviewer reads the
|
|
57
|
+
* decision. `openai/widgetCSP` is ChatGPT's older spelling of the same
|
|
58
|
+
* thing; redirect_domains lets "Open in Virlow" go straight to the app.
|
|
59
|
+
*/
|
|
60
|
+
function resourceMeta(view) {
|
|
61
|
+
return {
|
|
62
|
+
ui: {
|
|
63
|
+
prefersBorder: true,
|
|
64
|
+
csp: { connectDomains: [], resourceDomains: [], frameDomains: [] },
|
|
65
|
+
},
|
|
66
|
+
'openai/widgetDescription': view === 'saved'
|
|
67
|
+
? 'Shows the memory just saved to Virlow, with Undo and a link to the app. The card already shows the fact, so a one-line confirmation is enough.'
|
|
68
|
+
: 'Shows a short summary of what Virlow has saved about this user or project.',
|
|
69
|
+
'openai/widgetCSP': {
|
|
70
|
+
connect_domains: [],
|
|
71
|
+
resource_domains: [],
|
|
72
|
+
redirect_domains: [VIRLOW_APP_URL],
|
|
73
|
+
},
|
|
74
|
+
'openai/widgetPrefersBorder': true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Everything the server needs to list and serve the two resources. */
|
|
78
|
+
export const WIDGET_RESOURCES = [
|
|
79
|
+
{
|
|
80
|
+
name: 'saved-memory-card',
|
|
81
|
+
uri: SAVED_MEMORY_URI,
|
|
82
|
+
title: 'Saved to Virlow',
|
|
83
|
+
description: 'Inline card shown after a memory is saved or updated.',
|
|
84
|
+
view: 'saved',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'memory-context-card',
|
|
88
|
+
uri: MEMORY_CONTEXT_URI,
|
|
89
|
+
title: 'What Virlow knows',
|
|
90
|
+
description: 'Inline card summarising the memories returned by memory_context.',
|
|
91
|
+
view: 'context',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
/** A `resources/read` result for one card. */
|
|
95
|
+
export function readWidget(view) {
|
|
96
|
+
const uri = VIEW_URI[view];
|
|
97
|
+
return {
|
|
98
|
+
contents: [{ uri, mimeType: WIDGET_MIME_TYPE, text: widgetHtml(view), _meta: resourceMeta(view) }],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// ---------- the page ----------
|
|
102
|
+
/** The Virlow mark from public/favicon.svg: the italic v and its dot. */
|
|
103
|
+
const MARK_SVG = '<svg class="mark" viewBox="0 0 64 64" width="20" height="20" aria-hidden="true" focusable="false">' +
|
|
104
|
+
'<rect width="64" height="64" rx="12" fill="#fbfaf7"/>' +
|
|
105
|
+
'<g transform="translate(6.172854 51.207268) scale(2.201335896 -2.201335896)">' +
|
|
106
|
+
'<path d="M7.8 -0.4Q6.8 -0.4 6.4 -0.2Q6.1 0.1 6.1 0.4Q6.1 0.6 6.1 0.8Q6.1 0.9 6.1 1.1Q6.1 1.3 6.1 1.7Q6 3.9 5.7 6.3Q5.3 8.7 4.6 10.8Q3.9 12.9 3.1 14.3Q2.9 14.5 2.7 14.7Q2.5 14.8 2.3 14.8Q2 14.8 1.7 14.7Q1.4 14.5 1 14.2Q0.8 13.9 0.5 13.9Q0.4 13.9 0.1 14.1Q-0.1 14.3 -0.1 14.7Q-0.1 14.9 0 15.1Q1 16.5 2.3 17.2Q3.6 17.8 5.1 17.8Q7.1 17.8 8.2 15.9Q9 14.5 9.5 12.6Q9.9 10.8 10.1 8.7Q10.3 6.6 10.4 4.5Q10.4 4.1 10 3.1Q9.7 2.2 9.3 1.7Q10.3 2.3 11.4 3.3Q12.5 4.3 13.4 5.6Q14.4 6.9 15 8.3Q15.6 9.7 15.6 11.1Q15.6 12 15.4 12.6Q15.2 13.3 14.9 14Q14.7 14.7 14.7 15.5Q14.7 16.6 15.4 17.2Q16.1 17.8 17.2 17.8Q18.2 17.8 18.9 17.2Q19.5 16.6 19.8 15.8Q20.1 14.9 20.1 13.9Q20.1 12.4 19.4 10.7Q18.7 9 17.5 7.3Q16.2 5.7 14.8 4.2Q13.3 2.7 11.8 1.5Q10.3 0.4 9 -0.3Q9 -0.4 8.8 -0.4Q8.6 -0.4 7.8 -0.4Z" fill="#1f1d1a"/>' +
|
|
107
|
+
'<path d="M17.7 2.6Q17.7 3.9 18.5 4.9Q19.4 5.8 20.8 5.8Q22.1 5.8 22.8 5Q23.5 4.2 23.5 3Q23.5 1.6 22.6 0.6Q21.7 -0.4 20.4 -0.4Q19 -0.4 18.3 0.5Q17.7 1.4 17.7 2.6Z" fill="#775825"/>' +
|
|
108
|
+
'</g></svg>';
|
|
109
|
+
function kindVars(mode) {
|
|
110
|
+
return Object.entries(KIND_COLORS)
|
|
111
|
+
.map(([kind, c]) => `--k-${kind}:${c[mode]};`)
|
|
112
|
+
.join('');
|
|
113
|
+
}
|
|
114
|
+
const LIGHT = 'color-scheme:light;--ink:#1F1D1A;--ink-2:#5E5A52;--ink-3:#6B665C;--rule:rgba(0,0,0,0.12);' +
|
|
115
|
+
'--chip:rgba(0,0,0,0.05);--btn:#4F6D8F;--btn-ink:#FFFFFF;--btn-hover:#33506E;--ghost-hover:rgba(0,0,0,0.05);' +
|
|
116
|
+
'--ring:#33506E;--danger:#B3402E;--ok:#4C783C;' +
|
|
117
|
+
kindVars('light');
|
|
118
|
+
const DARK = 'color-scheme:dark;--ink:#EDE7DD;--ink-2:#B8B0A4;--ink-3:#9A9488;--rule:rgba(255,255,255,0.16);' +
|
|
119
|
+
'--chip:rgba(255,255,255,0.07);--btn:#8FB0D0;--btn-ink:#14181D;--btn-hover:#B8D1E8;--ghost-hover:rgba(255,255,255,0.08);' +
|
|
120
|
+
'--ring:#B8D1E8;--danger:#E07A68;--ok:#9DBB85;' +
|
|
121
|
+
kindVars('dark');
|
|
122
|
+
/** Kept to body and body-small sizes, system fonts, no inner scrolling: the
|
|
123
|
+
* Apps SDK rules for an inline card. Long text is clamped, not scrolled. */
|
|
124
|
+
const CSS = `
|
|
125
|
+
:root{${LIGHT}}
|
|
126
|
+
:root[data-theme="dark"]{${DARK}}
|
|
127
|
+
@media (prefers-color-scheme: dark){:root:not([data-theme="light"]){${DARK}}}
|
|
128
|
+
*{box-sizing:border-box}
|
|
129
|
+
html,body{margin:0;padding:0;background:transparent;overflow:hidden}
|
|
130
|
+
body{font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;color:var(--ink);-webkit-font-smoothing:antialiased}
|
|
131
|
+
.card{padding:14px 16px;display:flex;flex-direction:column;gap:8px}
|
|
132
|
+
.head{display:flex;align-items:center;gap:8px;min-width:0}
|
|
133
|
+
.mark{flex:none;border-radius:5px;box-shadow:0 0 0 1px var(--rule)}
|
|
134
|
+
.status{font-size:13px;font-weight:600;color:var(--ink-2);outline:none}
|
|
135
|
+
.status.removed{color:var(--ink-3)}
|
|
136
|
+
.ns{margin-left:auto;font-size:12px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:45%}
|
|
137
|
+
.label{font-weight:600;margin:0;overflow-wrap:anywhere}
|
|
138
|
+
.fact{margin:0;color:var(--ink-2);overflow-wrap:anywhere;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}
|
|
139
|
+
.removed-body .label,.removed-body .fact{text-decoration:line-through;color:var(--ink-3)}
|
|
140
|
+
.chips{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:0;list-style:none}
|
|
141
|
+
.chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:2px 9px 2px 7px;border-radius:999px;background:var(--chip);font-size:12px;color:var(--ink)}
|
|
142
|
+
.chip span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
143
|
+
.dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--k-other)}
|
|
144
|
+
${Object.keys(KIND_COLORS)
|
|
145
|
+
.map((k) => `.chip[data-kind="${k}"] .dot{background:var(--k-${k})}`)
|
|
146
|
+
.join('\n')}
|
|
147
|
+
.facts{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
|
|
148
|
+
.facts li{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere;color:var(--ink-2)}
|
|
149
|
+
.facts b{color:var(--ink);font-weight:600}
|
|
150
|
+
.note{margin:0;font-size:12px;color:var(--ink-3)}
|
|
151
|
+
.error{margin:0;font-size:12px;color:var(--danger)}
|
|
152
|
+
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:2px}
|
|
153
|
+
button{font:inherit;font-size:13px;font-weight:600;border-radius:8px;padding:6px 12px;cursor:pointer;border:1px solid transparent}
|
|
154
|
+
button:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
|
|
155
|
+
button[disabled]{opacity:.6;cursor:default}
|
|
156
|
+
.primary{background:var(--btn);color:var(--btn-ink)}
|
|
157
|
+
.primary:hover:not([disabled]){background:var(--btn-hover)}
|
|
158
|
+
.secondary{background:transparent;color:var(--ink);border-color:var(--rule)}
|
|
159
|
+
.secondary:hover:not([disabled]){background:var(--ghost-hover)}
|
|
160
|
+
.empty{margin:0;color:var(--ink-3)}
|
|
161
|
+
`;
|
|
162
|
+
/**
|
|
163
|
+
* The card's script. Plain ES2017 in a string, because it ships inside the
|
|
164
|
+
* HTML rather than through the package build. It speaks to whichever host it
|
|
165
|
+
* finds:
|
|
166
|
+
*
|
|
167
|
+
* - ChatGPT injects `window.openai`: toolOutput (the structuredContent),
|
|
168
|
+
* widgetState / setWidgetState, callTool, openExternal, theme, and an
|
|
169
|
+
* `openai:set_globals` event when any of them change.
|
|
170
|
+
* - Other MCP Apps hosts speak JSON-RPC over postMessage: the card sends
|
|
171
|
+
* ui/initialize, receives ui/notifications/tool-result, and calls
|
|
172
|
+
* tools/call and ui/open-link.
|
|
173
|
+
*/
|
|
174
|
+
const SCRIPT = `
|
|
175
|
+
(function () {
|
|
176
|
+
'use strict';
|
|
177
|
+
var APP_URL = ${JSON.stringify(VIRLOW_APP_URL)};
|
|
178
|
+
var KIND_LABEL = {person:'person',organization:'organization',project:'project',tool:'tool',place:'place',event:'event',topic:'topic',product:'product',media:'media',pet:'pet',group:'group',goal:'goal',other:'other'};
|
|
179
|
+
var root = document.getElementById('root');
|
|
180
|
+
var view = document.body.getAttribute('data-view') === 'context' ? 'context' : 'saved';
|
|
181
|
+
var state = { data: null, removed: false, busy: false, error: '' };
|
|
182
|
+
var bridge = null;
|
|
183
|
+
|
|
184
|
+
function openai() { return window.openai || null; }
|
|
185
|
+
|
|
186
|
+
function el(tag, cls, text) {
|
|
187
|
+
var node = document.createElement(tag);
|
|
188
|
+
if (cls) node.className = cls;
|
|
189
|
+
if (text != null) node.textContent = String(text);
|
|
190
|
+
return node;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function applyTheme(theme) {
|
|
194
|
+
if (theme === 'light' || theme === 'dark') document.documentElement.setAttribute('data-theme', theme);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function header(status, ns, removed) {
|
|
198
|
+
var head = el('div', 'head');
|
|
199
|
+
var mark = document.createElement('span');
|
|
200
|
+
mark.innerHTML = ${JSON.stringify(MARK_SVG)};
|
|
201
|
+
head.appendChild(mark.firstChild);
|
|
202
|
+
var s = el('span', 'status' + (removed ? ' removed' : ''), status);
|
|
203
|
+
s.id = 'status';
|
|
204
|
+
s.setAttribute('tabindex', '-1');
|
|
205
|
+
s.setAttribute('role', 'status');
|
|
206
|
+
s.setAttribute('aria-live', 'polite');
|
|
207
|
+
head.appendChild(s);
|
|
208
|
+
if (ns) {
|
|
209
|
+
var n = el('span', 'ns', ns);
|
|
210
|
+
n.title = 'Project: ' + ns;
|
|
211
|
+
head.appendChild(n);
|
|
212
|
+
}
|
|
213
|
+
return head;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function chips(entities, max) {
|
|
217
|
+
var list = el('ul', 'chips');
|
|
218
|
+
list.setAttribute('aria-label', 'About');
|
|
219
|
+
var shown = entities.slice(0, max);
|
|
220
|
+
for (var i = 0; i < shown.length; i++) {
|
|
221
|
+
var e = shown[i];
|
|
222
|
+
var kind = KIND_LABEL[e.kind] ? e.kind : 'other';
|
|
223
|
+
var li = el('li', 'chip');
|
|
224
|
+
li.setAttribute('data-kind', kind);
|
|
225
|
+
li.title = e.name + ' (' + kind + ')';
|
|
226
|
+
li.appendChild(el('span', 'dot')).setAttribute('aria-hidden', 'true');
|
|
227
|
+
var name = el('span', null, e.name);
|
|
228
|
+
li.appendChild(name);
|
|
229
|
+
var sr = el('span', 'sr', ', ' + kind);
|
|
230
|
+
sr.style.cssText = 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)';
|
|
231
|
+
li.appendChild(sr);
|
|
232
|
+
list.appendChild(li);
|
|
233
|
+
}
|
|
234
|
+
if (entities.length > max) list.appendChild(el('li', 'chip', '+' + (entities.length - max)));
|
|
235
|
+
return list;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function button(label, cls, onClick, aria) {
|
|
239
|
+
var b = el('button', cls, label);
|
|
240
|
+
b.type = 'button';
|
|
241
|
+
if (aria) b.setAttribute('aria-label', aria);
|
|
242
|
+
b.addEventListener('click', onClick);
|
|
243
|
+
return b;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function openApp() {
|
|
247
|
+
var host = openai();
|
|
248
|
+
if (host && typeof host.openExternal === 'function') { host.openExternal({ href: APP_URL }); return; }
|
|
249
|
+
if (bridge) { bridge.request('ui/open-link', { url: APP_URL }).catch(function () {}); return; }
|
|
250
|
+
window.open(APP_URL, '_blank', 'noopener');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function callTool(name, args) {
|
|
254
|
+
var host = openai();
|
|
255
|
+
if (host && typeof host.callTool === 'function') return Promise.resolve(host.callTool(name, args));
|
|
256
|
+
if (bridge) return bridge.request('tools/call', { name: name, arguments: args });
|
|
257
|
+
return Promise.reject(new Error('This host cannot run tools from a card.'));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function persist() {
|
|
261
|
+
var host = openai();
|
|
262
|
+
if (host && typeof host.setWidgetState === 'function') {
|
|
263
|
+
try { host.setWidgetState({ removed: state.removed }); } catch (e) { /* the card still shows it */ }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function undo() {
|
|
268
|
+
var d = state.data;
|
|
269
|
+
if (!d || !d.id || state.busy || state.removed) return;
|
|
270
|
+
state.busy = true;
|
|
271
|
+
state.error = '';
|
|
272
|
+
render();
|
|
273
|
+
callTool('delete_memory', { id: d.id })
|
|
274
|
+
.then(function (result) {
|
|
275
|
+
if (result && result.isError) throw new Error('refused');
|
|
276
|
+
state.removed = true;
|
|
277
|
+
persist();
|
|
278
|
+
})
|
|
279
|
+
.catch(function () { state.error = 'Could not remove it. Try again.'; })
|
|
280
|
+
.then(function () {
|
|
281
|
+
state.busy = false;
|
|
282
|
+
render();
|
|
283
|
+
var s = document.getElementById('status');
|
|
284
|
+
if (state.removed && s) s.focus();
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function renderSaved(d) {
|
|
289
|
+
var card = el('div', 'card' + (state.removed ? ' removed-body' : ''));
|
|
290
|
+
var status = state.removed ? 'Removed from Virlow' : d.updated ? 'Updated in Virlow' : 'Saved to Virlow';
|
|
291
|
+
card.appendChild(header(status, d.namespace, state.removed));
|
|
292
|
+
card.appendChild(el('p', 'label', d.label));
|
|
293
|
+
card.appendChild(el('p', 'fact', d.text));
|
|
294
|
+
if (d.entities && d.entities.length) card.appendChild(chips(d.entities, 8));
|
|
295
|
+
if (state.removed) card.appendChild(el('p', 'note', 'Moved to the Trash. You can restore it in the Virlow app.'));
|
|
296
|
+
if (state.error) card.appendChild(el('p', 'error', state.error));
|
|
297
|
+
var actions = el('div', 'actions');
|
|
298
|
+
// Undo deletes the memory, so it is offered only for a new one: on an
|
|
299
|
+
// update it would delete what was there before, not reverse the change.
|
|
300
|
+
if (!d.updated && !state.removed) {
|
|
301
|
+
var u = button(state.busy ? 'Removing...' : 'Undo', 'primary', undo, 'Undo: remove this memory from Virlow');
|
|
302
|
+
if (state.busy) u.disabled = true;
|
|
303
|
+
actions.appendChild(u);
|
|
304
|
+
}
|
|
305
|
+
actions.appendChild(button('Open in Virlow', 'secondary', openApp, 'Open Virlow in a new tab'));
|
|
306
|
+
card.appendChild(actions);
|
|
307
|
+
return card;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function renderContext(d) {
|
|
311
|
+
var card = el('div', 'card');
|
|
312
|
+
card.appendChild(header('What Virlow knows', d.namespace, false));
|
|
313
|
+
var facts = d.facts || [];
|
|
314
|
+
if (facts.length) {
|
|
315
|
+
var list = el('ul', 'facts');
|
|
316
|
+
for (var i = 0; i < facts.length; i++) {
|
|
317
|
+
var li = el('li');
|
|
318
|
+
li.appendChild(el('b', null, facts[i].label));
|
|
319
|
+
li.appendChild(document.createTextNode(': ' + facts[i].text));
|
|
320
|
+
list.appendChild(li);
|
|
321
|
+
}
|
|
322
|
+
card.appendChild(list);
|
|
323
|
+
} else {
|
|
324
|
+
card.appendChild(el('p', 'empty', 'Nothing saved yet.'));
|
|
325
|
+
}
|
|
326
|
+
var people = (d.entities || []).filter(function (e) { return e.kind === 'person' || e.kind === 'project'; });
|
|
327
|
+
if (people.length) card.appendChild(chips(people, 6));
|
|
328
|
+
if (d.paused) card.appendChild(el('p', 'note', 'Saving new memories is paused.'));
|
|
329
|
+
var actions = el('div', 'actions');
|
|
330
|
+
actions.appendChild(button('Open in Virlow', 'secondary', openApp, 'Open Virlow in a new tab'));
|
|
331
|
+
card.appendChild(actions);
|
|
332
|
+
return card;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function render() {
|
|
336
|
+
root.textContent = '';
|
|
337
|
+
var d = state.data;
|
|
338
|
+
if (!d || typeof d !== 'object') return;
|
|
339
|
+
root.appendChild(view === 'context' ? renderContext(d) : renderSaved(d));
|
|
340
|
+
if (bridge) bridge.resized();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function fromOpenai() {
|
|
344
|
+
var host = openai();
|
|
345
|
+
if (!host) return;
|
|
346
|
+
applyTheme(host.theme);
|
|
347
|
+
if (host.toolOutput && typeof host.toolOutput === 'object') state.data = host.toolOutput;
|
|
348
|
+
var ws = host.widgetState;
|
|
349
|
+
if (ws && ws.removed === true) state.removed = true;
|
|
350
|
+
render();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function startBridge() {
|
|
354
|
+
var nextId = 1;
|
|
355
|
+
var pending = {};
|
|
356
|
+
var lastHeight = 0;
|
|
357
|
+
function post(msg) { window.parent.postMessage(msg, '*'); }
|
|
358
|
+
bridge = {
|
|
359
|
+
request: function (method, params) {
|
|
360
|
+
return new Promise(function (resolve, reject) {
|
|
361
|
+
var id = nextId++;
|
|
362
|
+
pending[id] = { resolve: resolve, reject: reject };
|
|
363
|
+
post({ jsonrpc: '2.0', id: id, method: method, params: params });
|
|
364
|
+
});
|
|
365
|
+
},
|
|
366
|
+
notify: function (method, params) { post({ jsonrpc: '2.0', method: method, params: params || {} }); },
|
|
367
|
+
resized: function () {
|
|
368
|
+
var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
|
|
369
|
+
if (h && h !== lastHeight) { lastHeight = h; bridge.notify('ui/notifications/size-changed', { height: h }); }
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
window.addEventListener('message', function (event) {
|
|
373
|
+
if (event.source !== window.parent) return;
|
|
374
|
+
var m = event.data;
|
|
375
|
+
if (!m || m.jsonrpc !== '2.0') return;
|
|
376
|
+
if (m.method === undefined && m.id != null && pending[m.id]) {
|
|
377
|
+
var p = pending[m.id];
|
|
378
|
+
delete pending[m.id];
|
|
379
|
+
if (m.error) p.reject(new Error(m.error.message || 'error')); else p.resolve(m.result);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
var params = m.params || {};
|
|
383
|
+
if (m.method === 'ui/notifications/tool-result') {
|
|
384
|
+
if (params.structuredContent) { state.data = params.structuredContent; render(); }
|
|
385
|
+
} else if (m.method === 'ui/notifications/host-context-changed') {
|
|
386
|
+
applyTheme(params.theme);
|
|
387
|
+
} else if (m.method === 'ui/resource-teardown' && m.id != null) {
|
|
388
|
+
post({ jsonrpc: '2.0', id: m.id, result: {} });
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
bridge.request('ui/initialize', {
|
|
392
|
+
appInfo: { name: 'virlow-card', version: '1' },
|
|
393
|
+
appCapabilities: {},
|
|
394
|
+
protocolVersion: '2026-01-26'
|
|
395
|
+
}).then(function (result) {
|
|
396
|
+
if (result && result.hostContext) applyTheme(result.hostContext.theme);
|
|
397
|
+
bridge.notify('ui/notifications/initialized');
|
|
398
|
+
}).catch(function () {});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (openai()) {
|
|
402
|
+
window.addEventListener('openai:set_globals', fromOpenai);
|
|
403
|
+
fromOpenai();
|
|
404
|
+
} else if (window.parent && window.parent !== window) {
|
|
405
|
+
startBridge();
|
|
406
|
+
}
|
|
407
|
+
})();
|
|
408
|
+
`;
|
|
409
|
+
/** The whole page for one card. */
|
|
410
|
+
export function widgetHtml(view) {
|
|
411
|
+
return ('<!doctype html><html lang="en"><head><meta charset="utf-8">' +
|
|
412
|
+
'<meta name="viewport" content="width=device-width,initial-scale=1">' +
|
|
413
|
+
`<title>${view === 'saved' ? 'Saved to Virlow' : 'What Virlow knows'}</title>` +
|
|
414
|
+
`<style>${CSS}</style></head>` +
|
|
415
|
+
`<body data-view="${view}"><div id="root"></div><script>${SCRIPT}</script></body></html>`);
|
|
416
|
+
}
|
|
417
|
+
/** The pieces, for the widget tests: they run the script against the page
|
|
418
|
+
* body in jsdom without a host. */
|
|
419
|
+
export const WIDGET_PARTS = { css: CSS, script: SCRIPT };
|
|
420
|
+
//# sourceMappingURL=widgets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@batalabs/virlow-mcp-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.85.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Virlow MCP connector core: encrypted note and memory access, folder exposure, and the memories opt-in. Shared by the local virlow-mcp CLI and the hosted connector.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
61
61
|
"yaml": ">=2.8.3 <3",
|
|
62
62
|
"zod": "^3.24.0",
|
|
63
|
-
"@batalabs/virlow-crypto": "3.
|
|
64
|
-
"@batalabs/virlow-memory": "3.
|
|
63
|
+
"@batalabs/virlow-crypto": "3.85.0",
|
|
64
|
+
"@batalabs/virlow-memory": "3.85.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/node": "^22.10.0",
|