@dipertq/dsh-openviking-status 0.1.5
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 +112 -0
- package/cordis.patch.yml +4 -0
- package/lib/client.cjs +1456 -0
- package/lib/client.cjs.map +1 -0
- package/lib/client.d.cts +277 -0
- package/lib/client.js +1431 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/package.json +75 -0
package/lib/client.cjs
ADDED
|
@@ -0,0 +1,1456 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@dipertq/dsh-openviking-status",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
"use strict";
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name2 in all)
|
|
13
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
|
+
|
|
25
|
+
// src/client/index.tsx
|
|
26
|
+
var client_exports = {};
|
|
27
|
+
__export(client_exports, {
|
|
28
|
+
COMMIT_THRESHOLD: () => COMMIT_THRESHOLD,
|
|
29
|
+
DEFAULT_OPENVIKING_ENDPOINT: () => DEFAULT_OPENVIKING_ENDPOINT,
|
|
30
|
+
OpenVikingClient: () => OpenVikingClient,
|
|
31
|
+
OpenVikingStatusChip: () => OpenVikingStatusChip,
|
|
32
|
+
OpenVikingStatusPopover: () => OpenVikingStatusPopover,
|
|
33
|
+
apply: () => apply,
|
|
34
|
+
checkHealth: () => checkHealth,
|
|
35
|
+
commitSession: () => commitSession,
|
|
36
|
+
defaultOpenVikingClient: () => defaultOpenVikingClient,
|
|
37
|
+
fetchSession: () => fetchSession,
|
|
38
|
+
formatEndpoint: () => formatEndpoint,
|
|
39
|
+
formatMemoryLeafName: () => formatMemoryLeafName,
|
|
40
|
+
formatPendingTokens: () => formatPendingTokens,
|
|
41
|
+
formatRelativeTime: () => formatRelativeTime,
|
|
42
|
+
formatStatusLabel: () => formatStatusLabel,
|
|
43
|
+
formatTooltipTitle: () => formatTooltipTitle,
|
|
44
|
+
getCategoryBadgeStyle: () => getCategoryBadgeStyle,
|
|
45
|
+
getFallbackSessionMessages: () => getFallbackSessionMessages,
|
|
46
|
+
getProgressBarColor: () => getProgressBarColor,
|
|
47
|
+
getProgressBarPercent: () => getProgressBarPercent,
|
|
48
|
+
getSession: () => getSession,
|
|
49
|
+
getStatusGlow: () => getStatusGlow,
|
|
50
|
+
getStatusIndicatorColor: () => getStatusIndicatorColor,
|
|
51
|
+
handleEscapeKey: () => handleEscapeKey,
|
|
52
|
+
inferCategory: () => inferCategory,
|
|
53
|
+
inject: () => inject,
|
|
54
|
+
name: () => name,
|
|
55
|
+
parseRecalledMemories: () => parseRecalledMemories,
|
|
56
|
+
resolveApiKey: () => resolveApiKey,
|
|
57
|
+
resolveEndpoint: () => resolveEndpoint,
|
|
58
|
+
truncateSessionId: () => truncateSessionId
|
|
59
|
+
});
|
|
60
|
+
module.exports = __toCommonJS(client_exports);
|
|
61
|
+
|
|
62
|
+
// src/client/OpenVikingStatusChip.tsx
|
|
63
|
+
var import_react2 = require("react");
|
|
64
|
+
|
|
65
|
+
// src/client/api.ts
|
|
66
|
+
var DEFAULT_OPENVIKING_ENDPOINT = "http://127.0.0.1:1933";
|
|
67
|
+
function resolveEndpoint(endpoint) {
|
|
68
|
+
if (endpoint && endpoint.trim().length > 0) {
|
|
69
|
+
return endpoint.trim().replace(/\/+$/, "");
|
|
70
|
+
}
|
|
71
|
+
if (typeof window !== "undefined") {
|
|
72
|
+
const win = window;
|
|
73
|
+
if (typeof win.__OPENVIKING_ENDPOINT__ === "string" && win.__OPENVIKING_ENDPOINT__.trim()) {
|
|
74
|
+
return win.__OPENVIKING_ENDPOINT__.trim().replace(/\/+$/, "");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (typeof localStorage !== "undefined") {
|
|
78
|
+
try {
|
|
79
|
+
const stored = localStorage.getItem("openviking_endpoint") || localStorage.getItem("OPENVIKING_ENDPOINT");
|
|
80
|
+
if (stored && stored.trim()) {
|
|
81
|
+
return stored.trim().replace(/\/+$/, "");
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return DEFAULT_OPENVIKING_ENDPOINT;
|
|
87
|
+
}
|
|
88
|
+
function resolveApiKey(apiKey) {
|
|
89
|
+
if (apiKey && apiKey.trim().length > 0) {
|
|
90
|
+
return apiKey.trim();
|
|
91
|
+
}
|
|
92
|
+
if (typeof window !== "undefined") {
|
|
93
|
+
const win = window;
|
|
94
|
+
if (typeof win.__OPENVIKING_API_KEY__ === "string" && win.__OPENVIKING_API_KEY__.trim()) {
|
|
95
|
+
return win.__OPENVIKING_API_KEY__.trim();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (typeof localStorage !== "undefined") {
|
|
99
|
+
try {
|
|
100
|
+
const stored = localStorage.getItem("openviking_api_key") || localStorage.getItem("OPENVIKING_API_KEY");
|
|
101
|
+
if (stored && stored.trim()) {
|
|
102
|
+
return stored.trim();
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
var OpenVikingClient = class {
|
|
110
|
+
endpoint;
|
|
111
|
+
apiKey;
|
|
112
|
+
resolvedSessionIds = /* @__PURE__ */ new Map();
|
|
113
|
+
constructor(endpoint, apiKey) {
|
|
114
|
+
this.endpoint = resolveEndpoint(endpoint);
|
|
115
|
+
this.apiKey = resolveApiKey(apiKey);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Формирование заголовков запроса, включая опциональный заголовок авторизации
|
|
119
|
+
*/
|
|
120
|
+
getHeaders(customHeaders) {
|
|
121
|
+
const headers = {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
...customHeaders
|
|
124
|
+
};
|
|
125
|
+
if (this.apiKey) {
|
|
126
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
127
|
+
}
|
|
128
|
+
return headers;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Генерация кандидатов ID сессии для fallback-разрешения:
|
|
132
|
+
* dsh-session-${id} <-> dsh-${id}
|
|
133
|
+
*/
|
|
134
|
+
getCandidateSessionIds(sessionId) {
|
|
135
|
+
const raw = sessionId.trim();
|
|
136
|
+
const candidates = [];
|
|
137
|
+
const cached = this.resolvedSessionIds.get(raw);
|
|
138
|
+
if (cached) {
|
|
139
|
+
candidates.push(cached);
|
|
140
|
+
}
|
|
141
|
+
if (raw.startsWith("dsh-session-")) {
|
|
142
|
+
const suffix = raw.slice("dsh-session-".length);
|
|
143
|
+
candidates.push(raw, `dsh-${suffix}`);
|
|
144
|
+
} else if (raw.startsWith("dsh-")) {
|
|
145
|
+
const suffix = raw.slice("dsh-".length);
|
|
146
|
+
candidates.push(raw, `dsh-session-${suffix}`);
|
|
147
|
+
} else {
|
|
148
|
+
candidates.push(`dsh-session-${raw}`, `dsh-${raw}`, raw);
|
|
149
|
+
}
|
|
150
|
+
return Array.from(new Set(candidates));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Проверка доступности и состояния сервиса OpenViking
|
|
154
|
+
*/
|
|
155
|
+
async checkHealth() {
|
|
156
|
+
try {
|
|
157
|
+
const res = await fetch(`${this.endpoint}/health`, {
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers: this.getHeaders()
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: `HTTP ${res.status}: ${res.statusText}`
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const body = await res.json().catch(() => ({}));
|
|
168
|
+
const isOk = body.ok !== false && body.status !== "error" && (body.ok === true || body.status === "ok" || body.status === "healthy" || res.ok);
|
|
169
|
+
return {
|
|
170
|
+
ok: isOk,
|
|
171
|
+
version: typeof body.version === "string" ? body.version : void 0,
|
|
172
|
+
storage: typeof body.storage === "string" ? body.storage : void 0
|
|
173
|
+
};
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
error: err instanceof Error ? err.message : String(err)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Получение метаданных сессии по идентификатору с автоматическим разрешением префикса.
|
|
183
|
+
* При сетевых сбоях или ошибках авторизации возвращает null, не выбрасывая исключений.
|
|
184
|
+
*/
|
|
185
|
+
async fetchSession(sessionId) {
|
|
186
|
+
if (!sessionId || !sessionId.trim()) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const candidates = this.getCandidateSessionIds(sessionId);
|
|
190
|
+
for (const candidateId of candidates) {
|
|
191
|
+
try {
|
|
192
|
+
const res = await fetch(
|
|
193
|
+
`${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}`,
|
|
194
|
+
{
|
|
195
|
+
method: "GET",
|
|
196
|
+
headers: this.getHeaders()
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
if (res.status === 404) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (!res.ok) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const data = await res.json();
|
|
206
|
+
const raw = data?.result ?? data?.data ?? data;
|
|
207
|
+
if (!raw || typeof raw !== "object") {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
211
|
+
return {
|
|
212
|
+
session_id: typeof raw.session_id === "string" ? raw.session_id : candidateId,
|
|
213
|
+
peer_id: typeof raw.peer_id === "string" ? raw.peer_id : void 0,
|
|
214
|
+
pending_tokens: typeof raw.pending_tokens === "number" ? raw.pending_tokens : 0,
|
|
215
|
+
message_count: typeof raw.message_count === "number" ? raw.message_count : void 0,
|
|
216
|
+
commit_count: typeof raw.commit_count === "number" ? raw.commit_count : void 0,
|
|
217
|
+
last_commit_at: typeof raw.last_commit_at === "string" ? raw.last_commit_at : typeof raw.last_commit === "string" ? raw.last_commit : void 0,
|
|
218
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
219
|
+
updated_at: typeof raw.updated_at === "string" ? raw.updated_at : void 0
|
|
220
|
+
};
|
|
221
|
+
} catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Алиас для fetchSession
|
|
229
|
+
*/
|
|
230
|
+
async getSession(sessionId) {
|
|
231
|
+
return this.fetchSession(sessionId);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Инициация фиксации (коммита) накопленных токенов сессии в долговременную память
|
|
235
|
+
*/
|
|
236
|
+
async commitSession(sessionId, options) {
|
|
237
|
+
if (!sessionId || !sessionId.trim()) {
|
|
238
|
+
return { ok: false, error: "Missing sessionId" };
|
|
239
|
+
}
|
|
240
|
+
const candidates = this.getCandidateSessionIds(sessionId);
|
|
241
|
+
const bodyPayload = JSON.stringify(options ?? { keep_recent_count: 10 });
|
|
242
|
+
let lastError = "Session commit failed";
|
|
243
|
+
for (const candidateId of candidates) {
|
|
244
|
+
try {
|
|
245
|
+
const res = await fetch(
|
|
246
|
+
`${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}/commit`,
|
|
247
|
+
{
|
|
248
|
+
method: "POST",
|
|
249
|
+
headers: this.getHeaders(),
|
|
250
|
+
body: bodyPayload
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
if (res.status === 404) {
|
|
254
|
+
lastError = `Session not found: ${candidateId}`;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
const errBody = await res.json().catch(() => null);
|
|
259
|
+
const errorMsg = errBody?.error?.message || errBody?.message || errBody?.error || `HTTP ${res.status}: ${res.statusText}`;
|
|
260
|
+
return { ok: false, error: String(errorMsg) };
|
|
261
|
+
}
|
|
262
|
+
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
263
|
+
return { ok: true };
|
|
264
|
+
} catch (err) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: err instanceof Error ? err.message : String(err)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return { ok: false, error: lastError };
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var defaultOpenVikingClient = new OpenVikingClient();
|
|
275
|
+
function checkHealth(endpoint, apiKey) {
|
|
276
|
+
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
277
|
+
return client.checkHealth();
|
|
278
|
+
}
|
|
279
|
+
function fetchSession(sessionId, endpoint, apiKey) {
|
|
280
|
+
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
281
|
+
return client.fetchSession(sessionId);
|
|
282
|
+
}
|
|
283
|
+
function getSession(sessionId, endpoint, apiKey) {
|
|
284
|
+
return fetchSession(sessionId, endpoint, apiKey);
|
|
285
|
+
}
|
|
286
|
+
function commitSession(sessionId, options, endpoint, apiKey) {
|
|
287
|
+
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
288
|
+
return client.commitSession(sessionId, options);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/client/recallParser.ts
|
|
292
|
+
var KNOWN_CATEGORIES = /* @__PURE__ */ new Set([
|
|
293
|
+
"preferences",
|
|
294
|
+
"entities",
|
|
295
|
+
"events",
|
|
296
|
+
"skills",
|
|
297
|
+
"resources",
|
|
298
|
+
"profile"
|
|
299
|
+
]);
|
|
300
|
+
function inferCategory(uri) {
|
|
301
|
+
if (!uri || typeof uri !== "string") return void 0;
|
|
302
|
+
const memoriesMatch = uri.match(/\/memories\/([^/#?]+)/i);
|
|
303
|
+
if (memoriesMatch && memoriesMatch[1]) {
|
|
304
|
+
return memoriesMatch[1].toLowerCase();
|
|
305
|
+
}
|
|
306
|
+
const cleanUri = uri.replace(/^viking:\/\/?/i, "");
|
|
307
|
+
const segments = cleanUri.split("/").filter(Boolean);
|
|
308
|
+
for (const seg of segments) {
|
|
309
|
+
const lower = seg.toLowerCase();
|
|
310
|
+
if (KNOWN_CATEGORIES.has(lower)) {
|
|
311
|
+
return lower;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (segments[0]?.toLowerCase() === "user" && segments.length >= 3) {
|
|
315
|
+
return segments[2].toLowerCase();
|
|
316
|
+
}
|
|
317
|
+
if (segments.length >= 2) {
|
|
318
|
+
return segments[0].toLowerCase();
|
|
319
|
+
}
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
function extractAllText(input) {
|
|
323
|
+
if (input == null) return "";
|
|
324
|
+
if (typeof input === "string") return input;
|
|
325
|
+
if (typeof input === "number" || typeof input === "boolean")
|
|
326
|
+
return String(input);
|
|
327
|
+
if (Array.isArray(input)) {
|
|
328
|
+
return input.map(extractAllText).filter(Boolean).join("\n");
|
|
329
|
+
}
|
|
330
|
+
if (typeof input === "object") {
|
|
331
|
+
const obj = input;
|
|
332
|
+
const parts = [];
|
|
333
|
+
if ("content" in obj && obj.content != null) {
|
|
334
|
+
parts.push(extractAllText(obj.content));
|
|
335
|
+
}
|
|
336
|
+
if ("text" in obj && obj.text != null) {
|
|
337
|
+
parts.push(extractAllText(obj.text));
|
|
338
|
+
}
|
|
339
|
+
if ("message" in obj && obj.message != null) {
|
|
340
|
+
parts.push(extractAllText(obj.message));
|
|
341
|
+
}
|
|
342
|
+
if (parts.length > 0) {
|
|
343
|
+
return parts.join("\n");
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
return Object.values(obj).map(extractAllText).filter(Boolean).join("\n");
|
|
347
|
+
} catch {
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return "";
|
|
352
|
+
}
|
|
353
|
+
function findContextBlocks(text) {
|
|
354
|
+
const blocks = [];
|
|
355
|
+
const openTagRegex = /<openviking-context\b([^>]*)>/gi;
|
|
356
|
+
let match;
|
|
357
|
+
const tagPositions = [];
|
|
358
|
+
while ((match = openTagRegex.exec(text)) !== null) {
|
|
359
|
+
tagPositions.push({
|
|
360
|
+
index: match.index,
|
|
361
|
+
endIndex: match.index + match[0].length,
|
|
362
|
+
attrs: match[1] || ""
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
if (tagPositions.length === 0) {
|
|
366
|
+
if (/<available-memories\b/i.test(text)) {
|
|
367
|
+
blocks.push({
|
|
368
|
+
isProfile: true,
|
|
369
|
+
attributes: 'source="profile"',
|
|
370
|
+
content: text
|
|
371
|
+
});
|
|
372
|
+
} else if (/<memory\b/i.test(text)) {
|
|
373
|
+
blocks.push({
|
|
374
|
+
isProfile: false,
|
|
375
|
+
attributes: "",
|
|
376
|
+
content: text
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return blocks;
|
|
380
|
+
}
|
|
381
|
+
for (let i = 0; i < tagPositions.length; i++) {
|
|
382
|
+
const current = tagPositions[i];
|
|
383
|
+
const nextStart = i + 1 < tagPositions.length ? tagPositions[i + 1].index : text.length;
|
|
384
|
+
const blockText = text.slice(current.endIndex, nextStart);
|
|
385
|
+
const closeIndex = blockText.search(/<\/openviking-context>/i);
|
|
386
|
+
const content = closeIndex !== -1 ? blockText.slice(0, closeIndex) : blockText;
|
|
387
|
+
const isProfile = /\bsource\s*=\s*["']profile["']/i.test(current.attrs);
|
|
388
|
+
blocks.push({
|
|
389
|
+
isProfile,
|
|
390
|
+
attributes: current.attrs,
|
|
391
|
+
content
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return blocks;
|
|
395
|
+
}
|
|
396
|
+
function normalizeUri(uri) {
|
|
397
|
+
let clean = uri.trim();
|
|
398
|
+
if (clean.endsWith("/") && !clean.endsWith("://")) {
|
|
399
|
+
clean = clean.slice(0, -1);
|
|
400
|
+
}
|
|
401
|
+
return clean;
|
|
402
|
+
}
|
|
403
|
+
function parseProfileItems(blockContent) {
|
|
404
|
+
const items = [];
|
|
405
|
+
let memContent = blockContent;
|
|
406
|
+
const availMatch = blockContent.match(
|
|
407
|
+
/<available-memories\b[^>]*>([\s\S]*?)(?:<\/available-memories>|$)/i
|
|
408
|
+
);
|
|
409
|
+
if (availMatch && availMatch[1]) {
|
|
410
|
+
memContent = availMatch[1];
|
|
411
|
+
}
|
|
412
|
+
const lines = memContent.split(/\r?\n/);
|
|
413
|
+
let currentBaseUri = null;
|
|
414
|
+
for (const rawLine of lines) {
|
|
415
|
+
const trimmed = rawLine.trim();
|
|
416
|
+
if (!trimmed) continue;
|
|
417
|
+
const baseMatch = trimmed.match(
|
|
418
|
+
/^[-*•]?\s*(viking:\/\/[^\s<>"'`]+?\/)\s*$/i
|
|
419
|
+
);
|
|
420
|
+
if (baseMatch) {
|
|
421
|
+
currentBaseUri = baseMatch[1];
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const bulletMatch = trimmed.match(/^[-*•]\s+([^\s<>"'`].*?)$/);
|
|
425
|
+
if (bulletMatch && currentBaseUri && !bulletMatch[1].startsWith("viking://")) {
|
|
426
|
+
let relPath = bulletMatch[1].trim();
|
|
427
|
+
relPath = relPath.replace(/^\[([^\]]+)\](?:\([^)]*\))?/, "$1");
|
|
428
|
+
relPath = relPath.replace(/[.,;:)\]]+$/, "").trim();
|
|
429
|
+
if (relPath) {
|
|
430
|
+
const cleanBase = currentBaseUri.endsWith("/") ? currentBaseUri : currentBaseUri + "/";
|
|
431
|
+
const fullUri = normalizeUri(cleanBase + relPath.replace(/^\/+/, ""));
|
|
432
|
+
items.push({
|
|
433
|
+
uri: fullUri,
|
|
434
|
+
category: inferCategory(fullUri),
|
|
435
|
+
source: "profile"
|
|
436
|
+
});
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const fullBulletMatch = trimmed.match(
|
|
441
|
+
/^[-*•]\s+(viking:\/\/[^<>"'`\r\n]+)/i
|
|
442
|
+
);
|
|
443
|
+
if (fullBulletMatch) {
|
|
444
|
+
let uri = fullBulletMatch[1].trim().replace(/[.,;:)\]]+$/, "");
|
|
445
|
+
if (uri.endsWith("/")) {
|
|
446
|
+
currentBaseUri = uri;
|
|
447
|
+
} else {
|
|
448
|
+
uri = normalizeUri(uri);
|
|
449
|
+
items.push({
|
|
450
|
+
uri,
|
|
451
|
+
category: inferCategory(uri),
|
|
452
|
+
source: "profile"
|
|
453
|
+
});
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const inlineMatches = trimmed.matchAll(/viking:\/\/[^\s<>"'`]+/gi);
|
|
458
|
+
for (const m of inlineMatches) {
|
|
459
|
+
let uri = m[0].replace(/[.,;:)\]]+$/, "");
|
|
460
|
+
if (uri.endsWith("/")) {
|
|
461
|
+
currentBaseUri = uri;
|
|
462
|
+
} else {
|
|
463
|
+
uri = normalizeUri(uri);
|
|
464
|
+
items.push({
|
|
465
|
+
uri,
|
|
466
|
+
category: inferCategory(uri),
|
|
467
|
+
source: "profile"
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return items;
|
|
473
|
+
}
|
|
474
|
+
function parseRecallItems(blockContent) {
|
|
475
|
+
const items = [];
|
|
476
|
+
const memoryTagRegex = /<memory\b([^>]*?)(?:\/>|>([\s\S]*?)(?:<\/memory>|(?=<memory\b)|$))/gi;
|
|
477
|
+
let memMatch;
|
|
478
|
+
while ((memMatch = memoryTagRegex.exec(blockContent)) !== null) {
|
|
479
|
+
const attrsString = memMatch[1] || "";
|
|
480
|
+
const innerContent = memMatch[2] || "";
|
|
481
|
+
const uriMatch = attrsString.match(
|
|
482
|
+
/\buri\s*=\s*(?:["'](viking:\/\/[^"']+)["']|(viking:\/\/[^\s>]+))/i
|
|
483
|
+
);
|
|
484
|
+
if (!uriMatch) continue;
|
|
485
|
+
const rawUri = uriMatch[1] || uriMatch[2];
|
|
486
|
+
const uri = normalizeUri(rawUri);
|
|
487
|
+
const scoreMatch = attrsString.match(
|
|
488
|
+
/\bscore\s*=\s*(?:["']([^"']+)["']|([0-9.]+))/i
|
|
489
|
+
);
|
|
490
|
+
let score;
|
|
491
|
+
const scoreStr = scoreMatch ? scoreMatch[1] || scoreMatch[2] : void 0;
|
|
492
|
+
if (scoreStr) {
|
|
493
|
+
const parsed = parseFloat(scoreStr);
|
|
494
|
+
if (Number.isFinite(parsed)) {
|
|
495
|
+
score = parsed;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const typeMatch = attrsString.match(
|
|
499
|
+
/\b(?:type|category)\s*=\s*["']([^"']+)["']/i
|
|
500
|
+
);
|
|
501
|
+
const explicitCategory = typeMatch ? typeMatch[1].trim() : void 0;
|
|
502
|
+
const category = inferCategory(uri) || explicitCategory;
|
|
503
|
+
let abstractText;
|
|
504
|
+
const abstractAttrMatch = attrsString.match(
|
|
505
|
+
/\babstract\s*=\s*["']([^"']+)["']/i
|
|
506
|
+
);
|
|
507
|
+
if (abstractAttrMatch) {
|
|
508
|
+
abstractText = abstractAttrMatch[1].trim();
|
|
509
|
+
} else {
|
|
510
|
+
const trimmedContent = innerContent.trim();
|
|
511
|
+
if (trimmedContent) {
|
|
512
|
+
abstractText = trimmedContent;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
items.push({
|
|
516
|
+
uri,
|
|
517
|
+
category,
|
|
518
|
+
source: "recall",
|
|
519
|
+
score,
|
|
520
|
+
abstract: abstractText
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
return items;
|
|
524
|
+
}
|
|
525
|
+
function parseRecalledMemories(input) {
|
|
526
|
+
if (input == null) {
|
|
527
|
+
return {
|
|
528
|
+
recalledCount: 0,
|
|
529
|
+
items: [],
|
|
530
|
+
profileItems: [],
|
|
531
|
+
recallItems: []
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
const text = extractAllText(input);
|
|
535
|
+
if (!text.trim()) {
|
|
536
|
+
return {
|
|
537
|
+
recalledCount: 0,
|
|
538
|
+
items: [],
|
|
539
|
+
profileItems: [],
|
|
540
|
+
recallItems: []
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
const blocks = findContextBlocks(text);
|
|
544
|
+
const rawProfileItems = [];
|
|
545
|
+
const rawRecallItems = [];
|
|
546
|
+
for (const block of blocks) {
|
|
547
|
+
if (block.isProfile) {
|
|
548
|
+
rawProfileItems.push(...parseProfileItems(block.content));
|
|
549
|
+
} else {
|
|
550
|
+
rawRecallItems.push(...parseRecallItems(block.content));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const recallMap = /* @__PURE__ */ new Map();
|
|
554
|
+
for (const item of rawRecallItems) {
|
|
555
|
+
const existing = recallMap.get(item.uri);
|
|
556
|
+
if (!existing) {
|
|
557
|
+
recallMap.set(item.uri, { ...item });
|
|
558
|
+
} else {
|
|
559
|
+
if (existing.score === void 0 && item.score !== void 0) {
|
|
560
|
+
existing.score = item.score;
|
|
561
|
+
} else if (existing.score !== void 0 && item.score !== void 0) {
|
|
562
|
+
existing.score = Math.max(existing.score, item.score);
|
|
563
|
+
}
|
|
564
|
+
if (!existing.abstract && item.abstract) {
|
|
565
|
+
existing.abstract = item.abstract;
|
|
566
|
+
}
|
|
567
|
+
if (!existing.category && item.category) {
|
|
568
|
+
existing.category = item.category;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const profileMap = /* @__PURE__ */ new Map();
|
|
573
|
+
for (const item of rawProfileItems) {
|
|
574
|
+
if (!profileMap.has(item.uri)) {
|
|
575
|
+
profileMap.set(item.uri, { ...item });
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const itemsMap = /* @__PURE__ */ new Map();
|
|
579
|
+
for (const [uri, item] of profileMap.entries()) {
|
|
580
|
+
itemsMap.set(uri, item);
|
|
581
|
+
}
|
|
582
|
+
for (const [uri, item] of recallMap.entries()) {
|
|
583
|
+
itemsMap.set(uri, item);
|
|
584
|
+
}
|
|
585
|
+
const items = Array.from(itemsMap.values());
|
|
586
|
+
const profileItems = items.filter((it) => it.source === "profile");
|
|
587
|
+
const recallItems = items.filter((it) => it.source === "recall");
|
|
588
|
+
return {
|
|
589
|
+
recalledCount: items.length,
|
|
590
|
+
items,
|
|
591
|
+
profileItems,
|
|
592
|
+
recallItems
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/client/OpenVikingStatusPopover.tsx
|
|
597
|
+
var import_react = require("react");
|
|
598
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
599
|
+
function getProgressBarPercent(pendingTokens, threshold = COMMIT_THRESHOLD) {
|
|
600
|
+
if (!threshold || threshold <= 0) return 0;
|
|
601
|
+
const ratio = (pendingTokens || 0) / threshold;
|
|
602
|
+
return Math.min(100, Math.max(0, Math.round(ratio * 100)));
|
|
603
|
+
}
|
|
604
|
+
function getProgressBarColor(percent) {
|
|
605
|
+
if (percent >= 80) {
|
|
606
|
+
return "var(--dsw-status-warning, #fbbf24)";
|
|
607
|
+
}
|
|
608
|
+
return "var(--dsw-status-success, #34d399)";
|
|
609
|
+
}
|
|
610
|
+
function formatRelativeTime(isoOrTimestamp, now = Date.now()) {
|
|
611
|
+
if (!isoOrTimestamp) return void 0;
|
|
612
|
+
const date = new Date(isoOrTimestamp);
|
|
613
|
+
const time = date.getTime();
|
|
614
|
+
if (isNaN(time)) return String(isoOrTimestamp);
|
|
615
|
+
const diffMs = now - time;
|
|
616
|
+
if (diffMs < 0) return "just now";
|
|
617
|
+
const diffSec = Math.floor(diffMs / 1e3);
|
|
618
|
+
if (diffSec < 60) return "just now";
|
|
619
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
620
|
+
if (diffMin < 60) return `${diffMin}m ago`;
|
|
621
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
622
|
+
if (diffHour < 24) return `${diffHour}h ago`;
|
|
623
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
624
|
+
return `${diffDay}d ago`;
|
|
625
|
+
}
|
|
626
|
+
function formatMemoryLeafName(uri) {
|
|
627
|
+
if (!uri) return "";
|
|
628
|
+
const clean = uri.replace(/^viking:\/\/?/i, "");
|
|
629
|
+
const memMatch = clean.match(/memories\/[^/]+\/(.+)$/i);
|
|
630
|
+
if (memMatch && memMatch[1]) {
|
|
631
|
+
return memMatch[1];
|
|
632
|
+
}
|
|
633
|
+
const segments = clean.split("/").filter(Boolean);
|
|
634
|
+
if (segments.length >= 2) {
|
|
635
|
+
return segments.slice(-2).join("/");
|
|
636
|
+
}
|
|
637
|
+
return segments[0] || clean;
|
|
638
|
+
}
|
|
639
|
+
function formatEndpoint(endpoint) {
|
|
640
|
+
if (!endpoint || !endpoint.trim()) {
|
|
641
|
+
return "127.0.0.1:1933";
|
|
642
|
+
}
|
|
643
|
+
return endpoint.trim().replace(/^https?:\/\//, "");
|
|
644
|
+
}
|
|
645
|
+
function truncateSessionId(id, maxLen = 16) {
|
|
646
|
+
if (!id) return "";
|
|
647
|
+
if (id.length <= maxLen) return id;
|
|
648
|
+
return `${id.slice(0, maxLen)}...`;
|
|
649
|
+
}
|
|
650
|
+
function getCategoryBadgeStyle(category) {
|
|
651
|
+
switch (category?.toLowerCase()) {
|
|
652
|
+
case "preferences":
|
|
653
|
+
return {
|
|
654
|
+
backgroundColor: "rgba(168, 85, 247, 0.15)",
|
|
655
|
+
color: "var(--dsw-status-purple, #c084fc)"
|
|
656
|
+
};
|
|
657
|
+
case "entities":
|
|
658
|
+
return {
|
|
659
|
+
backgroundColor: "rgba(59, 130, 246, 0.15)",
|
|
660
|
+
color: "var(--dsw-status-info, #60a5fa)"
|
|
661
|
+
};
|
|
662
|
+
case "skills":
|
|
663
|
+
return {
|
|
664
|
+
backgroundColor: "rgba(236, 72, 153, 0.15)",
|
|
665
|
+
color: "var(--dsw-status-pink, #f472b6)"
|
|
666
|
+
};
|
|
667
|
+
case "events":
|
|
668
|
+
return {
|
|
669
|
+
backgroundColor: "rgba(245, 158, 11, 0.15)",
|
|
670
|
+
color: "var(--dsw-status-warning, #fbbf24)"
|
|
671
|
+
};
|
|
672
|
+
case "resources":
|
|
673
|
+
return {
|
|
674
|
+
backgroundColor: "rgba(20, 184, 166, 0.15)",
|
|
675
|
+
color: "var(--dsw-status-teal, #2dd4bf)"
|
|
676
|
+
};
|
|
677
|
+
default:
|
|
678
|
+
return {
|
|
679
|
+
backgroundColor: "rgba(148, 163, 184, 0.15)",
|
|
680
|
+
color: "var(--dsw-text-muted, #94a3b8)"
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function handleEscapeKey(event, onClose) {
|
|
685
|
+
if (event.key === "Escape") {
|
|
686
|
+
onClose?.();
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
function OpenVikingStatusPopover({
|
|
692
|
+
sessionId,
|
|
693
|
+
health,
|
|
694
|
+
sessionData,
|
|
695
|
+
recalledResult,
|
|
696
|
+
endpoint,
|
|
697
|
+
isCommitting = false,
|
|
698
|
+
commitError = null,
|
|
699
|
+
onCommitNow,
|
|
700
|
+
onClose,
|
|
701
|
+
className,
|
|
702
|
+
style
|
|
703
|
+
}) {
|
|
704
|
+
const [copied, setCopied] = (0, import_react.useState)(false);
|
|
705
|
+
const copyTimeoutRef = (0, import_react.useRef)(null);
|
|
706
|
+
(0, import_react.useEffect)(() => {
|
|
707
|
+
return () => {
|
|
708
|
+
if (copyTimeoutRef.current) {
|
|
709
|
+
clearTimeout(copyTimeoutRef.current);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}, []);
|
|
713
|
+
const isOnline = health?.ok === true;
|
|
714
|
+
const statusColor = isOnline ? "var(--dsw-status-success, #34d399)" : "var(--dsw-status-error, #f87171)";
|
|
715
|
+
const displaySessionId = sessionData?.session_id || sessionId || "";
|
|
716
|
+
const pendingTokens = sessionData?.pending_tokens ?? 0;
|
|
717
|
+
const progressPercent = getProgressBarPercent(pendingTokens);
|
|
718
|
+
const progressBarColor = getProgressBarColor(progressPercent);
|
|
719
|
+
const memoryItems = recalledResult?.items || [];
|
|
720
|
+
const recalledCount = recalledResult?.recalledCount ?? memoryItems.length;
|
|
721
|
+
const handleCopySessionId = (0, import_react.useCallback)(() => {
|
|
722
|
+
if (!displaySessionId) return;
|
|
723
|
+
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
724
|
+
navigator.clipboard.writeText(displaySessionId).catch(() => {
|
|
725
|
+
});
|
|
726
|
+
setCopied(true);
|
|
727
|
+
if (copyTimeoutRef.current) {
|
|
728
|
+
clearTimeout(copyTimeoutRef.current);
|
|
729
|
+
}
|
|
730
|
+
copyTimeoutRef.current = setTimeout(() => {
|
|
731
|
+
setCopied(false);
|
|
732
|
+
copyTimeoutRef.current = null;
|
|
733
|
+
}, 1500);
|
|
734
|
+
}
|
|
735
|
+
}, [displaySessionId]);
|
|
736
|
+
const isCommitDisabled = isCommitting || !isOnline || pendingTokens === 0;
|
|
737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
738
|
+
"div",
|
|
739
|
+
{
|
|
740
|
+
role: "dialog",
|
|
741
|
+
"aria-label": "OpenViking Memory Details",
|
|
742
|
+
className,
|
|
743
|
+
style: {
|
|
744
|
+
position: "absolute",
|
|
745
|
+
bottom: "calc(100% + 8px)",
|
|
746
|
+
right: 0,
|
|
747
|
+
width: "320px",
|
|
748
|
+
backgroundColor: "var(--dsw-surface-overlay, #1e293b)",
|
|
749
|
+
border: "1px solid var(--dsw-border-default, #334155)",
|
|
750
|
+
borderRadius: "8px",
|
|
751
|
+
padding: "12px",
|
|
752
|
+
boxShadow: "0 10px 25px -5px rgba(0, 0, 0, 0.5)",
|
|
753
|
+
zIndex: 1e3,
|
|
754
|
+
fontSize: "12px",
|
|
755
|
+
color: "var(--dsw-text-default, #f1f5f9)",
|
|
756
|
+
fontFamily: "var(--dsw-font-sans, system-ui, sans-serif)",
|
|
757
|
+
boxSizing: "border-box",
|
|
758
|
+
...style
|
|
759
|
+
},
|
|
760
|
+
children: [
|
|
761
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
|
|
762
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
763
|
+
"div",
|
|
764
|
+
{
|
|
765
|
+
style: {
|
|
766
|
+
display: "flex",
|
|
767
|
+
justifyContent: "space-between",
|
|
768
|
+
alignItems: "flex-start",
|
|
769
|
+
marginBottom: "10px",
|
|
770
|
+
paddingBottom: "8px",
|
|
771
|
+
borderBottom: "1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.08))"
|
|
772
|
+
},
|
|
773
|
+
children: [
|
|
774
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
775
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
776
|
+
"div",
|
|
777
|
+
{
|
|
778
|
+
style: {
|
|
779
|
+
fontWeight: 600,
|
|
780
|
+
fontSize: "13px",
|
|
781
|
+
color: "var(--dsw-text-default, #f1f5f9)"
|
|
782
|
+
},
|
|
783
|
+
children: "OpenViking Memory"
|
|
784
|
+
}
|
|
785
|
+
),
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
787
|
+
"div",
|
|
788
|
+
{
|
|
789
|
+
"data-testid": "endpoint-label",
|
|
790
|
+
style: {
|
|
791
|
+
fontSize: "10px",
|
|
792
|
+
color: "var(--dsw-text-muted, #94a3b8)",
|
|
793
|
+
fontFamily: "var(--dsw-font-mono, monospace)",
|
|
794
|
+
marginTop: "1px"
|
|
795
|
+
},
|
|
796
|
+
children: formatEndpoint(endpoint)
|
|
797
|
+
}
|
|
798
|
+
)
|
|
799
|
+
] }),
|
|
800
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
801
|
+
"div",
|
|
802
|
+
{
|
|
803
|
+
"data-testid": "status-badge",
|
|
804
|
+
style: {
|
|
805
|
+
display: "inline-flex",
|
|
806
|
+
alignItems: "center",
|
|
807
|
+
gap: "5px",
|
|
808
|
+
fontSize: "10px",
|
|
809
|
+
padding: "2px 7px",
|
|
810
|
+
borderRadius: "4px",
|
|
811
|
+
backgroundColor: isOnline ? "rgba(52, 211, 153, 0.15)" : "rgba(248, 113, 113, 0.15)",
|
|
812
|
+
color: statusColor,
|
|
813
|
+
fontWeight: 600
|
|
814
|
+
},
|
|
815
|
+
children: [
|
|
816
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
817
|
+
"span",
|
|
818
|
+
{
|
|
819
|
+
"data-testid": "status-badge-dot",
|
|
820
|
+
style: {
|
|
821
|
+
width: "6px",
|
|
822
|
+
height: "6px",
|
|
823
|
+
borderRadius: "50%",
|
|
824
|
+
backgroundColor: statusColor,
|
|
825
|
+
flexShrink: 0
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: isOnline ? health?.version ? `ONLINE v${health.version}` : "ONLINE" : "OFFLINE" })
|
|
830
|
+
]
|
|
831
|
+
}
|
|
832
|
+
)
|
|
833
|
+
]
|
|
834
|
+
}
|
|
835
|
+
),
|
|
836
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
837
|
+
"div",
|
|
838
|
+
{
|
|
839
|
+
style: {
|
|
840
|
+
display: "flex",
|
|
841
|
+
flexDirection: "column",
|
|
842
|
+
gap: "6px",
|
|
843
|
+
marginBottom: "10px"
|
|
844
|
+
},
|
|
845
|
+
children: [
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
847
|
+
"div",
|
|
848
|
+
{
|
|
849
|
+
style: {
|
|
850
|
+
display: "flex",
|
|
851
|
+
justifyContent: "space-between",
|
|
852
|
+
alignItems: "center"
|
|
853
|
+
},
|
|
854
|
+
children: [
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--dsw-text-muted, #94a3b8)" }, children: "Session ID:" }),
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
857
|
+
"div",
|
|
858
|
+
{
|
|
859
|
+
style: {
|
|
860
|
+
display: "flex",
|
|
861
|
+
alignItems: "center",
|
|
862
|
+
gap: "4px"
|
|
863
|
+
},
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
866
|
+
"span",
|
|
867
|
+
{
|
|
868
|
+
"data-testid": "session-id-value",
|
|
869
|
+
role: "button",
|
|
870
|
+
tabIndex: 0,
|
|
871
|
+
onKeyDown: (e) => {
|
|
872
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
873
|
+
e.preventDefault();
|
|
874
|
+
handleCopySessionId();
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
"aria-label": "Click to copy Session ID",
|
|
878
|
+
style: {
|
|
879
|
+
fontFamily: "var(--dsw-font-mono, monospace)",
|
|
880
|
+
cursor: "pointer"
|
|
881
|
+
},
|
|
882
|
+
title: displaySessionId,
|
|
883
|
+
onClick: handleCopySessionId,
|
|
884
|
+
children: truncateSessionId(displaySessionId)
|
|
885
|
+
}
|
|
886
|
+
),
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
888
|
+
"button",
|
|
889
|
+
{
|
|
890
|
+
type: "button",
|
|
891
|
+
"data-testid": "copy-session-btn",
|
|
892
|
+
onClick: handleCopySessionId,
|
|
893
|
+
title: copied ? "Copied!" : "Copy Session ID",
|
|
894
|
+
"aria-label": copied ? "Copied!" : "Copy Session ID",
|
|
895
|
+
style: {
|
|
896
|
+
background: "none",
|
|
897
|
+
border: "none",
|
|
898
|
+
cursor: "pointer",
|
|
899
|
+
padding: "2px 4px",
|
|
900
|
+
fontSize: "10px",
|
|
901
|
+
color: copied ? "var(--dsw-status-success, #34d399)" : "var(--dsw-text-muted, #94a3b8)",
|
|
902
|
+
borderRadius: "3px"
|
|
903
|
+
},
|
|
904
|
+
children: copied ? "\u2713" : "\u{1F4CB}"
|
|
905
|
+
}
|
|
906
|
+
)
|
|
907
|
+
]
|
|
908
|
+
}
|
|
909
|
+
)
|
|
910
|
+
]
|
|
911
|
+
}
|
|
912
|
+
),
|
|
913
|
+
sessionData?.peer_id && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
914
|
+
"div",
|
|
915
|
+
{
|
|
916
|
+
style: {
|
|
917
|
+
display: "flex",
|
|
918
|
+
justifyContent: "space-between",
|
|
919
|
+
alignItems: "center"
|
|
920
|
+
},
|
|
921
|
+
children: [
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--dsw-text-muted, #94a3b8)" }, children: "Peer ID:" }),
|
|
923
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
924
|
+
"span",
|
|
925
|
+
{
|
|
926
|
+
"data-testid": "peer-id-value",
|
|
927
|
+
style: {
|
|
928
|
+
maxWidth: "180px",
|
|
929
|
+
overflow: "hidden",
|
|
930
|
+
textOverflow: "ellipsis",
|
|
931
|
+
whiteSpace: "nowrap",
|
|
932
|
+
fontFamily: "var(--dsw-font-mono, monospace)"
|
|
933
|
+
},
|
|
934
|
+
title: sessionData.peer_id,
|
|
935
|
+
children: sessionData.peer_id
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
]
|
|
939
|
+
}
|
|
940
|
+
),
|
|
941
|
+
sessionData?.last_commit_at && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
942
|
+
"div",
|
|
943
|
+
{
|
|
944
|
+
style: {
|
|
945
|
+
display: "flex",
|
|
946
|
+
justifyContent: "space-between",
|
|
947
|
+
alignItems: "center"
|
|
948
|
+
},
|
|
949
|
+
children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--dsw-text-muted, #94a3b8)" }, children: "Last Commit:" }),
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
952
|
+
"span",
|
|
953
|
+
{
|
|
954
|
+
"data-testid": "last-commit-value",
|
|
955
|
+
title: sessionData.last_commit_at,
|
|
956
|
+
style: { color: "var(--dsw-text-default, #e2e8f0)" },
|
|
957
|
+
children: formatRelativeTime(sessionData.last_commit_at) || sessionData.last_commit_at
|
|
958
|
+
}
|
|
959
|
+
)
|
|
960
|
+
]
|
|
961
|
+
}
|
|
962
|
+
)
|
|
963
|
+
]
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: "12px" }, children: [
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
968
|
+
"div",
|
|
969
|
+
{
|
|
970
|
+
style: {
|
|
971
|
+
display: "flex",
|
|
972
|
+
justifyContent: "space-between",
|
|
973
|
+
marginBottom: "4px"
|
|
974
|
+
},
|
|
975
|
+
children: [
|
|
976
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "var(--dsw-text-muted, #94a3b8)" }, children: "Pending Tokens:" }),
|
|
977
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "pending-tokens-label", style: { fontWeight: 500 }, children: `${pendingTokens.toLocaleString()} / ${COMMIT_THRESHOLD.toLocaleString()}` })
|
|
978
|
+
]
|
|
979
|
+
}
|
|
980
|
+
),
|
|
981
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
982
|
+
"div",
|
|
983
|
+
{
|
|
984
|
+
"data-testid": "progress-bar-track",
|
|
985
|
+
style: {
|
|
986
|
+
width: "100%",
|
|
987
|
+
height: "6px",
|
|
988
|
+
borderRadius: "3px",
|
|
989
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
990
|
+
overflow: "hidden"
|
|
991
|
+
},
|
|
992
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
993
|
+
"div",
|
|
994
|
+
{
|
|
995
|
+
"data-testid": "progress-bar-fill",
|
|
996
|
+
style: {
|
|
997
|
+
width: `${progressPercent}%`,
|
|
998
|
+
height: "100%",
|
|
999
|
+
backgroundColor: progressBarColor,
|
|
1000
|
+
transition: "width 0.3s ease, background-color 0.3s ease"
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
)
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
] }),
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: "12px" }, children: [
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1009
|
+
"div",
|
|
1010
|
+
{
|
|
1011
|
+
style: {
|
|
1012
|
+
display: "flex",
|
|
1013
|
+
justifyContent: "space-between",
|
|
1014
|
+
alignItems: "center",
|
|
1015
|
+
marginBottom: "4px"
|
|
1016
|
+
},
|
|
1017
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontWeight: 600, fontSize: "11px" }, children: `Recalled Memories (${recalledCount})` })
|
|
1018
|
+
}
|
|
1019
|
+
),
|
|
1020
|
+
memoryItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1021
|
+
"div",
|
|
1022
|
+
{
|
|
1023
|
+
"data-testid": "empty-memories-message",
|
|
1024
|
+
style: {
|
|
1025
|
+
padding: "8px 0",
|
|
1026
|
+
color: "var(--dsw-text-muted, #94a3b8)",
|
|
1027
|
+
fontStyle: "italic",
|
|
1028
|
+
fontSize: "11px",
|
|
1029
|
+
textAlign: "center"
|
|
1030
|
+
},
|
|
1031
|
+
children: "No memories recalled in this session"
|
|
1032
|
+
}
|
|
1033
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1034
|
+
"div",
|
|
1035
|
+
{
|
|
1036
|
+
"data-testid": "recalled-memories-list",
|
|
1037
|
+
style: {
|
|
1038
|
+
maxHeight: "140px",
|
|
1039
|
+
overflowY: "auto",
|
|
1040
|
+
display: "flex",
|
|
1041
|
+
flexDirection: "column",
|
|
1042
|
+
gap: "4px",
|
|
1043
|
+
paddingRight: "2px"
|
|
1044
|
+
},
|
|
1045
|
+
children: memoryItems.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1046
|
+
"div",
|
|
1047
|
+
{
|
|
1048
|
+
"data-testid": "recalled-memory-item",
|
|
1049
|
+
title: item.uri,
|
|
1050
|
+
style: {
|
|
1051
|
+
display: "flex",
|
|
1052
|
+
alignItems: "center",
|
|
1053
|
+
gap: "6px",
|
|
1054
|
+
padding: "4px 6px",
|
|
1055
|
+
borderRadius: "4px",
|
|
1056
|
+
backgroundColor: "rgba(255, 255, 255, 0.04)",
|
|
1057
|
+
border: "1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.05))",
|
|
1058
|
+
fontSize: "11px"
|
|
1059
|
+
},
|
|
1060
|
+
children: [
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1062
|
+
"span",
|
|
1063
|
+
{
|
|
1064
|
+
"data-testid": "memory-category-badge",
|
|
1065
|
+
style: {
|
|
1066
|
+
fontSize: "9px",
|
|
1067
|
+
fontWeight: 600,
|
|
1068
|
+
padding: "1px 4px",
|
|
1069
|
+
borderRadius: "3px",
|
|
1070
|
+
textTransform: "uppercase",
|
|
1071
|
+
flexShrink: 0,
|
|
1072
|
+
...getCategoryBadgeStyle(item.category)
|
|
1073
|
+
},
|
|
1074
|
+
children: item.category || "memory"
|
|
1075
|
+
}
|
|
1076
|
+
),
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1078
|
+
"span",
|
|
1079
|
+
{
|
|
1080
|
+
"data-testid": "memory-source-badge",
|
|
1081
|
+
style: {
|
|
1082
|
+
fontSize: "9px",
|
|
1083
|
+
fontWeight: 600,
|
|
1084
|
+
padding: "1px 4px",
|
|
1085
|
+
borderRadius: "3px",
|
|
1086
|
+
textTransform: "uppercase",
|
|
1087
|
+
flexShrink: 0,
|
|
1088
|
+
...item.source === "profile" ? {
|
|
1089
|
+
backgroundColor: "rgba(99, 102, 241, 0.15)",
|
|
1090
|
+
color: "#818cf8"
|
|
1091
|
+
} : {
|
|
1092
|
+
backgroundColor: "rgba(16, 185, 129, 0.15)",
|
|
1093
|
+
color: "var(--dsw-status-success, #34d399)"
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
children: item.source
|
|
1097
|
+
}
|
|
1098
|
+
),
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1100
|
+
"span",
|
|
1101
|
+
{
|
|
1102
|
+
"data-testid": "memory-leaf-name",
|
|
1103
|
+
style: {
|
|
1104
|
+
flex: 1,
|
|
1105
|
+
overflow: "hidden",
|
|
1106
|
+
textOverflow: "ellipsis",
|
|
1107
|
+
whiteSpace: "nowrap",
|
|
1108
|
+
fontFamily: "var(--dsw-font-mono, monospace)",
|
|
1109
|
+
color: "var(--dsw-text-default, #f1f5f9)"
|
|
1110
|
+
},
|
|
1111
|
+
children: formatMemoryLeafName(item.uri)
|
|
1112
|
+
}
|
|
1113
|
+
)
|
|
1114
|
+
]
|
|
1115
|
+
},
|
|
1116
|
+
`${item.uri}-${idx}`
|
|
1117
|
+
))
|
|
1118
|
+
}
|
|
1119
|
+
)
|
|
1120
|
+
] }),
|
|
1121
|
+
commitError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1122
|
+
"div",
|
|
1123
|
+
{
|
|
1124
|
+
"data-testid": "commit-error-message",
|
|
1125
|
+
style: {
|
|
1126
|
+
marginBottom: "8px",
|
|
1127
|
+
padding: "6px 8px",
|
|
1128
|
+
borderRadius: "4px",
|
|
1129
|
+
backgroundColor: "rgba(248, 113, 113, 0.1)",
|
|
1130
|
+
border: "1px solid var(--dsw-status-error, #f87171)",
|
|
1131
|
+
color: "var(--dsw-status-error, #f87171)",
|
|
1132
|
+
fontSize: "11px",
|
|
1133
|
+
wordBreak: "break-word"
|
|
1134
|
+
},
|
|
1135
|
+
children: commitError
|
|
1136
|
+
}
|
|
1137
|
+
),
|
|
1138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1139
|
+
"button",
|
|
1140
|
+
{
|
|
1141
|
+
type: "button",
|
|
1142
|
+
"data-testid": "commit-now-btn",
|
|
1143
|
+
onClick: () => onCommitNow?.(),
|
|
1144
|
+
disabled: isCommitDisabled,
|
|
1145
|
+
style: {
|
|
1146
|
+
width: "100%",
|
|
1147
|
+
padding: "7px 0",
|
|
1148
|
+
borderRadius: "6px",
|
|
1149
|
+
border: "1px solid var(--dsw-border-default, #475569)",
|
|
1150
|
+
backgroundColor: isCommitting ? "var(--dsw-surface-active, #334155)" : isCommitDisabled ? "rgba(255, 255, 255, 0.03)" : "var(--dsw-surface-base, #1e293b)",
|
|
1151
|
+
color: isCommitDisabled ? "var(--dsw-text-muted, #64748b)" : "var(--dsw-text-default, #f8fafc)",
|
|
1152
|
+
cursor: isCommitDisabled ? "not-allowed" : "pointer",
|
|
1153
|
+
fontWeight: 600,
|
|
1154
|
+
fontSize: "12px",
|
|
1155
|
+
transition: "all 0.15s ease",
|
|
1156
|
+
display: "flex",
|
|
1157
|
+
alignItems: "center",
|
|
1158
|
+
justifyContent: "center",
|
|
1159
|
+
gap: "6px"
|
|
1160
|
+
},
|
|
1161
|
+
children: isCommitting ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1163
|
+
"span",
|
|
1164
|
+
{
|
|
1165
|
+
style: {
|
|
1166
|
+
display: "inline-block",
|
|
1167
|
+
width: "10px",
|
|
1168
|
+
height: "10px",
|
|
1169
|
+
borderRadius: "50%",
|
|
1170
|
+
border: "2px solid var(--dsw-status-warning, #fbbf24)",
|
|
1171
|
+
borderTopColor: "transparent",
|
|
1172
|
+
animation: "ov-spin 1s linear infinite"
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
),
|
|
1176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Committing..." })
|
|
1177
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Commit To Memory Now" })
|
|
1178
|
+
}
|
|
1179
|
+
)
|
|
1180
|
+
]
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/client/OpenVikingStatusChip.tsx
|
|
1186
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1187
|
+
var COMMIT_THRESHOLD = 2e4;
|
|
1188
|
+
function formatPendingTokens(pendingTokens) {
|
|
1189
|
+
const k = Math.round((pendingTokens || 0) / 1e3);
|
|
1190
|
+
return `${k}k pend`;
|
|
1191
|
+
}
|
|
1192
|
+
function formatStatusLabel(isOnline, recalledCount, pendingTokens) {
|
|
1193
|
+
if (!isOnline) {
|
|
1194
|
+
return "OV offline";
|
|
1195
|
+
}
|
|
1196
|
+
return `OV: ${recalledCount} rec \xB7 ${formatPendingTokens(pendingTokens)}`;
|
|
1197
|
+
}
|
|
1198
|
+
function formatTooltipTitle({
|
|
1199
|
+
isOnline,
|
|
1200
|
+
isCommitting = false,
|
|
1201
|
+
recalledCount,
|
|
1202
|
+
pendingTokens
|
|
1203
|
+
}) {
|
|
1204
|
+
if (!isOnline) {
|
|
1205
|
+
return "OpenViking: Offline";
|
|
1206
|
+
}
|
|
1207
|
+
const countLabel = `${recalledCount} recalled`;
|
|
1208
|
+
const tokenLabel = `${(pendingTokens || 0).toLocaleString()} pending tokens`;
|
|
1209
|
+
if (isCommitting) {
|
|
1210
|
+
return `OpenViking: Committing... (${countLabel}, ${tokenLabel})`;
|
|
1211
|
+
}
|
|
1212
|
+
return `OpenViking: Online (${countLabel}, ${tokenLabel})`;
|
|
1213
|
+
}
|
|
1214
|
+
function getStatusIndicatorColor(isOnline, isCommitting = false) {
|
|
1215
|
+
if (!isOnline) {
|
|
1216
|
+
return "var(--dsw-status-error, #f87171)";
|
|
1217
|
+
}
|
|
1218
|
+
if (isCommitting) {
|
|
1219
|
+
return "var(--dsw-status-warning, #fbbf24)";
|
|
1220
|
+
}
|
|
1221
|
+
return "var(--dsw-status-success, #34d399)";
|
|
1222
|
+
}
|
|
1223
|
+
function getStatusGlow(isOnline, isCommitting = false) {
|
|
1224
|
+
if (isOnline && !isCommitting) {
|
|
1225
|
+
return "0 0 6px var(--dsw-status-success, #34d399)";
|
|
1226
|
+
}
|
|
1227
|
+
return "none";
|
|
1228
|
+
}
|
|
1229
|
+
function getFallbackSessionMessages(sessionId) {
|
|
1230
|
+
if (!sessionId || typeof window === "undefined") {
|
|
1231
|
+
return void 0;
|
|
1232
|
+
}
|
|
1233
|
+
const win = window;
|
|
1234
|
+
if (win.__DSH_STORE__?.getState) {
|
|
1235
|
+
const state = win.__DSH_STORE__.getState();
|
|
1236
|
+
return state?.conversations?.[sessionId]?.messages || state?.sessions?.[sessionId]?.messages;
|
|
1237
|
+
}
|
|
1238
|
+
if (win.__DSH_SESSION_MESSAGES__?.[sessionId]) {
|
|
1239
|
+
return win.__DSH_SESSION_MESSAGES__[sessionId];
|
|
1240
|
+
}
|
|
1241
|
+
return void 0;
|
|
1242
|
+
}
|
|
1243
|
+
function OpenVikingStatusChip({
|
|
1244
|
+
sessionId,
|
|
1245
|
+
messages,
|
|
1246
|
+
contextText,
|
|
1247
|
+
client,
|
|
1248
|
+
onCommit,
|
|
1249
|
+
className,
|
|
1250
|
+
initialHealth,
|
|
1251
|
+
initialSessionData,
|
|
1252
|
+
initialOpen = false
|
|
1253
|
+
}) {
|
|
1254
|
+
const [health, setHealth] = (0, import_react2.useState)(
|
|
1255
|
+
initialHealth ?? null
|
|
1256
|
+
);
|
|
1257
|
+
const [sessionData, setSessionData] = (0, import_react2.useState)(
|
|
1258
|
+
initialSessionData ?? null
|
|
1259
|
+
);
|
|
1260
|
+
const [isOpen, setIsOpen] = (0, import_react2.useState)(initialOpen);
|
|
1261
|
+
const [isCommitting, setIsCommitting] = (0, import_react2.useState)(false);
|
|
1262
|
+
const [commitError, setCommitError] = (0, import_react2.useState)(null);
|
|
1263
|
+
const popoverRef = (0, import_react2.useRef)(null);
|
|
1264
|
+
const apiClient = client ?? defaultOpenVikingClient;
|
|
1265
|
+
const fallbackMessages = (0, import_react2.useMemo)(() => {
|
|
1266
|
+
if (messages || contextText) return void 0;
|
|
1267
|
+
return getFallbackSessionMessages(sessionId);
|
|
1268
|
+
}, [sessionId, messages, contextText]);
|
|
1269
|
+
const inputForParser = (0, import_react2.useMemo)(() => {
|
|
1270
|
+
if (contextText && messages) {
|
|
1271
|
+
return [contextText, ...messages];
|
|
1272
|
+
}
|
|
1273
|
+
return contextText ?? messages ?? fallbackMessages;
|
|
1274
|
+
}, [contextText, messages, fallbackMessages]);
|
|
1275
|
+
const recalledResult = (0, import_react2.useMemo)(
|
|
1276
|
+
() => parseRecalledMemories(inputForParser),
|
|
1277
|
+
[inputForParser]
|
|
1278
|
+
);
|
|
1279
|
+
const fetchStatus = (0, import_react2.useCallback)(async () => {
|
|
1280
|
+
try {
|
|
1281
|
+
const healthRes = await apiClient.checkHealth();
|
|
1282
|
+
setHealth(healthRes);
|
|
1283
|
+
if (!healthRes.ok) {
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
const session = await apiClient.fetchSession(sessionId);
|
|
1287
|
+
setSessionData(session);
|
|
1288
|
+
} catch {
|
|
1289
|
+
setHealth({ ok: false });
|
|
1290
|
+
}
|
|
1291
|
+
}, [sessionId, apiClient]);
|
|
1292
|
+
(0, import_react2.useEffect)(() => {
|
|
1293
|
+
fetchStatus();
|
|
1294
|
+
const timer = setInterval(fetchStatus, 15e3);
|
|
1295
|
+
return () => clearInterval(timer);
|
|
1296
|
+
}, [fetchStatus]);
|
|
1297
|
+
(0, import_react2.useEffect)(() => {
|
|
1298
|
+
function handleClickOutside(event) {
|
|
1299
|
+
if (popoverRef.current && !popoverRef.current.contains(event.target)) {
|
|
1300
|
+
setIsOpen(false);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
function handleKeyDown(event) {
|
|
1304
|
+
if (event.key === "Escape") {
|
|
1305
|
+
setIsOpen(false);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
if (isOpen) {
|
|
1309
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1310
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1311
|
+
}
|
|
1312
|
+
return () => {
|
|
1313
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1314
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1315
|
+
};
|
|
1316
|
+
}, [isOpen]);
|
|
1317
|
+
const handleCommitNow = async () => {
|
|
1318
|
+
if (isCommitting) return;
|
|
1319
|
+
setIsCommitting(true);
|
|
1320
|
+
setCommitError(null);
|
|
1321
|
+
try {
|
|
1322
|
+
const res = await apiClient.commitSession(sessionId, {
|
|
1323
|
+
keep_recent_count: 10
|
|
1324
|
+
});
|
|
1325
|
+
if (res.ok) {
|
|
1326
|
+
await fetchStatus();
|
|
1327
|
+
onCommit?.();
|
|
1328
|
+
} else {
|
|
1329
|
+
setCommitError(res.error || "Commit failed");
|
|
1330
|
+
}
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1333
|
+
setCommitError(msg || "Failed to commit session");
|
|
1334
|
+
console.error("[dsh-openviking-status] Failed to commit session:", e);
|
|
1335
|
+
} finally {
|
|
1336
|
+
setIsCommitting(false);
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
const isOnline = health?.ok === true;
|
|
1340
|
+
const pendingTokens = sessionData?.pending_tokens ?? 0;
|
|
1341
|
+
const pendingTokensK = Math.round(pendingTokens / 1e3);
|
|
1342
|
+
const recalledCount = recalledResult.recalledCount;
|
|
1343
|
+
const statusColor = getStatusIndicatorColor(isOnline, isCommitting);
|
|
1344
|
+
const statusGlow = getStatusGlow(isOnline, isCommitting);
|
|
1345
|
+
const tooltipTitle = formatTooltipTitle({
|
|
1346
|
+
isOnline,
|
|
1347
|
+
isCommitting,
|
|
1348
|
+
recalledCount,
|
|
1349
|
+
pendingTokens
|
|
1350
|
+
});
|
|
1351
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1352
|
+
"div",
|
|
1353
|
+
{
|
|
1354
|
+
className,
|
|
1355
|
+
style: {
|
|
1356
|
+
position: "relative",
|
|
1357
|
+
display: "inline-flex",
|
|
1358
|
+
alignItems: "center"
|
|
1359
|
+
},
|
|
1360
|
+
ref: popoverRef,
|
|
1361
|
+
children: [
|
|
1362
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1363
|
+
"button",
|
|
1364
|
+
{
|
|
1365
|
+
type: "button",
|
|
1366
|
+
onClick: () => setIsOpen(!isOpen),
|
|
1367
|
+
"aria-expanded": isOpen,
|
|
1368
|
+
"aria-haspopup": "dialog",
|
|
1369
|
+
style: {
|
|
1370
|
+
display: "inline-flex",
|
|
1371
|
+
alignItems: "center",
|
|
1372
|
+
gap: "6px",
|
|
1373
|
+
height: "26px",
|
|
1374
|
+
padding: "0 8px",
|
|
1375
|
+
fontSize: "12px",
|
|
1376
|
+
fontFamily: "var(--dsw-font-mono, monospace)",
|
|
1377
|
+
borderRadius: "6px",
|
|
1378
|
+
background: "var(--dsw-surface-base, rgba(255, 255, 255, 0.05))",
|
|
1379
|
+
border: "1px solid var(--dsw-border-subtle, rgba(255, 255, 255, 0.1))",
|
|
1380
|
+
color: "var(--dsw-text-muted, #94a3b8)",
|
|
1381
|
+
cursor: "pointer",
|
|
1382
|
+
transition: "all 0.15s ease",
|
|
1383
|
+
userSelect: "none"
|
|
1384
|
+
},
|
|
1385
|
+
title: tooltipTitle,
|
|
1386
|
+
"aria-label": tooltipTitle,
|
|
1387
|
+
children: [
|
|
1388
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1389
|
+
"span",
|
|
1390
|
+
{
|
|
1391
|
+
"data-testid": "status-dot",
|
|
1392
|
+
style: {
|
|
1393
|
+
width: "7px",
|
|
1394
|
+
height: "7px",
|
|
1395
|
+
borderRadius: "50%",
|
|
1396
|
+
backgroundColor: statusColor,
|
|
1397
|
+
boxShadow: statusGlow,
|
|
1398
|
+
flexShrink: 0
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
),
|
|
1402
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1403
|
+
"span",
|
|
1404
|
+
{
|
|
1405
|
+
style: { fontWeight: 600, color: "var(--dsw-text-default, #e2e8f0)" },
|
|
1406
|
+
children: isOnline ? "OV:" : "OV"
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
" ",
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: isOnline ? `${recalledCount} rec \xB7 ${pendingTokensK}k pend` : "offline" })
|
|
1411
|
+
]
|
|
1412
|
+
}
|
|
1413
|
+
),
|
|
1414
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1415
|
+
OpenVikingStatusPopover,
|
|
1416
|
+
{
|
|
1417
|
+
sessionId,
|
|
1418
|
+
health,
|
|
1419
|
+
sessionData,
|
|
1420
|
+
recalledResult,
|
|
1421
|
+
endpoint: apiClient.endpoint,
|
|
1422
|
+
isCommitting,
|
|
1423
|
+
commitError,
|
|
1424
|
+
onCommitNow: handleCommitNow,
|
|
1425
|
+
onClose: () => setIsOpen(false)
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
]
|
|
1429
|
+
}
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/client/index.tsx
|
|
1434
|
+
var name = "@dipertq/dsh-openviking-status";
|
|
1435
|
+
var inject = ["slots"];
|
|
1436
|
+
function apply(ctx) {
|
|
1437
|
+
ctx.effect(
|
|
1438
|
+
() => ctx.slots.inject(
|
|
1439
|
+
"conversation.input.right",
|
|
1440
|
+
() => ctx.slots.register(
|
|
1441
|
+
{
|
|
1442
|
+
name: "conversation.input.right",
|
|
1443
|
+
id: "openviking-status",
|
|
1444
|
+
order: 50,
|
|
1445
|
+
label: "OpenViking"
|
|
1446
|
+
},
|
|
1447
|
+
OpenVikingStatusChip
|
|
1448
|
+
)
|
|
1449
|
+
),
|
|
1450
|
+
"openviking-status: composer chip"
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
return module.exports;
|
|
1454
|
+
},
|
|
1455
|
+
});
|
|
1456
|
+
//# sourceMappingURL=client.cjs.map
|