@dipertq/dsh-openviking-status 0.2.2 → 0.3.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 +1 -1
- package/lib/client.cjs +656 -12
- package/lib/client.cjs.map +1 -1
- package/lib/client.d.cts +219 -3
- package/lib/client.js +656 -12
- package/lib/client.js.map +1 -1
- package/lib/index.js +159 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -16,6 +16,71 @@ import React2, {
|
|
|
16
16
|
import ReactDOM from "react-dom";
|
|
17
17
|
|
|
18
18
|
// src/client/api.ts
|
|
19
|
+
var TASKS_LIMIT_CAP = 200;
|
|
20
|
+
function normalizeExtractionTask(raw) {
|
|
21
|
+
if (!raw || typeof raw !== "object") return null;
|
|
22
|
+
const taskId = typeof raw.task_id === "string" && raw.task_id || typeof raw.id === "string" && raw.id || "";
|
|
23
|
+
if (!taskId) return null;
|
|
24
|
+
const statusRaw = typeof raw.status === "string" ? raw.status : "";
|
|
25
|
+
const status = statusRaw === "running" || statusRaw === "pending" || statusRaw === "completed" || statusRaw === "failed" ? statusRaw : "pending";
|
|
26
|
+
const result = raw.result ?? {};
|
|
27
|
+
const extracted = result.memories_extracted ?? {};
|
|
28
|
+
const numberOf = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
29
|
+
const tokenUsageRaw = result.token_usage ?? raw.token_usage;
|
|
30
|
+
const tokenUsage = typeof tokenUsageRaw === "number" ? tokenUsageRaw : numberOf(
|
|
31
|
+
tokenUsageRaw?.total_tokens
|
|
32
|
+
);
|
|
33
|
+
const errorRaw = raw.error;
|
|
34
|
+
const errorMsg = typeof errorRaw === "string" ? errorRaw : typeof errorRaw?.message === "string" ? errorRaw.message : void 0;
|
|
35
|
+
return {
|
|
36
|
+
task_id: taskId,
|
|
37
|
+
status,
|
|
38
|
+
resource_id: typeof raw.resource_id === "string" ? raw.resource_id : void 0,
|
|
39
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
40
|
+
updated_at: typeof raw.updated_at === "string" ? raw.updated_at : void 0,
|
|
41
|
+
memory_write: numberOf(extracted.memory_write ?? extracted.written),
|
|
42
|
+
memory_edit: numberOf(extracted.memory_edit ?? extracted.edited),
|
|
43
|
+
token_usage: tokenUsage,
|
|
44
|
+
error: errorMsg
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function computeBreakdown(tasks) {
|
|
48
|
+
const b = {
|
|
49
|
+
running: 0,
|
|
50
|
+
pending: 0,
|
|
51
|
+
completed: 0,
|
|
52
|
+
failed: 0,
|
|
53
|
+
total: tasks.length,
|
|
54
|
+
firstRunning: null,
|
|
55
|
+
lastCompleted: null,
|
|
56
|
+
lastFailed: null
|
|
57
|
+
};
|
|
58
|
+
for (const t of tasks) {
|
|
59
|
+
if (t.status === "running") {
|
|
60
|
+
b.running += 1;
|
|
61
|
+
if (!b.firstRunning) b.firstRunning = t;
|
|
62
|
+
} else if (t.status === "pending") b.pending += 1;
|
|
63
|
+
else if (t.status === "completed") {
|
|
64
|
+
b.completed += 1;
|
|
65
|
+
if (!b.lastCompleted) b.lastCompleted = t;
|
|
66
|
+
} else if (t.status === "failed") {
|
|
67
|
+
b.failed += 1;
|
|
68
|
+
if (!b.lastFailed) b.lastFailed = t;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return b;
|
|
72
|
+
}
|
|
73
|
+
function normalizeExecutionEvent(raw) {
|
|
74
|
+
return {
|
|
75
|
+
seq: typeof raw.seq === "number" ? raw.seq : void 0,
|
|
76
|
+
recorded_at: typeof raw.recorded_at === "string" ? raw.recorded_at : void 0,
|
|
77
|
+
kind: typeof raw.kind === "string" ? raw.kind : void 0,
|
|
78
|
+
status: typeof raw.status === "string" ? raw.status : void 0,
|
|
79
|
+
stage: typeof raw.stage === "string" ? raw.stage : null,
|
|
80
|
+
operation: typeof raw.operation === "string" ? raw.operation : null,
|
|
81
|
+
error: typeof raw.error === "string" ? raw.error : null
|
|
82
|
+
};
|
|
83
|
+
}
|
|
19
84
|
var DEFAULT_OPENVIKING_ENDPOINT = "http://127.0.0.1:1933";
|
|
20
85
|
var PROXY_OPENVIKING_ENDPOINT = "/openviking-status/api";
|
|
21
86
|
function resolveEndpoint(endpoint) {
|
|
@@ -324,7 +389,9 @@ var OpenVikingClient = class {
|
|
|
324
389
|
const data = await res.json().catch(() => ({}));
|
|
325
390
|
return {
|
|
326
391
|
ok: data.ok === true,
|
|
327
|
-
error: typeof data.error === "string" ? data.error : void 0
|
|
392
|
+
error: typeof data.error === "string" ? data.error : void 0,
|
|
393
|
+
task_id: typeof data.task_id === "string" ? data.task_id : void 0,
|
|
394
|
+
resource_id: typeof data.resource_id === "string" ? data.resource_id : void 0
|
|
328
395
|
};
|
|
329
396
|
} catch (err) {
|
|
330
397
|
return {
|
|
@@ -356,7 +423,14 @@ var OpenVikingClient = class {
|
|
|
356
423
|
return { ok: false, error: String(errorMsg) };
|
|
357
424
|
}
|
|
358
425
|
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
359
|
-
|
|
426
|
+
const okBody = await res.json().catch(() => ({}));
|
|
427
|
+
const container = okBody.result ?? okBody.data ?? okBody;
|
|
428
|
+
const taskId = container.task_id ?? container.id;
|
|
429
|
+
return {
|
|
430
|
+
ok: true,
|
|
431
|
+
task_id: typeof taskId === "string" && taskId.trim() ? taskId.trim() : void 0,
|
|
432
|
+
resource_id: candidateId
|
|
433
|
+
};
|
|
360
434
|
} catch (err) {
|
|
361
435
|
return {
|
|
362
436
|
ok: false,
|
|
@@ -366,7 +440,193 @@ var OpenVikingClient = class {
|
|
|
366
440
|
}
|
|
367
441
|
return { ok: false, error: lastError };
|
|
368
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Список задач Phase 2 (Memory Extraction) текущей сессии со сводной
|
|
445
|
+
* разбивкой по статусам.
|
|
446
|
+
*
|
|
447
|
+
* Через прокси: один запрос `GET /tasks?session=<id>`, разбивка считается
|
|
448
|
+
* здесь. Напрямую: разрешаем `resource_id` через кандидатов и запрашиваем
|
|
449
|
+
* `GET /api/v1/tasks?resource_id=<id>&task_type=session_commit&limit=200`.
|
|
450
|
+
*/
|
|
451
|
+
async listTasks(sessionId) {
|
|
452
|
+
if (!sessionId || !sessionId.trim()) {
|
|
453
|
+
return { status: "missing" };
|
|
454
|
+
}
|
|
455
|
+
if (this.isProxy()) {
|
|
456
|
+
try {
|
|
457
|
+
const res = await fetch(
|
|
458
|
+
`${this.endpoint}/tasks?session=${encodeURIComponent(
|
|
459
|
+
sessionId.trim()
|
|
460
|
+
)}&limit=${TASKS_LIMIT_CAP}`,
|
|
461
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
462
|
+
);
|
|
463
|
+
if (res.status === 401 || res.status === 403) {
|
|
464
|
+
return { status: "unauthorized" };
|
|
465
|
+
}
|
|
466
|
+
if (!res.ok) {
|
|
467
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
468
|
+
}
|
|
469
|
+
const data = await res.json();
|
|
470
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
471
|
+
if (data.status === "missing") return { status: "missing" };
|
|
472
|
+
if (data.status === "unreachable")
|
|
473
|
+
return {
|
|
474
|
+
status: "unreachable",
|
|
475
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
476
|
+
};
|
|
477
|
+
if (data.status === "error")
|
|
478
|
+
return {
|
|
479
|
+
status: "error",
|
|
480
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
481
|
+
};
|
|
482
|
+
const tasks = normalizeTaskList(data.tasks);
|
|
483
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
484
|
+
} catch (err) {
|
|
485
|
+
return {
|
|
486
|
+
status: "unreachable",
|
|
487
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const candidates = this.getCandidateSessionIds(sessionId);
|
|
492
|
+
let resourceId = null;
|
|
493
|
+
for (const candidateId of candidates) {
|
|
494
|
+
try {
|
|
495
|
+
const probe = await fetch(
|
|
496
|
+
`${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}`,
|
|
497
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
498
|
+
);
|
|
499
|
+
if (probe.status === 404) continue;
|
|
500
|
+
if (probe.status === 401 || probe.status === 403) {
|
|
501
|
+
return { status: "unauthorized" };
|
|
502
|
+
}
|
|
503
|
+
if (probe.ok) {
|
|
504
|
+
resourceId = candidateId;
|
|
505
|
+
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
} catch (err) {
|
|
509
|
+
return {
|
|
510
|
+
status: "unreachable",
|
|
511
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (!resourceId) return { status: "missing" };
|
|
516
|
+
try {
|
|
517
|
+
const query = new URLSearchParams({
|
|
518
|
+
resource_id: resourceId,
|
|
519
|
+
task_type: "session_commit",
|
|
520
|
+
limit: String(TASKS_LIMIT_CAP)
|
|
521
|
+
});
|
|
522
|
+
const res = await fetch(
|
|
523
|
+
`${this.endpoint}/api/v1/tasks?${query.toString()}`,
|
|
524
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
525
|
+
);
|
|
526
|
+
if (res.status === 401 || res.status === 403) {
|
|
527
|
+
return { status: "unauthorized" };
|
|
528
|
+
}
|
|
529
|
+
if (!res.ok) {
|
|
530
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
531
|
+
}
|
|
532
|
+
const data = await res.json();
|
|
533
|
+
const items = data?.items ?? data?.tasks ?? data?.result?.items ?? (Array.isArray(data) ? data : []);
|
|
534
|
+
const tasks = normalizeTaskList(items);
|
|
535
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
536
|
+
} catch (err) {
|
|
537
|
+
return {
|
|
538
|
+
status: "unreachable",
|
|
539
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Лениво загрузить одну задачу с лентой `execution_events` для «Show log».
|
|
545
|
+
*/
|
|
546
|
+
async fetchTaskEvents(taskId) {
|
|
547
|
+
if (!taskId || !taskId.trim()) {
|
|
548
|
+
return { status: "missing" };
|
|
549
|
+
}
|
|
550
|
+
const buildOk = (taskRaw) => {
|
|
551
|
+
const task = normalizeExtractionTask(taskRaw);
|
|
552
|
+
const eventsContainer = taskRaw.execution_events ?? {};
|
|
553
|
+
const rawEvents = eventsContainer.items ?? (Array.isArray(taskRaw.execution_events) ? taskRaw.execution_events : []);
|
|
554
|
+
const events = (Array.isArray(rawEvents) ? rawEvents : []).filter(
|
|
555
|
+
(e) => !!e && typeof e === "object"
|
|
556
|
+
).map(normalizeExecutionEvent);
|
|
557
|
+
if (!task) return { status: "error", detail: "malformed task" };
|
|
558
|
+
return { status: "ok", task, events };
|
|
559
|
+
};
|
|
560
|
+
if (this.isProxy()) {
|
|
561
|
+
try {
|
|
562
|
+
const res = await fetch(
|
|
563
|
+
`${this.endpoint}/task?id=${encodeURIComponent(
|
|
564
|
+
taskId.trim()
|
|
565
|
+
)}&events=1`,
|
|
566
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
567
|
+
);
|
|
568
|
+
if (res.status === 401 || res.status === 403) {
|
|
569
|
+
return { status: "unauthorized" };
|
|
570
|
+
}
|
|
571
|
+
if (!res.ok) {
|
|
572
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
573
|
+
}
|
|
574
|
+
const data = await res.json();
|
|
575
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
576
|
+
if (data.status === "missing") return { status: "missing" };
|
|
577
|
+
if (data.status === "unreachable")
|
|
578
|
+
return {
|
|
579
|
+
status: "unreachable",
|
|
580
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
581
|
+
};
|
|
582
|
+
if (data.status === "error")
|
|
583
|
+
return {
|
|
584
|
+
status: "error",
|
|
585
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
586
|
+
};
|
|
587
|
+
return buildOk(data.task ?? {});
|
|
588
|
+
} catch (err) {
|
|
589
|
+
return {
|
|
590
|
+
status: "unreachable",
|
|
591
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
try {
|
|
596
|
+
const res = await fetch(
|
|
597
|
+
`${this.endpoint}/api/v1/tasks/${encodeURIComponent(
|
|
598
|
+
taskId.trim()
|
|
599
|
+
)}?include_events=true`,
|
|
600
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
601
|
+
);
|
|
602
|
+
if (res.status === 404) return { status: "missing" };
|
|
603
|
+
if (res.status === 401 || res.status === 403) {
|
|
604
|
+
return { status: "unauthorized" };
|
|
605
|
+
}
|
|
606
|
+
if (!res.ok) {
|
|
607
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
608
|
+
}
|
|
609
|
+
const data = await res.json();
|
|
610
|
+
const taskRaw = data?.result ?? data?.data ?? data;
|
|
611
|
+
return buildOk(taskRaw);
|
|
612
|
+
} catch (err) {
|
|
613
|
+
return {
|
|
614
|
+
status: "unreachable",
|
|
615
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
}
|
|
369
619
|
};
|
|
620
|
+
function normalizeTaskList(raw) {
|
|
621
|
+
if (!Array.isArray(raw)) return [];
|
|
622
|
+
const tasks = [];
|
|
623
|
+
for (const entry of raw) {
|
|
624
|
+
if (!entry || typeof entry !== "object") continue;
|
|
625
|
+
const task = normalizeExtractionTask(entry);
|
|
626
|
+
if (task) tasks.push(task);
|
|
627
|
+
}
|
|
628
|
+
return tasks;
|
|
629
|
+
}
|
|
370
630
|
var defaultOpenVikingClient = new OpenVikingClient();
|
|
371
631
|
function checkHealth(endpoint, apiKey) {
|
|
372
632
|
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
@@ -740,6 +1000,39 @@ function dshIcon(name2) {
|
|
|
740
1000
|
return null;
|
|
741
1001
|
}
|
|
742
1002
|
}
|
|
1003
|
+
function formatDuration(startIso, endIso) {
|
|
1004
|
+
if (!startIso || !endIso) return void 0;
|
|
1005
|
+
const start = new Date(startIso).getTime();
|
|
1006
|
+
const end = new Date(endIso).getTime();
|
|
1007
|
+
if (isNaN(start) || isNaN(end) || end < start) return void 0;
|
|
1008
|
+
const ms = end - start;
|
|
1009
|
+
const sec = ms / 1e3;
|
|
1010
|
+
if (sec < 60) {
|
|
1011
|
+
return `${sec < 10 ? sec.toFixed(1) : Math.round(sec)}s`;
|
|
1012
|
+
}
|
|
1013
|
+
const min = Math.floor(sec / 60);
|
|
1014
|
+
const rem = Math.round(sec % 60);
|
|
1015
|
+
return `${min}m ${rem}s`;
|
|
1016
|
+
}
|
|
1017
|
+
function formatExtractionSummary(task, now = Date.now()) {
|
|
1018
|
+
const write = task.memory_write ?? 0;
|
|
1019
|
+
const edit = task.memory_edit ?? 0;
|
|
1020
|
+
const parts = [`${write} written, ${edit} edited`];
|
|
1021
|
+
const duration = formatDuration(task.created_at, task.updated_at);
|
|
1022
|
+
if (duration) parts.push(duration);
|
|
1023
|
+
const rel = formatRelativeTime(task.updated_at, now);
|
|
1024
|
+
if (rel) parts.push(rel);
|
|
1025
|
+
return parts.join(" \xB7 ");
|
|
1026
|
+
}
|
|
1027
|
+
function formatBacklog(breakdown) {
|
|
1028
|
+
if (!breakdown) return null;
|
|
1029
|
+
const { pending, failed } = breakdown;
|
|
1030
|
+
if (pending <= 0 && failed <= 0) return null;
|
|
1031
|
+
const parts = [];
|
|
1032
|
+
if (pending > 0) parts.push(`${pending} pending`);
|
|
1033
|
+
if (failed > 0) parts.push(`${failed} failed`);
|
|
1034
|
+
return parts.join(" / ");
|
|
1035
|
+
}
|
|
743
1036
|
function getProgressBarPercent(pendingTokens, threshold = COMMIT_THRESHOLD) {
|
|
744
1037
|
if (!threshold || threshold <= 0) return 0;
|
|
745
1038
|
const ratio = (pendingTokens || 0) / threshold;
|
|
@@ -806,6 +1099,229 @@ function handleEscapeKey(event, onClose) {
|
|
|
806
1099
|
}
|
|
807
1100
|
return false;
|
|
808
1101
|
}
|
|
1102
|
+
function ExtractionSection({
|
|
1103
|
+
breakdown,
|
|
1104
|
+
client
|
|
1105
|
+
}) {
|
|
1106
|
+
const [showLog, setShowLog] = useState(false);
|
|
1107
|
+
const [events, setEvents] = useState(null);
|
|
1108
|
+
const [logError, setLogError] = useState(null);
|
|
1109
|
+
const [loadingLog, setLoadingLog] = useState(false);
|
|
1110
|
+
if (!breakdown) return null;
|
|
1111
|
+
const isRunning = breakdown.running >= 1;
|
|
1112
|
+
const lastCompleted = breakdown.lastCompleted;
|
|
1113
|
+
const lastFailed = breakdown.lastFailed;
|
|
1114
|
+
const backlog = formatBacklog(breakdown);
|
|
1115
|
+
const failedActive = !isRunning && breakdown.failed >= 1;
|
|
1116
|
+
const logTaskId = (isRunning ? breakdown.firstRunning?.task_id : null) ?? (failedActive ? lastFailed?.task_id : null) ?? lastCompleted?.task_id ?? lastFailed?.task_id ?? null;
|
|
1117
|
+
const mutedStyle = { color: themeVar("labelTertiary") };
|
|
1118
|
+
const toggleLog = useCallback(async () => {
|
|
1119
|
+
const next = !showLog;
|
|
1120
|
+
setShowLog(next);
|
|
1121
|
+
if (next && events === null && logTaskId && client) {
|
|
1122
|
+
setLoadingLog(true);
|
|
1123
|
+
setLogError(null);
|
|
1124
|
+
const res = await client.fetchTaskEvents(logTaskId);
|
|
1125
|
+
setLoadingLog(false);
|
|
1126
|
+
if (res.status === "ok") {
|
|
1127
|
+
setEvents(res.events);
|
|
1128
|
+
} else {
|
|
1129
|
+
setLogError(
|
|
1130
|
+
res.status === "unauthorized" ? "No access to task log" : "Could not load log"
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}, [showLog, events, logTaskId, client]);
|
|
1135
|
+
if (!isRunning && !failedActive && !lastCompleted && !backlog) {
|
|
1136
|
+
return null;
|
|
1137
|
+
}
|
|
1138
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": "extraction-section", style: { marginBottom: "12px" }, children: [
|
|
1139
|
+
/* @__PURE__ */ jsx(
|
|
1140
|
+
"div",
|
|
1141
|
+
{
|
|
1142
|
+
style: {
|
|
1143
|
+
marginBottom: "4px",
|
|
1144
|
+
color: themeVar("labelPrimary"),
|
|
1145
|
+
fontWeight: 500
|
|
1146
|
+
},
|
|
1147
|
+
children: "Memory Extraction"
|
|
1148
|
+
}
|
|
1149
|
+
),
|
|
1150
|
+
isRunning ? /* @__PURE__ */ jsxs("div", { children: [
|
|
1151
|
+
/* @__PURE__ */ jsxs(
|
|
1152
|
+
"div",
|
|
1153
|
+
{
|
|
1154
|
+
style: {
|
|
1155
|
+
display: "flex",
|
|
1156
|
+
alignItems: "center",
|
|
1157
|
+
gap: "6px",
|
|
1158
|
+
marginBottom: "4px"
|
|
1159
|
+
},
|
|
1160
|
+
children: [
|
|
1161
|
+
/* @__PURE__ */ jsx(
|
|
1162
|
+
"span",
|
|
1163
|
+
{
|
|
1164
|
+
"data-testid": "extraction-status-dot",
|
|
1165
|
+
"aria-hidden": "true",
|
|
1166
|
+
style: {
|
|
1167
|
+
width: "6px",
|
|
1168
|
+
height: "6px",
|
|
1169
|
+
borderRadius: "50%",
|
|
1170
|
+
background: themeVar("stateSuccess"),
|
|
1171
|
+
animation: "ov-pulse 1.2s ease-in-out infinite",
|
|
1172
|
+
flexShrink: 0
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
),
|
|
1176
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "extraction-running-label", children: "Extracting\u2026" })
|
|
1177
|
+
]
|
|
1178
|
+
}
|
|
1179
|
+
),
|
|
1180
|
+
/* @__PURE__ */ jsx(
|
|
1181
|
+
"div",
|
|
1182
|
+
{
|
|
1183
|
+
"data-testid": "extraction-indeterminate-track",
|
|
1184
|
+
style: {
|
|
1185
|
+
width: "100%",
|
|
1186
|
+
height: "6px",
|
|
1187
|
+
borderRadius: "3px",
|
|
1188
|
+
backgroundColor: themeVar("insetSurface"),
|
|
1189
|
+
overflow: "hidden",
|
|
1190
|
+
position: "relative"
|
|
1191
|
+
},
|
|
1192
|
+
children: /* @__PURE__ */ jsx(
|
|
1193
|
+
"div",
|
|
1194
|
+
{
|
|
1195
|
+
"data-testid": "extraction-indeterminate-fill",
|
|
1196
|
+
style: {
|
|
1197
|
+
position: "absolute",
|
|
1198
|
+
left: 0,
|
|
1199
|
+
top: 0,
|
|
1200
|
+
height: "100%",
|
|
1201
|
+
width: "40%",
|
|
1202
|
+
borderRadius: "3px",
|
|
1203
|
+
backgroundColor: themeVar("stateSuccess"),
|
|
1204
|
+
animation: "ov-indeterminate 1.2s ease-in-out infinite"
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
)
|
|
1208
|
+
}
|
|
1209
|
+
)
|
|
1210
|
+
] }) : failedActive && lastFailed ? /* @__PURE__ */ jsxs(
|
|
1211
|
+
"div",
|
|
1212
|
+
{
|
|
1213
|
+
"data-testid": "extraction-failed-line",
|
|
1214
|
+
style: {
|
|
1215
|
+
display: "flex",
|
|
1216
|
+
alignItems: "center",
|
|
1217
|
+
gap: "6px",
|
|
1218
|
+
color: themeVar("stateWarning")
|
|
1219
|
+
},
|
|
1220
|
+
children: [
|
|
1221
|
+
/* @__PURE__ */ jsx(
|
|
1222
|
+
"span",
|
|
1223
|
+
{
|
|
1224
|
+
"aria-hidden": "true",
|
|
1225
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
1226
|
+
children: /* @__PURE__ */ jsx(WarningGlyph, { size: 12, testId: "extraction-warning-glyph" })
|
|
1227
|
+
}
|
|
1228
|
+
),
|
|
1229
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "extraction-failed-text", children: lastFailed.error || "extraction failed" })
|
|
1230
|
+
]
|
|
1231
|
+
}
|
|
1232
|
+
) : lastCompleted ? /* @__PURE__ */ jsxs(
|
|
1233
|
+
"div",
|
|
1234
|
+
{
|
|
1235
|
+
style: {
|
|
1236
|
+
display: "flex",
|
|
1237
|
+
alignItems: "center",
|
|
1238
|
+
gap: "6px"
|
|
1239
|
+
},
|
|
1240
|
+
children: [
|
|
1241
|
+
/* @__PURE__ */ jsx(
|
|
1242
|
+
"span",
|
|
1243
|
+
{
|
|
1244
|
+
"data-testid": "extraction-status-dot",
|
|
1245
|
+
"aria-hidden": "true",
|
|
1246
|
+
style: {
|
|
1247
|
+
width: "6px",
|
|
1248
|
+
height: "6px",
|
|
1249
|
+
borderRadius: "50%",
|
|
1250
|
+
background: themeVar("stateSuccess"),
|
|
1251
|
+
flexShrink: 0
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
),
|
|
1255
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "extraction-last-line", children: formatExtractionSummary(lastCompleted) })
|
|
1256
|
+
]
|
|
1257
|
+
}
|
|
1258
|
+
) : null,
|
|
1259
|
+
backlog && /* @__PURE__ */ jsx(
|
|
1260
|
+
"div",
|
|
1261
|
+
{
|
|
1262
|
+
"data-testid": "extraction-backlog-line",
|
|
1263
|
+
style: { ...mutedStyle, marginTop: "4px" },
|
|
1264
|
+
children: backlog
|
|
1265
|
+
}
|
|
1266
|
+
),
|
|
1267
|
+
logTaskId && client && /* @__PURE__ */ jsxs("div", { style: { marginTop: "6px" }, children: [
|
|
1268
|
+
/* @__PURE__ */ jsx(
|
|
1269
|
+
"button",
|
|
1270
|
+
{
|
|
1271
|
+
type: "button",
|
|
1272
|
+
"data-testid": "extraction-show-log-btn",
|
|
1273
|
+
onClick: () => void toggleLog(),
|
|
1274
|
+
style: {
|
|
1275
|
+
background: "none",
|
|
1276
|
+
border: "none",
|
|
1277
|
+
padding: 0,
|
|
1278
|
+
cursor: "pointer",
|
|
1279
|
+
font: "inherit",
|
|
1280
|
+
color: themeVar("labelTertiary"),
|
|
1281
|
+
textDecoration: "underline"
|
|
1282
|
+
},
|
|
1283
|
+
"aria-expanded": showLog,
|
|
1284
|
+
children: showLog ? "Hide log" : "Show log"
|
|
1285
|
+
}
|
|
1286
|
+
),
|
|
1287
|
+
showLog && /* @__PURE__ */ jsxs(
|
|
1288
|
+
"div",
|
|
1289
|
+
{
|
|
1290
|
+
"data-testid": "extraction-log",
|
|
1291
|
+
style: {
|
|
1292
|
+
marginTop: "6px",
|
|
1293
|
+
display: "flex",
|
|
1294
|
+
flexDirection: "column",
|
|
1295
|
+
gap: "2px",
|
|
1296
|
+
maxHeight: "120px",
|
|
1297
|
+
overflowY: "auto"
|
|
1298
|
+
},
|
|
1299
|
+
children: [
|
|
1300
|
+
loadingLog && /* @__PURE__ */ jsx("div", { "data-testid": "extraction-log-loading", style: mutedStyle, children: "Loading\u2026" }),
|
|
1301
|
+
logError && /* @__PURE__ */ jsx("div", { "data-testid": "extraction-log-error", style: mutedStyle, children: logError }),
|
|
1302
|
+
events && events.length === 0 && !loadingLog && !logError && /* @__PURE__ */ jsx("div", { "data-testid": "extraction-log-empty", style: mutedStyle, children: "No events" }),
|
|
1303
|
+
events?.map((ev, idx) => /* @__PURE__ */ jsxs(
|
|
1304
|
+
"div",
|
|
1305
|
+
{
|
|
1306
|
+
"data-testid": "extraction-log-event",
|
|
1307
|
+
style: {
|
|
1308
|
+
display: "flex",
|
|
1309
|
+
justifyContent: "space-between",
|
|
1310
|
+
gap: "8px"
|
|
1311
|
+
},
|
|
1312
|
+
children: [
|
|
1313
|
+
/* @__PURE__ */ jsx("span", { children: ev.status || ev.kind || "event" }),
|
|
1314
|
+
/* @__PURE__ */ jsx("span", { style: mutedStyle, children: formatRelativeTime(ev.recorded_at) || "" })
|
|
1315
|
+
]
|
|
1316
|
+
},
|
|
1317
|
+
`${ev.seq ?? idx}-${ev.recorded_at ?? idx}`
|
|
1318
|
+
))
|
|
1319
|
+
]
|
|
1320
|
+
}
|
|
1321
|
+
)
|
|
1322
|
+
] })
|
|
1323
|
+
] });
|
|
1324
|
+
}
|
|
809
1325
|
function OpenVikingStatusPopover({
|
|
810
1326
|
sessionId,
|
|
811
1327
|
health,
|
|
@@ -815,6 +1331,8 @@ function OpenVikingStatusPopover({
|
|
|
815
1331
|
endpoint,
|
|
816
1332
|
isCommitting = false,
|
|
817
1333
|
commitError = null,
|
|
1334
|
+
breakdown = null,
|
|
1335
|
+
client,
|
|
818
1336
|
onCommitNow,
|
|
819
1337
|
onClose,
|
|
820
1338
|
className,
|
|
@@ -895,7 +1413,7 @@ function OpenVikingStatusPopover({
|
|
|
895
1413
|
...style
|
|
896
1414
|
},
|
|
897
1415
|
children: [
|
|
898
|
-
/* @__PURE__ */ jsx("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
|
|
1416
|
+
/* @__PURE__ */ jsx("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes ov-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } } @keyframes ov-indeterminate { 0% { left: -40%; } 100% { left: 100%; } }` }),
|
|
899
1417
|
/* @__PURE__ */ jsxs(
|
|
900
1418
|
"div",
|
|
901
1419
|
{
|
|
@@ -1178,6 +1696,7 @@ function OpenVikingStatusPopover({
|
|
|
1178
1696
|
}
|
|
1179
1697
|
)
|
|
1180
1698
|
] }),
|
|
1699
|
+
/* @__PURE__ */ jsx(ExtractionSection, { breakdown, client }),
|
|
1181
1700
|
commitError && /* @__PURE__ */ jsx(
|
|
1182
1701
|
"div",
|
|
1183
1702
|
{
|
|
@@ -1235,6 +1754,77 @@ function OpenVikingStatusPopover({
|
|
|
1235
1754
|
// src/client/OpenVikingStatusChip.tsx
|
|
1236
1755
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1237
1756
|
var COMMIT_THRESHOLD = 2e4;
|
|
1757
|
+
var POLL_INTERVAL_IDLE_MS = 15e3;
|
|
1758
|
+
var POLL_INTERVAL_ACTIVE_MS = 2500;
|
|
1759
|
+
function getChipDotState({
|
|
1760
|
+
isOnline,
|
|
1761
|
+
sessionUnreadable,
|
|
1762
|
+
breakdown
|
|
1763
|
+
}) {
|
|
1764
|
+
if (!isOnline) return "offline";
|
|
1765
|
+
if (sessionUnreadable) return "session-unreadable";
|
|
1766
|
+
if (breakdown && breakdown.running >= 1) return "busy";
|
|
1767
|
+
if (breakdown && breakdown.failed >= 1) return "extraction-failed";
|
|
1768
|
+
return "online-idle";
|
|
1769
|
+
}
|
|
1770
|
+
function WarningGlyph({
|
|
1771
|
+
size = 11,
|
|
1772
|
+
testId = "extraction-warning-glyph"
|
|
1773
|
+
}) {
|
|
1774
|
+
return /* @__PURE__ */ jsxs2(
|
|
1775
|
+
"svg",
|
|
1776
|
+
{
|
|
1777
|
+
"data-testid": testId,
|
|
1778
|
+
width: size,
|
|
1779
|
+
height: size,
|
|
1780
|
+
viewBox: "0 0 16 16",
|
|
1781
|
+
fill: "none",
|
|
1782
|
+
"aria-hidden": "true",
|
|
1783
|
+
style: { flexShrink: 0, display: "block" },
|
|
1784
|
+
children: [
|
|
1785
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1786
|
+
/* @__PURE__ */ jsx2(
|
|
1787
|
+
"path",
|
|
1788
|
+
{
|
|
1789
|
+
d: "M8 5v3.5",
|
|
1790
|
+
stroke: "currentColor",
|
|
1791
|
+
strokeWidth: "1.5",
|
|
1792
|
+
strokeLinecap: "round"
|
|
1793
|
+
}
|
|
1794
|
+
),
|
|
1795
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "11", r: "0.9", fill: "currentColor" })
|
|
1796
|
+
]
|
|
1797
|
+
}
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
function seedRunningTask(prev, taskId, resourceId) {
|
|
1801
|
+
const existing = prev && prev.status === "ok" ? prev.tasks : [];
|
|
1802
|
+
if (existing.some((t) => t.task_id === taskId)) {
|
|
1803
|
+
return prev;
|
|
1804
|
+
}
|
|
1805
|
+
const seeded = {
|
|
1806
|
+
task_id: taskId,
|
|
1807
|
+
status: "running",
|
|
1808
|
+
resource_id: resourceId,
|
|
1809
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1810
|
+
};
|
|
1811
|
+
const tasks = [seeded, ...existing];
|
|
1812
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
1813
|
+
}
|
|
1814
|
+
function getDotColorForState(state) {
|
|
1815
|
+
switch (state) {
|
|
1816
|
+
case "offline":
|
|
1817
|
+
return themeVar("stateError");
|
|
1818
|
+
case "session-unreadable":
|
|
1819
|
+
case "extraction-failed":
|
|
1820
|
+
return themeVar("stateWarning");
|
|
1821
|
+
case "busy":
|
|
1822
|
+
return themeVar("stateSuccess");
|
|
1823
|
+
case "online-idle":
|
|
1824
|
+
default:
|
|
1825
|
+
return themeVar("stateSuccess");
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1238
1828
|
function formatPendingTokens(pendingTokens) {
|
|
1239
1829
|
const k = Math.round((pendingTokens || 0) / 1e3);
|
|
1240
1830
|
return `${k}k pend`;
|
|
@@ -1337,6 +1927,7 @@ function StatusChipView({
|
|
|
1337
1927
|
initialHealth,
|
|
1338
1928
|
initialSessionData,
|
|
1339
1929
|
initialSessionRead,
|
|
1930
|
+
initialTasksRead,
|
|
1340
1931
|
initialOpen = false
|
|
1341
1932
|
}) {
|
|
1342
1933
|
const [health, setHealth] = useState2(
|
|
@@ -1345,6 +1936,9 @@ function StatusChipView({
|
|
|
1345
1936
|
const [sessionRead, setSessionRead] = useState2(
|
|
1346
1937
|
initialSessionRead ?? (initialSessionData ? { status: "ok", session: initialSessionData } : null)
|
|
1347
1938
|
);
|
|
1939
|
+
const [tasksRead, setTasksRead] = useState2(
|
|
1940
|
+
initialTasksRead ?? null
|
|
1941
|
+
);
|
|
1348
1942
|
const [isOpen, setIsOpen] = useState2(initialOpen);
|
|
1349
1943
|
const [isCommitting, setIsCommitting] = useState2(false);
|
|
1350
1944
|
const [commitError, setCommitError] = useState2(null);
|
|
@@ -1380,16 +1974,24 @@ function StatusChipView({
|
|
|
1380
1974
|
if (!healthRes.ok) {
|
|
1381
1975
|
return;
|
|
1382
1976
|
}
|
|
1383
|
-
|
|
1977
|
+
const [session, tasks] = await Promise.all([
|
|
1978
|
+
apiClient.readSession(sessionId),
|
|
1979
|
+
apiClient.listTasks(sessionId)
|
|
1980
|
+
]);
|
|
1981
|
+
setSessionRead(session);
|
|
1982
|
+
setTasksRead(tasks);
|
|
1384
1983
|
} catch {
|
|
1385
1984
|
setHealth({ ok: false });
|
|
1386
1985
|
}
|
|
1387
1986
|
}, [sessionId, apiClient]);
|
|
1987
|
+
const breakdown = tasksRead?.status === "ok" ? tasksRead.breakdown : null;
|
|
1988
|
+
const hasRunning = (breakdown?.running ?? 0) >= 1;
|
|
1388
1989
|
useEffect2(() => {
|
|
1389
1990
|
fetchStatus();
|
|
1390
|
-
const
|
|
1991
|
+
const interval = hasRunning ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
|
|
1992
|
+
const timer = setInterval(fetchStatus, interval);
|
|
1391
1993
|
return () => clearInterval(timer);
|
|
1392
|
-
}, [fetchStatus]);
|
|
1994
|
+
}, [fetchStatus, hasRunning]);
|
|
1393
1995
|
useEffect2(() => {
|
|
1394
1996
|
function handleClickOutside(event) {
|
|
1395
1997
|
if (popoverRef.current && !popoverRef.current.contains(event.target)) {
|
|
@@ -1419,6 +2021,11 @@ function StatusChipView({
|
|
|
1419
2021
|
keep_recent_count: 10
|
|
1420
2022
|
});
|
|
1421
2023
|
if (res.ok) {
|
|
2024
|
+
if (res.task_id) {
|
|
2025
|
+
setTasksRead(
|
|
2026
|
+
(prev) => seedRunningTask(prev, res.task_id, res.resource_id)
|
|
2027
|
+
);
|
|
2028
|
+
}
|
|
1422
2029
|
await fetchStatus();
|
|
1423
2030
|
onCommit?.();
|
|
1424
2031
|
} else {
|
|
@@ -1437,11 +2044,15 @@ function StatusChipView({
|
|
|
1437
2044
|
const sessionUnreadable = isOnline && sessionRead !== null && sessionRead.status !== "ok";
|
|
1438
2045
|
const pendingTokens = sessionData?.pending_tokens ?? 0;
|
|
1439
2046
|
const recalledCount = recalledResult.recalledCount;
|
|
1440
|
-
const
|
|
2047
|
+
const rawDotState = getChipDotState({
|
|
1441
2048
|
isOnline,
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
);
|
|
2049
|
+
sessionUnreadable,
|
|
2050
|
+
breakdown
|
|
2051
|
+
});
|
|
2052
|
+
const dotState = isCommitting && (rawDotState === "online-idle" || rawDotState === "busy") ? "busy" : rawDotState;
|
|
2053
|
+
const dotBusy = dotState === "busy";
|
|
2054
|
+
const dotFailed = dotState === "extraction-failed";
|
|
2055
|
+
const statusColor = getDotColorForState(dotState);
|
|
1445
2056
|
const tooltipTitle = formatTooltipTitle({
|
|
1446
2057
|
isOnline,
|
|
1447
2058
|
isCommitting,
|
|
@@ -1458,6 +2069,7 @@ function StatusChipView({
|
|
|
1458
2069
|
style: { minWidth: 0, display: "inline-flex", position: "relative" },
|
|
1459
2070
|
ref: popoverRef,
|
|
1460
2071
|
children: [
|
|
2072
|
+
/* @__PURE__ */ jsx2("style", { children: `@keyframes ov-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } }` }),
|
|
1461
2073
|
/* @__PURE__ */ jsxs2(
|
|
1462
2074
|
"button",
|
|
1463
2075
|
{
|
|
@@ -1488,17 +2100,32 @@ function StatusChipView({
|
|
|
1488
2100
|
title: tooltipTitle,
|
|
1489
2101
|
"aria-label": tooltipTitle,
|
|
1490
2102
|
children: [
|
|
1491
|
-
/* @__PURE__ */ jsx2(
|
|
2103
|
+
dotFailed ? /* @__PURE__ */ jsx2(
|
|
1492
2104
|
"span",
|
|
1493
2105
|
{
|
|
1494
2106
|
"data-testid": "status-dot",
|
|
2107
|
+
"data-dot-state": dotState,
|
|
2108
|
+
"aria-hidden": "true",
|
|
2109
|
+
style: {
|
|
2110
|
+
display: "inline-flex",
|
|
2111
|
+
color: statusColor,
|
|
2112
|
+
flexShrink: 0
|
|
2113
|
+
},
|
|
2114
|
+
children: /* @__PURE__ */ jsx2(WarningGlyph, { size: 11, testId: "chip-warning-glyph" })
|
|
2115
|
+
}
|
|
2116
|
+
) : /* @__PURE__ */ jsx2(
|
|
2117
|
+
"span",
|
|
2118
|
+
{
|
|
2119
|
+
"data-testid": "status-dot",
|
|
2120
|
+
"data-dot-state": dotBusy ? "busy" : dotState,
|
|
1495
2121
|
"aria-hidden": "true",
|
|
1496
2122
|
style: {
|
|
1497
2123
|
width: "6px",
|
|
1498
2124
|
height: "6px",
|
|
1499
2125
|
borderRadius: "50%",
|
|
1500
2126
|
background: statusColor,
|
|
1501
|
-
flexShrink: 0
|
|
2127
|
+
flexShrink: 0,
|
|
2128
|
+
...dotBusy ? { animation: "ov-pulse 1.2s ease-in-out infinite" } : {}
|
|
1502
2129
|
}
|
|
1503
2130
|
}
|
|
1504
2131
|
),
|
|
@@ -1517,6 +2144,8 @@ function StatusChipView({
|
|
|
1517
2144
|
endpoint: apiClient.endpoint,
|
|
1518
2145
|
isCommitting,
|
|
1519
2146
|
commitError,
|
|
2147
|
+
breakdown,
|
|
2148
|
+
client: apiClient,
|
|
1520
2149
|
onCommitNow: handleCommitNow,
|
|
1521
2150
|
onClose: () => setIsOpen(false)
|
|
1522
2151
|
}
|
|
@@ -2130,26 +2759,37 @@ function apply(ctx) {
|
|
|
2130
2759
|
export {
|
|
2131
2760
|
COMMIT_THRESHOLD,
|
|
2132
2761
|
DEFAULT_OPENVIKING_ENDPOINT,
|
|
2762
|
+
ExtractionSection,
|
|
2133
2763
|
OpenVikingClient,
|
|
2134
2764
|
OpenVikingSettingsSection,
|
|
2135
2765
|
OpenVikingStatusChip,
|
|
2136
2766
|
OpenVikingStatusPopover,
|
|
2767
|
+
POLL_INTERVAL_ACTIVE_MS,
|
|
2768
|
+
POLL_INTERVAL_IDLE_MS,
|
|
2137
2769
|
PROXY_OPENVIKING_ENDPOINT,
|
|
2770
|
+
TASKS_LIMIT_CAP,
|
|
2138
2771
|
THEME,
|
|
2772
|
+
WarningGlyph,
|
|
2139
2773
|
apply,
|
|
2140
2774
|
chatNodesToText,
|
|
2141
2775
|
checkHealth,
|
|
2142
2776
|
commitSession,
|
|
2777
|
+
computeBreakdown,
|
|
2143
2778
|
defaultOpenVikingClient,
|
|
2144
2779
|
fetchSession,
|
|
2780
|
+
formatBacklog,
|
|
2145
2781
|
formatDaemonVersion,
|
|
2782
|
+
formatDuration,
|
|
2146
2783
|
formatEndpoint,
|
|
2784
|
+
formatExtractionSummary,
|
|
2147
2785
|
formatMemoryLeafName,
|
|
2148
2786
|
formatPendingTokens,
|
|
2149
2787
|
formatRelativeTime,
|
|
2150
2788
|
formatStatusLabel,
|
|
2151
2789
|
formatTooltipTitle,
|
|
2152
2790
|
getCategoryBadgeStyle,
|
|
2791
|
+
getChipDotState,
|
|
2792
|
+
getDotColorForState,
|
|
2153
2793
|
getProgressBarColor,
|
|
2154
2794
|
getProgressBarPercent,
|
|
2155
2795
|
getSession,
|
|
@@ -2158,9 +2798,13 @@ export {
|
|
|
2158
2798
|
inferCategory,
|
|
2159
2799
|
inject,
|
|
2160
2800
|
name,
|
|
2801
|
+
normalizeExecutionEvent,
|
|
2802
|
+
normalizeExtractionTask,
|
|
2803
|
+
normalizeTaskList,
|
|
2161
2804
|
parseRecalledMemories,
|
|
2162
2805
|
resolveApiKey,
|
|
2163
2806
|
resolveEndpoint,
|
|
2807
|
+
seedRunningTask,
|
|
2164
2808
|
themeVar,
|
|
2165
2809
|
truncateSessionId
|
|
2166
2810
|
};
|