@dipertq/dsh-openviking-status 0.2.2 → 0.3.1
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 +689 -12
- package/lib/client.cjs.map +1 -1
- package/lib/client.d.cts +242 -3
- package/lib/client.js +689 -12
- package/lib/client.js.map +1 -1
- package/lib/index.js +173 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.cjs
CHANGED
|
@@ -37,26 +37,38 @@ var client_exports = {};
|
|
|
37
37
|
__export(client_exports, {
|
|
38
38
|
COMMIT_THRESHOLD: () => COMMIT_THRESHOLD,
|
|
39
39
|
DEFAULT_OPENVIKING_ENDPOINT: () => DEFAULT_OPENVIKING_ENDPOINT,
|
|
40
|
+
ExtractionSection: () => ExtractionSection,
|
|
40
41
|
OpenVikingClient: () => OpenVikingClient,
|
|
41
42
|
OpenVikingSettingsSection: () => OpenVikingSettingsSection,
|
|
42
43
|
OpenVikingStatusChip: () => OpenVikingStatusChip,
|
|
43
44
|
OpenVikingStatusPopover: () => OpenVikingStatusPopover,
|
|
45
|
+
POLL_INTERVAL_ACTIVE_MS: () => POLL_INTERVAL_ACTIVE_MS,
|
|
46
|
+
POLL_INTERVAL_IDLE_MS: () => POLL_INTERVAL_IDLE_MS,
|
|
44
47
|
PROXY_OPENVIKING_ENDPOINT: () => PROXY_OPENVIKING_ENDPOINT,
|
|
48
|
+
TASKS_LIMIT_CAP: () => TASKS_LIMIT_CAP,
|
|
45
49
|
THEME: () => THEME,
|
|
50
|
+
WarningGlyph: () => WarningGlyph,
|
|
46
51
|
apply: () => apply,
|
|
47
52
|
chatNodesToText: () => chatNodesToText,
|
|
48
53
|
checkHealth: () => checkHealth,
|
|
49
54
|
commitSession: () => commitSession,
|
|
55
|
+
computeBreakdown: () => computeBreakdown,
|
|
50
56
|
defaultOpenVikingClient: () => defaultOpenVikingClient,
|
|
57
|
+
extractTaskList: () => extractTaskList,
|
|
51
58
|
fetchSession: () => fetchSession,
|
|
59
|
+
formatBacklog: () => formatBacklog,
|
|
52
60
|
formatDaemonVersion: () => formatDaemonVersion,
|
|
61
|
+
formatDuration: () => formatDuration,
|
|
53
62
|
formatEndpoint: () => formatEndpoint,
|
|
63
|
+
formatExtractionSummary: () => formatExtractionSummary,
|
|
54
64
|
formatMemoryLeafName: () => formatMemoryLeafName,
|
|
55
65
|
formatPendingTokens: () => formatPendingTokens,
|
|
56
66
|
formatRelativeTime: () => formatRelativeTime,
|
|
57
67
|
formatStatusLabel: () => formatStatusLabel,
|
|
58
68
|
formatTooltipTitle: () => formatTooltipTitle,
|
|
59
69
|
getCategoryBadgeStyle: () => getCategoryBadgeStyle,
|
|
70
|
+
getChipDotState: () => getChipDotState,
|
|
71
|
+
getDotColorForState: () => getDotColorForState,
|
|
60
72
|
getProgressBarColor: () => getProgressBarColor,
|
|
61
73
|
getProgressBarPercent: () => getProgressBarPercent,
|
|
62
74
|
getSession: () => getSession,
|
|
@@ -65,9 +77,15 @@ __export(client_exports, {
|
|
|
65
77
|
inferCategory: () => inferCategory,
|
|
66
78
|
inject: () => inject,
|
|
67
79
|
name: () => name,
|
|
80
|
+
normalizeExecutionEvent: () => normalizeExecutionEvent,
|
|
81
|
+
normalizeExtractionTask: () => normalizeExtractionTask,
|
|
82
|
+
normalizeTaskList: () => normalizeTaskList,
|
|
83
|
+
normalizeTimestamp: () => normalizeTimestamp,
|
|
68
84
|
parseRecalledMemories: () => parseRecalledMemories,
|
|
69
85
|
resolveApiKey: () => resolveApiKey,
|
|
70
86
|
resolveEndpoint: () => resolveEndpoint,
|
|
87
|
+
seedRunningTask: () => seedRunningTask,
|
|
88
|
+
shouldPollActive: () => shouldPollActive,
|
|
71
89
|
themeVar: () => themeVar,
|
|
72
90
|
truncateSessionId: () => truncateSessionId
|
|
73
91
|
});
|
|
@@ -78,6 +96,98 @@ var import_react2 = __toESM(require("react"), 1);
|
|
|
78
96
|
var import_react_dom = __toESM(require("react-dom"), 1);
|
|
79
97
|
|
|
80
98
|
// src/client/api.ts
|
|
99
|
+
var TASKS_LIMIT_CAP = 200;
|
|
100
|
+
function normalizeTimestamp(ts, isoFallback) {
|
|
101
|
+
if (typeof isoFallback === "string" && isoFallback.trim()) {
|
|
102
|
+
return isoFallback.trim();
|
|
103
|
+
}
|
|
104
|
+
if (typeof ts === "string" && ts.trim()) {
|
|
105
|
+
return ts.trim();
|
|
106
|
+
}
|
|
107
|
+
if (typeof ts === "number" && Number.isFinite(ts) && ts > 0) {
|
|
108
|
+
const ms = ts < 1e11 ? ts * 1e3 : ts;
|
|
109
|
+
return new Date(ms).toISOString();
|
|
110
|
+
}
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
function extractTaskList(data) {
|
|
114
|
+
if (!data || typeof data !== "object") return [];
|
|
115
|
+
if (Array.isArray(data)) return data;
|
|
116
|
+
const obj = data;
|
|
117
|
+
if (Array.isArray(obj.result)) return obj.result;
|
|
118
|
+
if (Array.isArray(obj.items)) return obj.items;
|
|
119
|
+
if (Array.isArray(obj.tasks)) return obj.tasks;
|
|
120
|
+
if (obj.result && typeof obj.result === "object") {
|
|
121
|
+
const res = obj.result;
|
|
122
|
+
if (Array.isArray(res.items)) return res.items;
|
|
123
|
+
if (Array.isArray(res.tasks)) return res.tasks;
|
|
124
|
+
}
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
function normalizeExtractionTask(raw) {
|
|
128
|
+
if (!raw || typeof raw !== "object") return null;
|
|
129
|
+
const taskId = typeof raw.task_id === "string" && raw.task_id || typeof raw.id === "string" && raw.id || "";
|
|
130
|
+
if (!taskId) return null;
|
|
131
|
+
const statusRaw = typeof raw.status === "string" ? raw.status : "";
|
|
132
|
+
const status = statusRaw === "running" || statusRaw === "pending" || statusRaw === "completed" || statusRaw === "failed" ? statusRaw : "pending";
|
|
133
|
+
const result = raw.result ?? {};
|
|
134
|
+
const extracted = result.memories_extracted ?? {};
|
|
135
|
+
const numberOf = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
136
|
+
const tokenUsageRaw = result.token_usage ?? raw.token_usage;
|
|
137
|
+
const tokenUsage = typeof tokenUsageRaw === "number" ? tokenUsageRaw : numberOf(
|
|
138
|
+
tokenUsageRaw?.total_tokens
|
|
139
|
+
);
|
|
140
|
+
const errorRaw = raw.error;
|
|
141
|
+
const errorMsg = typeof errorRaw === "string" ? errorRaw : typeof errorRaw?.message === "string" ? errorRaw.message : void 0;
|
|
142
|
+
return {
|
|
143
|
+
task_id: taskId,
|
|
144
|
+
status,
|
|
145
|
+
resource_id: typeof raw.resource_id === "string" ? raw.resource_id : void 0,
|
|
146
|
+
created_at: normalizeTimestamp(raw.created_at, raw.created_at_iso),
|
|
147
|
+
updated_at: normalizeTimestamp(raw.updated_at, raw.updated_at_iso),
|
|
148
|
+
memory_write: numberOf(extracted.memory_write ?? extracted.written),
|
|
149
|
+
memory_edit: numberOf(extracted.memory_edit ?? extracted.edited),
|
|
150
|
+
token_usage: tokenUsage,
|
|
151
|
+
error: errorMsg
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function computeBreakdown(tasks) {
|
|
155
|
+
const b = {
|
|
156
|
+
running: 0,
|
|
157
|
+
pending: 0,
|
|
158
|
+
completed: 0,
|
|
159
|
+
failed: 0,
|
|
160
|
+
total: tasks.length,
|
|
161
|
+
firstRunning: null,
|
|
162
|
+
lastCompleted: null,
|
|
163
|
+
lastFailed: null
|
|
164
|
+
};
|
|
165
|
+
for (const t of tasks) {
|
|
166
|
+
if (t.status === "running") {
|
|
167
|
+
b.running += 1;
|
|
168
|
+
if (!b.firstRunning) b.firstRunning = t;
|
|
169
|
+
} else if (t.status === "pending") b.pending += 1;
|
|
170
|
+
else if (t.status === "completed") {
|
|
171
|
+
b.completed += 1;
|
|
172
|
+
if (!b.lastCompleted) b.lastCompleted = t;
|
|
173
|
+
} else if (t.status === "failed") {
|
|
174
|
+
b.failed += 1;
|
|
175
|
+
if (!b.lastFailed) b.lastFailed = t;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return b;
|
|
179
|
+
}
|
|
180
|
+
function normalizeExecutionEvent(raw) {
|
|
181
|
+
return {
|
|
182
|
+
seq: typeof raw.seq === "number" ? raw.seq : void 0,
|
|
183
|
+
recorded_at: normalizeTimestamp(raw.recorded_at, raw.recorded_at_iso),
|
|
184
|
+
kind: typeof raw.kind === "string" ? raw.kind : void 0,
|
|
185
|
+
status: typeof raw.status === "string" ? raw.status : void 0,
|
|
186
|
+
stage: typeof raw.stage === "string" ? raw.stage : null,
|
|
187
|
+
operation: typeof raw.operation === "string" ? raw.operation : null,
|
|
188
|
+
error: typeof raw.error === "string" ? raw.error : null
|
|
189
|
+
};
|
|
190
|
+
}
|
|
81
191
|
var DEFAULT_OPENVIKING_ENDPOINT = "http://127.0.0.1:1933";
|
|
82
192
|
var PROXY_OPENVIKING_ENDPOINT = "/openviking-status/api";
|
|
83
193
|
function resolveEndpoint(endpoint) {
|
|
@@ -386,7 +496,9 @@ var OpenVikingClient = class {
|
|
|
386
496
|
const data = await res.json().catch(() => ({}));
|
|
387
497
|
return {
|
|
388
498
|
ok: data.ok === true,
|
|
389
|
-
error: typeof data.error === "string" ? data.error : void 0
|
|
499
|
+
error: typeof data.error === "string" ? data.error : void 0,
|
|
500
|
+
task_id: typeof data.task_id === "string" ? data.task_id : void 0,
|
|
501
|
+
resource_id: typeof data.resource_id === "string" ? data.resource_id : void 0
|
|
390
502
|
};
|
|
391
503
|
} catch (err) {
|
|
392
504
|
return {
|
|
@@ -418,7 +530,14 @@ var OpenVikingClient = class {
|
|
|
418
530
|
return { ok: false, error: String(errorMsg) };
|
|
419
531
|
}
|
|
420
532
|
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
421
|
-
|
|
533
|
+
const okBody = await res.json().catch(() => ({}));
|
|
534
|
+
const container = okBody.result ?? okBody.data ?? okBody;
|
|
535
|
+
const taskId = container.task_id ?? container.id;
|
|
536
|
+
return {
|
|
537
|
+
ok: true,
|
|
538
|
+
task_id: typeof taskId === "string" && taskId.trim() ? taskId.trim() : void 0,
|
|
539
|
+
resource_id: candidateId
|
|
540
|
+
};
|
|
422
541
|
} catch (err) {
|
|
423
542
|
return {
|
|
424
543
|
ok: false,
|
|
@@ -428,7 +547,192 @@ var OpenVikingClient = class {
|
|
|
428
547
|
}
|
|
429
548
|
return { ok: false, error: lastError };
|
|
430
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Список задач Phase 2 (Memory Extraction) текущей сессии со сводной
|
|
552
|
+
* разбивкой по статусам.
|
|
553
|
+
*
|
|
554
|
+
* Через прокси: один запрос `GET /tasks?session=<id>`, разбивка считается
|
|
555
|
+
* здесь. Напрямую: разрешаем `resource_id` через кандидатов и запрашиваем
|
|
556
|
+
* `GET /api/v1/tasks?resource_id=<id>&task_type=session_commit&limit=200`.
|
|
557
|
+
*/
|
|
558
|
+
async listTasks(sessionId) {
|
|
559
|
+
if (!sessionId || !sessionId.trim()) {
|
|
560
|
+
return { status: "missing" };
|
|
561
|
+
}
|
|
562
|
+
if (this.isProxy()) {
|
|
563
|
+
try {
|
|
564
|
+
const res = await fetch(
|
|
565
|
+
`${this.endpoint}/tasks?session=${encodeURIComponent(
|
|
566
|
+
sessionId.trim()
|
|
567
|
+
)}&limit=${TASKS_LIMIT_CAP}`,
|
|
568
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
569
|
+
);
|
|
570
|
+
if (res.status === 401 || res.status === 403) {
|
|
571
|
+
return { status: "unauthorized" };
|
|
572
|
+
}
|
|
573
|
+
if (!res.ok) {
|
|
574
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
575
|
+
}
|
|
576
|
+
const data = await res.json();
|
|
577
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
578
|
+
if (data.status === "missing") return { status: "missing" };
|
|
579
|
+
if (data.status === "unreachable")
|
|
580
|
+
return {
|
|
581
|
+
status: "unreachable",
|
|
582
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
583
|
+
};
|
|
584
|
+
if (data.status === "error")
|
|
585
|
+
return {
|
|
586
|
+
status: "error",
|
|
587
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
588
|
+
};
|
|
589
|
+
const tasks = normalizeTaskList(extractTaskList(data.tasks ?? data));
|
|
590
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
591
|
+
} catch (err) {
|
|
592
|
+
return {
|
|
593
|
+
status: "unreachable",
|
|
594
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const candidates = this.getCandidateSessionIds(sessionId);
|
|
599
|
+
let resourceId = null;
|
|
600
|
+
for (const candidateId of candidates) {
|
|
601
|
+
try {
|
|
602
|
+
const probe = await fetch(
|
|
603
|
+
`${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}`,
|
|
604
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
605
|
+
);
|
|
606
|
+
if (probe.status === 404) continue;
|
|
607
|
+
if (probe.status === 401 || probe.status === 403) {
|
|
608
|
+
return { status: "unauthorized" };
|
|
609
|
+
}
|
|
610
|
+
if (probe.ok) {
|
|
611
|
+
resourceId = candidateId;
|
|
612
|
+
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
} catch (err) {
|
|
616
|
+
return {
|
|
617
|
+
status: "unreachable",
|
|
618
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (!resourceId) return { status: "missing" };
|
|
623
|
+
try {
|
|
624
|
+
const query = new URLSearchParams({
|
|
625
|
+
resource_id: resourceId,
|
|
626
|
+
task_type: "session_commit",
|
|
627
|
+
limit: String(TASKS_LIMIT_CAP)
|
|
628
|
+
});
|
|
629
|
+
const res = await fetch(
|
|
630
|
+
`${this.endpoint}/api/v1/tasks?${query.toString()}`,
|
|
631
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
632
|
+
);
|
|
633
|
+
if (res.status === 401 || res.status === 403) {
|
|
634
|
+
return { status: "unauthorized" };
|
|
635
|
+
}
|
|
636
|
+
if (!res.ok) {
|
|
637
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
638
|
+
}
|
|
639
|
+
const data = await res.json();
|
|
640
|
+
const tasks = normalizeTaskList(extractTaskList(data));
|
|
641
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
642
|
+
} catch (err) {
|
|
643
|
+
return {
|
|
644
|
+
status: "unreachable",
|
|
645
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Лениво загрузить одну задачу с лентой `execution_events` для «Show log».
|
|
651
|
+
*/
|
|
652
|
+
async fetchTaskEvents(taskId) {
|
|
653
|
+
if (!taskId || !taskId.trim()) {
|
|
654
|
+
return { status: "missing" };
|
|
655
|
+
}
|
|
656
|
+
const buildOk = (taskRaw) => {
|
|
657
|
+
const task = normalizeExtractionTask(taskRaw);
|
|
658
|
+
const eventsContainer = taskRaw.execution_events ?? {};
|
|
659
|
+
const rawEvents = eventsContainer.items ?? (Array.isArray(taskRaw.execution_events) ? taskRaw.execution_events : []);
|
|
660
|
+
const events = (Array.isArray(rawEvents) ? rawEvents : []).filter(
|
|
661
|
+
(e) => !!e && typeof e === "object"
|
|
662
|
+
).map(normalizeExecutionEvent);
|
|
663
|
+
if (!task) return { status: "error", detail: "malformed task" };
|
|
664
|
+
return { status: "ok", task, events };
|
|
665
|
+
};
|
|
666
|
+
if (this.isProxy()) {
|
|
667
|
+
try {
|
|
668
|
+
const res = await fetch(
|
|
669
|
+
`${this.endpoint}/task?id=${encodeURIComponent(
|
|
670
|
+
taskId.trim()
|
|
671
|
+
)}&events=1`,
|
|
672
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
673
|
+
);
|
|
674
|
+
if (res.status === 401 || res.status === 403) {
|
|
675
|
+
return { status: "unauthorized" };
|
|
676
|
+
}
|
|
677
|
+
if (!res.ok) {
|
|
678
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
679
|
+
}
|
|
680
|
+
const data = await res.json();
|
|
681
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
682
|
+
if (data.status === "missing") return { status: "missing" };
|
|
683
|
+
if (data.status === "unreachable")
|
|
684
|
+
return {
|
|
685
|
+
status: "unreachable",
|
|
686
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
687
|
+
};
|
|
688
|
+
if (data.status === "error")
|
|
689
|
+
return {
|
|
690
|
+
status: "error",
|
|
691
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
692
|
+
};
|
|
693
|
+
return buildOk(data.task ?? {});
|
|
694
|
+
} catch (err) {
|
|
695
|
+
return {
|
|
696
|
+
status: "unreachable",
|
|
697
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
const res = await fetch(
|
|
703
|
+
`${this.endpoint}/api/v1/tasks/${encodeURIComponent(
|
|
704
|
+
taskId.trim()
|
|
705
|
+
)}?include_events=true`,
|
|
706
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
707
|
+
);
|
|
708
|
+
if (res.status === 404) return { status: "missing" };
|
|
709
|
+
if (res.status === 401 || res.status === 403) {
|
|
710
|
+
return { status: "unauthorized" };
|
|
711
|
+
}
|
|
712
|
+
if (!res.ok) {
|
|
713
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
714
|
+
}
|
|
715
|
+
const data = await res.json();
|
|
716
|
+
const taskRaw = data?.result ?? data?.data ?? data;
|
|
717
|
+
return buildOk(taskRaw);
|
|
718
|
+
} catch (err) {
|
|
719
|
+
return {
|
|
720
|
+
status: "unreachable",
|
|
721
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
}
|
|
431
725
|
};
|
|
726
|
+
function normalizeTaskList(raw) {
|
|
727
|
+
if (!Array.isArray(raw)) return [];
|
|
728
|
+
const tasks = [];
|
|
729
|
+
for (const entry of raw) {
|
|
730
|
+
if (!entry || typeof entry !== "object") continue;
|
|
731
|
+
const task = normalizeExtractionTask(entry);
|
|
732
|
+
if (task) tasks.push(task);
|
|
733
|
+
}
|
|
734
|
+
return tasks;
|
|
735
|
+
}
|
|
432
736
|
var defaultOpenVikingClient = new OpenVikingClient();
|
|
433
737
|
function checkHealth(endpoint, apiKey) {
|
|
434
738
|
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
@@ -802,6 +1106,39 @@ function dshIcon(name2) {
|
|
|
802
1106
|
return null;
|
|
803
1107
|
}
|
|
804
1108
|
}
|
|
1109
|
+
function formatDuration(startIso, endIso) {
|
|
1110
|
+
if (!startIso || !endIso) return void 0;
|
|
1111
|
+
const start = new Date(startIso).getTime();
|
|
1112
|
+
const end = new Date(endIso).getTime();
|
|
1113
|
+
if (isNaN(start) || isNaN(end) || end < start) return void 0;
|
|
1114
|
+
const ms = end - start;
|
|
1115
|
+
const sec = ms / 1e3;
|
|
1116
|
+
if (sec < 60) {
|
|
1117
|
+
return `${sec < 10 ? sec.toFixed(1) : Math.round(sec)}s`;
|
|
1118
|
+
}
|
|
1119
|
+
const min = Math.floor(sec / 60);
|
|
1120
|
+
const rem = Math.round(sec % 60);
|
|
1121
|
+
return `${min}m ${rem}s`;
|
|
1122
|
+
}
|
|
1123
|
+
function formatExtractionSummary(task, now = Date.now()) {
|
|
1124
|
+
const write = task.memory_write ?? 0;
|
|
1125
|
+
const edit = task.memory_edit ?? 0;
|
|
1126
|
+
const parts = [`${write} written, ${edit} edited`];
|
|
1127
|
+
const duration = formatDuration(task.created_at, task.updated_at);
|
|
1128
|
+
if (duration) parts.push(duration);
|
|
1129
|
+
const rel = formatRelativeTime(task.updated_at, now);
|
|
1130
|
+
if (rel) parts.push(rel);
|
|
1131
|
+
return parts.join(" \xB7 ");
|
|
1132
|
+
}
|
|
1133
|
+
function formatBacklog(breakdown) {
|
|
1134
|
+
if (!breakdown) return null;
|
|
1135
|
+
const { pending, failed } = breakdown;
|
|
1136
|
+
if (pending <= 0 && failed <= 0) return null;
|
|
1137
|
+
const parts = [];
|
|
1138
|
+
if (pending > 0) parts.push(`${pending} pending`);
|
|
1139
|
+
if (failed > 0) parts.push(`${failed} failed`);
|
|
1140
|
+
return parts.join(" / ");
|
|
1141
|
+
}
|
|
805
1142
|
function getProgressBarPercent(pendingTokens, threshold = COMMIT_THRESHOLD) {
|
|
806
1143
|
if (!threshold || threshold <= 0) return 0;
|
|
807
1144
|
const ratio = (pendingTokens || 0) / threshold;
|
|
@@ -868,6 +1205,229 @@ function handleEscapeKey(event, onClose) {
|
|
|
868
1205
|
}
|
|
869
1206
|
return false;
|
|
870
1207
|
}
|
|
1208
|
+
function ExtractionSection({
|
|
1209
|
+
breakdown,
|
|
1210
|
+
client
|
|
1211
|
+
}) {
|
|
1212
|
+
const [showLog, setShowLog] = (0, import_react.useState)(false);
|
|
1213
|
+
const [events, setEvents] = (0, import_react.useState)(null);
|
|
1214
|
+
const [logError, setLogError] = (0, import_react.useState)(null);
|
|
1215
|
+
const [loadingLog, setLoadingLog] = (0, import_react.useState)(false);
|
|
1216
|
+
if (!breakdown) return null;
|
|
1217
|
+
const isRunning = breakdown.running >= 1;
|
|
1218
|
+
const lastCompleted = breakdown.lastCompleted;
|
|
1219
|
+
const lastFailed = breakdown.lastFailed;
|
|
1220
|
+
const backlog = formatBacklog(breakdown);
|
|
1221
|
+
const failedActive = !isRunning && breakdown.failed >= 1;
|
|
1222
|
+
const logTaskId = (isRunning ? breakdown.firstRunning?.task_id : null) ?? (failedActive ? lastFailed?.task_id : null) ?? lastCompleted?.task_id ?? lastFailed?.task_id ?? null;
|
|
1223
|
+
const mutedStyle = { color: themeVar("labelTertiary") };
|
|
1224
|
+
const toggleLog = (0, import_react.useCallback)(async () => {
|
|
1225
|
+
const next = !showLog;
|
|
1226
|
+
setShowLog(next);
|
|
1227
|
+
if (next && events === null && logTaskId && client) {
|
|
1228
|
+
setLoadingLog(true);
|
|
1229
|
+
setLogError(null);
|
|
1230
|
+
const res = await client.fetchTaskEvents(logTaskId);
|
|
1231
|
+
setLoadingLog(false);
|
|
1232
|
+
if (res.status === "ok") {
|
|
1233
|
+
setEvents(res.events);
|
|
1234
|
+
} else {
|
|
1235
|
+
setLogError(
|
|
1236
|
+
res.status === "unauthorized" ? "No access to task log" : "Could not load log"
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}, [showLog, events, logTaskId, client]);
|
|
1241
|
+
if (!isRunning && !failedActive && !lastCompleted && !backlog) {
|
|
1242
|
+
return null;
|
|
1243
|
+
}
|
|
1244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-testid": "extraction-section", style: { marginBottom: "12px" }, children: [
|
|
1245
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
style: {
|
|
1249
|
+
marginBottom: "4px",
|
|
1250
|
+
color: themeVar("labelPrimary"),
|
|
1251
|
+
fontWeight: 500
|
|
1252
|
+
},
|
|
1253
|
+
children: "Memory Extraction"
|
|
1254
|
+
}
|
|
1255
|
+
),
|
|
1256
|
+
isRunning ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1258
|
+
"div",
|
|
1259
|
+
{
|
|
1260
|
+
style: {
|
|
1261
|
+
display: "flex",
|
|
1262
|
+
alignItems: "center",
|
|
1263
|
+
gap: "6px",
|
|
1264
|
+
marginBottom: "4px"
|
|
1265
|
+
},
|
|
1266
|
+
children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1268
|
+
"span",
|
|
1269
|
+
{
|
|
1270
|
+
"data-testid": "extraction-status-dot",
|
|
1271
|
+
"aria-hidden": "true",
|
|
1272
|
+
style: {
|
|
1273
|
+
width: "6px",
|
|
1274
|
+
height: "6px",
|
|
1275
|
+
borderRadius: "50%",
|
|
1276
|
+
background: themeVar("stateSuccess"),
|
|
1277
|
+
animation: "ov-pulse 1.2s ease-in-out infinite",
|
|
1278
|
+
flexShrink: 0
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
),
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-running-label", children: "Extracting\u2026" })
|
|
1283
|
+
]
|
|
1284
|
+
}
|
|
1285
|
+
),
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1287
|
+
"div",
|
|
1288
|
+
{
|
|
1289
|
+
"data-testid": "extraction-indeterminate-track",
|
|
1290
|
+
style: {
|
|
1291
|
+
width: "100%",
|
|
1292
|
+
height: "6px",
|
|
1293
|
+
borderRadius: "3px",
|
|
1294
|
+
backgroundColor: themeVar("insetSurface"),
|
|
1295
|
+
overflow: "hidden",
|
|
1296
|
+
position: "relative"
|
|
1297
|
+
},
|
|
1298
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1299
|
+
"div",
|
|
1300
|
+
{
|
|
1301
|
+
"data-testid": "extraction-indeterminate-fill",
|
|
1302
|
+
style: {
|
|
1303
|
+
position: "absolute",
|
|
1304
|
+
left: 0,
|
|
1305
|
+
top: 0,
|
|
1306
|
+
height: "100%",
|
|
1307
|
+
width: "40%",
|
|
1308
|
+
borderRadius: "3px",
|
|
1309
|
+
backgroundColor: themeVar("stateSuccess"),
|
|
1310
|
+
animation: "ov-indeterminate 1.2s ease-in-out infinite"
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
)
|
|
1314
|
+
}
|
|
1315
|
+
)
|
|
1316
|
+
] }) : failedActive && lastFailed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1317
|
+
"div",
|
|
1318
|
+
{
|
|
1319
|
+
"data-testid": "extraction-failed-line",
|
|
1320
|
+
style: {
|
|
1321
|
+
display: "flex",
|
|
1322
|
+
alignItems: "center",
|
|
1323
|
+
gap: "6px",
|
|
1324
|
+
color: themeVar("stateWarning")
|
|
1325
|
+
},
|
|
1326
|
+
children: [
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1328
|
+
"span",
|
|
1329
|
+
{
|
|
1330
|
+
"aria-hidden": "true",
|
|
1331
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
1332
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WarningGlyph, { size: 12, testId: "extraction-warning-glyph" })
|
|
1333
|
+
}
|
|
1334
|
+
),
|
|
1335
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-failed-text", children: lastFailed.error || "extraction failed" })
|
|
1336
|
+
]
|
|
1337
|
+
}
|
|
1338
|
+
) : lastCompleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1339
|
+
"div",
|
|
1340
|
+
{
|
|
1341
|
+
style: {
|
|
1342
|
+
display: "flex",
|
|
1343
|
+
alignItems: "center",
|
|
1344
|
+
gap: "6px"
|
|
1345
|
+
},
|
|
1346
|
+
children: [
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1348
|
+
"span",
|
|
1349
|
+
{
|
|
1350
|
+
"data-testid": "extraction-status-dot",
|
|
1351
|
+
"aria-hidden": "true",
|
|
1352
|
+
style: {
|
|
1353
|
+
width: "6px",
|
|
1354
|
+
height: "6px",
|
|
1355
|
+
borderRadius: "50%",
|
|
1356
|
+
background: themeVar("stateSuccess"),
|
|
1357
|
+
flexShrink: 0
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
),
|
|
1361
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-last-line", children: formatExtractionSummary(lastCompleted) })
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
) : null,
|
|
1365
|
+
backlog && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1366
|
+
"div",
|
|
1367
|
+
{
|
|
1368
|
+
"data-testid": "extraction-backlog-line",
|
|
1369
|
+
style: { ...mutedStyle, marginTop: "4px" },
|
|
1370
|
+
children: backlog
|
|
1371
|
+
}
|
|
1372
|
+
),
|
|
1373
|
+
logTaskId && client && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "6px" }, children: [
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1375
|
+
"button",
|
|
1376
|
+
{
|
|
1377
|
+
type: "button",
|
|
1378
|
+
"data-testid": "extraction-show-log-btn",
|
|
1379
|
+
onClick: () => void toggleLog(),
|
|
1380
|
+
style: {
|
|
1381
|
+
background: "none",
|
|
1382
|
+
border: "none",
|
|
1383
|
+
padding: 0,
|
|
1384
|
+
cursor: "pointer",
|
|
1385
|
+
font: "inherit",
|
|
1386
|
+
color: themeVar("labelTertiary"),
|
|
1387
|
+
textDecoration: "underline"
|
|
1388
|
+
},
|
|
1389
|
+
"aria-expanded": showLog,
|
|
1390
|
+
children: showLog ? "Hide log" : "Show log"
|
|
1391
|
+
}
|
|
1392
|
+
),
|
|
1393
|
+
showLog && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1394
|
+
"div",
|
|
1395
|
+
{
|
|
1396
|
+
"data-testid": "extraction-log",
|
|
1397
|
+
style: {
|
|
1398
|
+
marginTop: "6px",
|
|
1399
|
+
display: "flex",
|
|
1400
|
+
flexDirection: "column",
|
|
1401
|
+
gap: "2px",
|
|
1402
|
+
maxHeight: "120px",
|
|
1403
|
+
overflowY: "auto"
|
|
1404
|
+
},
|
|
1405
|
+
children: [
|
|
1406
|
+
loadingLog && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-loading", style: mutedStyle, children: "Loading\u2026" }),
|
|
1407
|
+
logError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-error", style: mutedStyle, children: logError }),
|
|
1408
|
+
events && events.length === 0 && !loadingLog && !logError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-empty", style: mutedStyle, children: "No events" }),
|
|
1409
|
+
events?.map((ev, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1410
|
+
"div",
|
|
1411
|
+
{
|
|
1412
|
+
"data-testid": "extraction-log-event",
|
|
1413
|
+
style: {
|
|
1414
|
+
display: "flex",
|
|
1415
|
+
justifyContent: "space-between",
|
|
1416
|
+
gap: "8px"
|
|
1417
|
+
},
|
|
1418
|
+
children: [
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: ev.status || ev.kind || "event" }),
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: mutedStyle, children: formatRelativeTime(ev.recorded_at) || "" })
|
|
1421
|
+
]
|
|
1422
|
+
},
|
|
1423
|
+
`${ev.seq ?? idx}-${ev.recorded_at ?? idx}`
|
|
1424
|
+
))
|
|
1425
|
+
]
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
] })
|
|
1429
|
+
] });
|
|
1430
|
+
}
|
|
871
1431
|
function OpenVikingStatusPopover({
|
|
872
1432
|
sessionId,
|
|
873
1433
|
health,
|
|
@@ -877,6 +1437,8 @@ function OpenVikingStatusPopover({
|
|
|
877
1437
|
endpoint,
|
|
878
1438
|
isCommitting = false,
|
|
879
1439
|
commitError = null,
|
|
1440
|
+
breakdown = null,
|
|
1441
|
+
client,
|
|
880
1442
|
onCommitNow,
|
|
881
1443
|
onClose,
|
|
882
1444
|
className,
|
|
@@ -957,7 +1519,7 @@ function OpenVikingStatusPopover({
|
|
|
957
1519
|
...style
|
|
958
1520
|
},
|
|
959
1521
|
children: [
|
|
960
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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%; } }` }),
|
|
961
1523
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
962
1524
|
"div",
|
|
963
1525
|
{
|
|
@@ -1240,6 +1802,7 @@ function OpenVikingStatusPopover({
|
|
|
1240
1802
|
}
|
|
1241
1803
|
)
|
|
1242
1804
|
] }),
|
|
1805
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExtractionSection, { breakdown, client }),
|
|
1243
1806
|
commitError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1244
1807
|
"div",
|
|
1245
1808
|
{
|
|
@@ -1297,6 +1860,81 @@ function OpenVikingStatusPopover({
|
|
|
1297
1860
|
// src/client/OpenVikingStatusChip.tsx
|
|
1298
1861
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1299
1862
|
var COMMIT_THRESHOLD = 2e4;
|
|
1863
|
+
var POLL_INTERVAL_IDLE_MS = 15e3;
|
|
1864
|
+
var POLL_INTERVAL_ACTIVE_MS = 2500;
|
|
1865
|
+
function shouldPollActive(breakdown) {
|
|
1866
|
+
if (!breakdown) return false;
|
|
1867
|
+
return breakdown.running >= 1 || breakdown.pending >= 1;
|
|
1868
|
+
}
|
|
1869
|
+
function getChipDotState({
|
|
1870
|
+
isOnline,
|
|
1871
|
+
sessionUnreadable,
|
|
1872
|
+
breakdown
|
|
1873
|
+
}) {
|
|
1874
|
+
if (!isOnline) return "offline";
|
|
1875
|
+
if (sessionUnreadable) return "session-unreadable";
|
|
1876
|
+
if (breakdown && breakdown.running >= 1) return "busy";
|
|
1877
|
+
if (breakdown && breakdown.failed >= 1) return "extraction-failed";
|
|
1878
|
+
return "online-idle";
|
|
1879
|
+
}
|
|
1880
|
+
function WarningGlyph({
|
|
1881
|
+
size = 11,
|
|
1882
|
+
testId = "extraction-warning-glyph"
|
|
1883
|
+
}) {
|
|
1884
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1885
|
+
"svg",
|
|
1886
|
+
{
|
|
1887
|
+
"data-testid": testId,
|
|
1888
|
+
width: size,
|
|
1889
|
+
height: size,
|
|
1890
|
+
viewBox: "0 0 16 16",
|
|
1891
|
+
fill: "none",
|
|
1892
|
+
"aria-hidden": "true",
|
|
1893
|
+
style: { flexShrink: 0, display: "block" },
|
|
1894
|
+
children: [
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1897
|
+
"path",
|
|
1898
|
+
{
|
|
1899
|
+
d: "M8 5v3.5",
|
|
1900
|
+
stroke: "currentColor",
|
|
1901
|
+
strokeWidth: "1.5",
|
|
1902
|
+
strokeLinecap: "round"
|
|
1903
|
+
}
|
|
1904
|
+
),
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "11", r: "0.9", fill: "currentColor" })
|
|
1906
|
+
]
|
|
1907
|
+
}
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
function seedRunningTask(prev, taskId, resourceId) {
|
|
1911
|
+
const existing = prev && prev.status === "ok" ? prev.tasks : [];
|
|
1912
|
+
if (existing.some((t) => t.task_id === taskId)) {
|
|
1913
|
+
return prev;
|
|
1914
|
+
}
|
|
1915
|
+
const seeded = {
|
|
1916
|
+
task_id: taskId,
|
|
1917
|
+
status: "running",
|
|
1918
|
+
resource_id: resourceId,
|
|
1919
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1920
|
+
};
|
|
1921
|
+
const tasks = [seeded, ...existing];
|
|
1922
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
1923
|
+
}
|
|
1924
|
+
function getDotColorForState(state) {
|
|
1925
|
+
switch (state) {
|
|
1926
|
+
case "offline":
|
|
1927
|
+
return themeVar("stateError");
|
|
1928
|
+
case "session-unreadable":
|
|
1929
|
+
case "extraction-failed":
|
|
1930
|
+
return themeVar("stateWarning");
|
|
1931
|
+
case "busy":
|
|
1932
|
+
return themeVar("stateSuccess");
|
|
1933
|
+
case "online-idle":
|
|
1934
|
+
default:
|
|
1935
|
+
return themeVar("stateSuccess");
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1300
1938
|
function formatPendingTokens(pendingTokens) {
|
|
1301
1939
|
const k = Math.round((pendingTokens || 0) / 1e3);
|
|
1302
1940
|
return `${k}k pend`;
|
|
@@ -1399,6 +2037,7 @@ function StatusChipView({
|
|
|
1399
2037
|
initialHealth,
|
|
1400
2038
|
initialSessionData,
|
|
1401
2039
|
initialSessionRead,
|
|
2040
|
+
initialTasksRead,
|
|
1402
2041
|
initialOpen = false
|
|
1403
2042
|
}) {
|
|
1404
2043
|
const [health, setHealth] = (0, import_react2.useState)(
|
|
@@ -1407,6 +2046,9 @@ function StatusChipView({
|
|
|
1407
2046
|
const [sessionRead, setSessionRead] = (0, import_react2.useState)(
|
|
1408
2047
|
initialSessionRead ?? (initialSessionData ? { status: "ok", session: initialSessionData } : null)
|
|
1409
2048
|
);
|
|
2049
|
+
const [tasksRead, setTasksRead] = (0, import_react2.useState)(
|
|
2050
|
+
initialTasksRead ?? null
|
|
2051
|
+
);
|
|
1410
2052
|
const [isOpen, setIsOpen] = (0, import_react2.useState)(initialOpen);
|
|
1411
2053
|
const [isCommitting, setIsCommitting] = (0, import_react2.useState)(false);
|
|
1412
2054
|
const [commitError, setCommitError] = (0, import_react2.useState)(null);
|
|
@@ -1442,16 +2084,24 @@ function StatusChipView({
|
|
|
1442
2084
|
if (!healthRes.ok) {
|
|
1443
2085
|
return;
|
|
1444
2086
|
}
|
|
1445
|
-
|
|
2087
|
+
const [session, tasks] = await Promise.all([
|
|
2088
|
+
apiClient.readSession(sessionId),
|
|
2089
|
+
apiClient.listTasks(sessionId)
|
|
2090
|
+
]);
|
|
2091
|
+
setSessionRead(session);
|
|
2092
|
+
setTasksRead(tasks);
|
|
1446
2093
|
} catch {
|
|
1447
2094
|
setHealth({ ok: false });
|
|
1448
2095
|
}
|
|
1449
2096
|
}, [sessionId, apiClient]);
|
|
2097
|
+
const breakdown = tasksRead?.status === "ok" ? tasksRead.breakdown : null;
|
|
2098
|
+
const hasActivePhase = shouldPollActive(breakdown);
|
|
1450
2099
|
(0, import_react2.useEffect)(() => {
|
|
1451
2100
|
fetchStatus();
|
|
1452
|
-
const
|
|
2101
|
+
const interval = hasActivePhase ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
|
|
2102
|
+
const timer = setInterval(fetchStatus, interval);
|
|
1453
2103
|
return () => clearInterval(timer);
|
|
1454
|
-
}, [fetchStatus]);
|
|
2104
|
+
}, [fetchStatus, hasActivePhase]);
|
|
1455
2105
|
(0, import_react2.useEffect)(() => {
|
|
1456
2106
|
function handleClickOutside(event) {
|
|
1457
2107
|
if (popoverRef.current && !popoverRef.current.contains(event.target)) {
|
|
@@ -1481,6 +2131,11 @@ function StatusChipView({
|
|
|
1481
2131
|
keep_recent_count: 10
|
|
1482
2132
|
});
|
|
1483
2133
|
if (res.ok) {
|
|
2134
|
+
if (res.task_id) {
|
|
2135
|
+
setTasksRead(
|
|
2136
|
+
(prev) => seedRunningTask(prev, res.task_id, res.resource_id)
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
1484
2139
|
await fetchStatus();
|
|
1485
2140
|
onCommit?.();
|
|
1486
2141
|
} else {
|
|
@@ -1499,11 +2154,15 @@ function StatusChipView({
|
|
|
1499
2154
|
const sessionUnreadable = isOnline && sessionRead !== null && sessionRead.status !== "ok";
|
|
1500
2155
|
const pendingTokens = sessionData?.pending_tokens ?? 0;
|
|
1501
2156
|
const recalledCount = recalledResult.recalledCount;
|
|
1502
|
-
const
|
|
2157
|
+
const rawDotState = getChipDotState({
|
|
1503
2158
|
isOnline,
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
);
|
|
2159
|
+
sessionUnreadable,
|
|
2160
|
+
breakdown
|
|
2161
|
+
});
|
|
2162
|
+
const dotState = isCommitting && (rawDotState === "online-idle" || rawDotState === "busy") ? "busy" : rawDotState;
|
|
2163
|
+
const dotBusy = dotState === "busy";
|
|
2164
|
+
const dotFailed = dotState === "extraction-failed";
|
|
2165
|
+
const statusColor = getDotColorForState(dotState);
|
|
1507
2166
|
const tooltipTitle = formatTooltipTitle({
|
|
1508
2167
|
isOnline,
|
|
1509
2168
|
isCommitting,
|
|
@@ -1520,6 +2179,7 @@ function StatusChipView({
|
|
|
1520
2179
|
style: { minWidth: 0, display: "inline-flex", position: "relative" },
|
|
1521
2180
|
ref: popoverRef,
|
|
1522
2181
|
children: [
|
|
2182
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ov-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } }` }),
|
|
1523
2183
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1524
2184
|
"button",
|
|
1525
2185
|
{
|
|
@@ -1550,17 +2210,32 @@ function StatusChipView({
|
|
|
1550
2210
|
title: tooltipTitle,
|
|
1551
2211
|
"aria-label": tooltipTitle,
|
|
1552
2212
|
children: [
|
|
1553
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2213
|
+
dotFailed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1554
2214
|
"span",
|
|
1555
2215
|
{
|
|
1556
2216
|
"data-testid": "status-dot",
|
|
2217
|
+
"data-dot-state": dotState,
|
|
2218
|
+
"aria-hidden": "true",
|
|
2219
|
+
style: {
|
|
2220
|
+
display: "inline-flex",
|
|
2221
|
+
color: statusColor,
|
|
2222
|
+
flexShrink: 0
|
|
2223
|
+
},
|
|
2224
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WarningGlyph, { size: 11, testId: "chip-warning-glyph" })
|
|
2225
|
+
}
|
|
2226
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2227
|
+
"span",
|
|
2228
|
+
{
|
|
2229
|
+
"data-testid": "status-dot",
|
|
2230
|
+
"data-dot-state": dotBusy ? "busy" : dotState,
|
|
1557
2231
|
"aria-hidden": "true",
|
|
1558
2232
|
style: {
|
|
1559
2233
|
width: "6px",
|
|
1560
2234
|
height: "6px",
|
|
1561
2235
|
borderRadius: "50%",
|
|
1562
2236
|
background: statusColor,
|
|
1563
|
-
flexShrink: 0
|
|
2237
|
+
flexShrink: 0,
|
|
2238
|
+
...dotBusy ? { animation: "ov-pulse 1.2s ease-in-out infinite" } : {}
|
|
1564
2239
|
}
|
|
1565
2240
|
}
|
|
1566
2241
|
),
|
|
@@ -1579,6 +2254,8 @@ function StatusChipView({
|
|
|
1579
2254
|
endpoint: apiClient.endpoint,
|
|
1580
2255
|
isCommitting,
|
|
1581
2256
|
commitError,
|
|
2257
|
+
breakdown,
|
|
2258
|
+
client: apiClient,
|
|
1582
2259
|
onCommitNow: handleCommitNow,
|
|
1583
2260
|
onClose: () => setIsOpen(false)
|
|
1584
2261
|
}
|