opencode-subagent-magazine 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/format.d.ts +7 -0
- package/dist/core/format.js +45 -0
- package/dist/core/kv.d.ts +21 -1
- package/dist/core/kv.js +33 -1
- package/dist/core/types.d.ts +12 -0
- package/dist/i18n.d.ts +19 -0
- package/dist/i18n.js +76 -0
- package/dist/index.js +73 -2
- package/dist/panel/SubAgentPanel.d.ts +5 -1
- package/dist/panel/SubAgentPanel.js +568 -261
- package/dist/panel/entry-map.d.ts +16 -0
- package/dist/panel/entry-map.js +74 -0
- package/dist/panel/panel-api.d.ts +8 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +8 -0
- package/dist/tui.js +927 -431
- package/dist/v2/commands.js +188 -44
- package/dist/v2/db.d.ts +47 -0
- package/dist/v2/db.js +267 -0
- package/dist/v2/index.js +26 -6
- package/dist/v2/settings-menu.d.ts +9 -0
- package/dist/v2/settings-menu.js +225 -0
- package/dist/v2/v2-panel-api.js +304 -44
- package/dist/v2.js +1741 -560
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/core/format.ts +44 -0
- package/src/core/kv.ts +42 -2
- package/src/core/types.ts +12 -0
- package/src/i18n.ts +76 -0
- package/src/index.tsx +93 -2
- package/src/panel/SubAgentPanel.tsx +541 -233
- package/src/panel/entry-map.ts +77 -1
- package/src/panel/panel-api.ts +8 -0
- package/src/server.ts +10 -1
- package/src/v2/commands.ts +187 -43
- package/src/v2/db.ts +274 -0
- package/src/v2/index.tsx +49 -7
- package/src/v2/settings-menu.ts +241 -0
- package/src/v2/types.ts +3 -1
- package/src/v2/v2-panel-api.ts +241 -33
package/dist/v2.js
CHANGED
|
@@ -2,6 +2,274 @@
|
|
|
2
2
|
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
3
3
|
import { createSignal as createSignal3 } from "solid-js";
|
|
4
4
|
|
|
5
|
+
// src/core/kv.ts
|
|
6
|
+
var KV_PREFIX = "subagent_magazine";
|
|
7
|
+
var SESSION_DATA_KEY = `${KV_PREFIX}.session_data`;
|
|
8
|
+
var SETTING_KEYS = {
|
|
9
|
+
lang: `${KV_PREFIX}.lang`,
|
|
10
|
+
maxEntries: `${KV_PREFIX}.max_entries`,
|
|
11
|
+
order: `${KV_PREFIX}.order`,
|
|
12
|
+
scrollMode: `${KV_PREFIX}.scroll_mode`,
|
|
13
|
+
open: `${KV_PREFIX}.open`,
|
|
14
|
+
ttlDays: `${KV_PREFIX}.ttl_days`,
|
|
15
|
+
border: `${KV_PREFIX}.border`,
|
|
16
|
+
showEntryCost: `${KV_PREFIX}.show_entry_cost`,
|
|
17
|
+
showEntryTime: `${KV_PREFIX}.show_entry_time`,
|
|
18
|
+
showEntryTokens: `${KV_PREFIX}.show_entry_tokens`,
|
|
19
|
+
timeFormat: `${KV_PREFIX}.time_format`,
|
|
20
|
+
dbSync: `${KV_PREFIX}.db_sync`
|
|
21
|
+
};
|
|
22
|
+
function loadSessionData(kv) {
|
|
23
|
+
try {
|
|
24
|
+
const raw = kv.get(SESSION_DATA_KEY, "{}");
|
|
25
|
+
return JSON.parse(String(raw));
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function updateSessionData(kv, mutator) {
|
|
31
|
+
try {
|
|
32
|
+
if (typeof kv.update === "function") {
|
|
33
|
+
return kv.update(SESSION_DATA_KEY, (raw) => {
|
|
34
|
+
const data2 = parseSessionData(raw);
|
|
35
|
+
mutator(data2);
|
|
36
|
+
return JSON.stringify(data2);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const data = loadSessionData(kv);
|
|
40
|
+
mutator(data);
|
|
41
|
+
kv.set(SESSION_DATA_KEY, JSON.stringify(data));
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function parseSessionData(raw) {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(String(raw ?? "{}"));
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function readTTLDays(kv) {
|
|
53
|
+
const ttlDaysRaw = parseInt(String(kv.get(SETTING_KEYS.ttlDays, "3")), 10);
|
|
54
|
+
return Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/v2/db.ts
|
|
58
|
+
import { homedir } from "node:os";
|
|
59
|
+
var BUN_SQLITE = "bun:sqlite";
|
|
60
|
+
function modelIdOf(value) {
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
const s = value.trim();
|
|
63
|
+
if (s.length === 0) return void 0;
|
|
64
|
+
if (s.startsWith("{")) {
|
|
65
|
+
try {
|
|
66
|
+
return modelIdOf(JSON.parse(s));
|
|
67
|
+
} catch {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return s;
|
|
72
|
+
}
|
|
73
|
+
if (value && typeof value === "object") {
|
|
74
|
+
const id = value.id;
|
|
75
|
+
if (id === void 0) return void 0;
|
|
76
|
+
const direct = modelIdOf(id);
|
|
77
|
+
if (direct !== void 0) return direct;
|
|
78
|
+
const s = String(id);
|
|
79
|
+
return s.length > 0 ? s : void 0;
|
|
80
|
+
}
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
function contextTokens(tokens) {
|
|
84
|
+
if (!tokens || typeof tokens !== "object") return void 0;
|
|
85
|
+
const t = tokens;
|
|
86
|
+
const cache = t.cache && typeof t.cache === "object" ? t.cache : {};
|
|
87
|
+
const sum = (Number(t.input) || 0) + (Number(t.output) || 0) + (Number(t.reasoning) || 0) + (Number(cache.read) || 0) + (Number(cache.write) || 0);
|
|
88
|
+
return sum > 0 ? sum : void 0;
|
|
89
|
+
}
|
|
90
|
+
function statusOfChild(info) {
|
|
91
|
+
if (!info || info.timeIdle == null) return void 0;
|
|
92
|
+
return info.idleOutcome === "interrupted" ? "cancelled" : "done";
|
|
93
|
+
}
|
|
94
|
+
function findDbPath(env = process.env, home = safeHomedir()) {
|
|
95
|
+
if (env.OPENCODE_DB) return env.OPENCODE_DB;
|
|
96
|
+
const base = env.XDG_DATA_HOME && env.XDG_DATA_HOME.length > 0 ? env.XDG_DATA_HOME : `${home}/.local/share`;
|
|
97
|
+
if (!base || base === "/.local/share") return void 0;
|
|
98
|
+
return `${base}/opencode/opencode.db`;
|
|
99
|
+
}
|
|
100
|
+
function safeHomedir() {
|
|
101
|
+
try {
|
|
102
|
+
return homedir();
|
|
103
|
+
} catch {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var CALL_MAP_SQL = `
|
|
108
|
+
SELECT json_extract(p.value, '$.id') AS call_id,
|
|
109
|
+
json_extract(p.value, '$.state.metadata.sessionID') AS child_sid
|
|
110
|
+
FROM session_message m, json_each(m.data, '$.content') p
|
|
111
|
+
WHERE m.session_id = ? AND m.type = 'assistant'
|
|
112
|
+
AND json_extract(p.value, '$.id') LIKE 'call_%'
|
|
113
|
+
AND json_extract(p.value, '$.state.metadata.sessionID') IS NOT NULL`;
|
|
114
|
+
var CHILD_INFO_SQL = `
|
|
115
|
+
SELECT parent_id, agent, title, model, cost,
|
|
116
|
+
tokens_input, tokens_output, tokens_reasoning, tokens_cache_read, tokens_cache_write,
|
|
117
|
+
time_created, time_idle, idle_outcome
|
|
118
|
+
FROM session_v2 WHERE id = ?`;
|
|
119
|
+
var LAST_ASSISTANT_SQL = `
|
|
120
|
+
SELECT json_extract(data, '$.tokens.input') AS input,
|
|
121
|
+
json_extract(data, '$.tokens.output') AS output,
|
|
122
|
+
json_extract(data, '$.tokens.reasoning') AS reasoning,
|
|
123
|
+
json_extract(data, '$.tokens.cache.read') AS cache_read,
|
|
124
|
+
json_extract(data, '$.tokens.cache.write') AS cache_write,
|
|
125
|
+
json_extract(data, '$.model.id') AS model_id
|
|
126
|
+
FROM session_message
|
|
127
|
+
WHERE session_id = ? AND type = 'assistant' AND json_extract(data, '$.tokens.output') > 0
|
|
128
|
+
ORDER BY seq DESC LIMIT 1`;
|
|
129
|
+
var ASSISTANT_COST_SQL = `
|
|
130
|
+
SELECT COUNT(*) AS n, SUM(COALESCE(json_extract(data, '$.cost'), 0)) AS total
|
|
131
|
+
FROM session_message WHERE session_id = ? AND type = 'assistant'`;
|
|
132
|
+
var CHILDREN_SQL = `
|
|
133
|
+
SELECT id, agent, time_created FROM session_v2 WHERE parent_id = ? ORDER BY time_created`;
|
|
134
|
+
var num = (v) => {
|
|
135
|
+
const n = Number(v);
|
|
136
|
+
return Number.isFinite(n) ? n : void 0;
|
|
137
|
+
};
|
|
138
|
+
var str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
139
|
+
function createSessionDbIndex(enabled) {
|
|
140
|
+
const dbPath = findDbPath();
|
|
141
|
+
if (!dbPath) return void 0;
|
|
142
|
+
let db;
|
|
143
|
+
let failed = false;
|
|
144
|
+
void (async () => {
|
|
145
|
+
try {
|
|
146
|
+
const mod2 = await import(BUN_SQLITE);
|
|
147
|
+
const Database = mod2?.Database;
|
|
148
|
+
if (!Database) {
|
|
149
|
+
failed = true;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
db = new Database(dbPath, { readonly: true });
|
|
153
|
+
try {
|
|
154
|
+
db.query("pragma query_only = on").all();
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
failed = true;
|
|
159
|
+
db = void 0;
|
|
160
|
+
}
|
|
161
|
+
})();
|
|
162
|
+
const CALL_MAP_TTL_MS = 15e3;
|
|
163
|
+
const CALL_MAP_RETRY_MS = 2e3;
|
|
164
|
+
const INFO_TTL_MS = 5e3;
|
|
165
|
+
const callMaps = /* @__PURE__ */ new Map();
|
|
166
|
+
const infos = /* @__PURE__ */ new Map();
|
|
167
|
+
const childrenCache = /* @__PURE__ */ new Map();
|
|
168
|
+
const ready = () => {
|
|
169
|
+
if (failed || db === void 0) return false;
|
|
170
|
+
try {
|
|
171
|
+
return enabled();
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const buildCallMap = (parentId) => {
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
const prev = callMaps.get(parentId);
|
|
179
|
+
if (prev && now - prev.at < CALL_MAP_RETRY_MS) return prev.map;
|
|
180
|
+
const map = /* @__PURE__ */ new Map();
|
|
181
|
+
try {
|
|
182
|
+
for (const row of db.query(CALL_MAP_SQL).all(parentId)) {
|
|
183
|
+
const id = str(row.call_id);
|
|
184
|
+
const sid = str(row.child_sid);
|
|
185
|
+
if (id && sid) map.set(id, sid);
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
callMaps.set(parentId, { map, at: now });
|
|
190
|
+
return map;
|
|
191
|
+
};
|
|
192
|
+
const resolveCall = (parentId, callId) => {
|
|
193
|
+
if (!ready() || !parentId || !callId) return void 0;
|
|
194
|
+
const prev = callMaps.get(parentId);
|
|
195
|
+
const map = prev && Date.now() - prev.at < CALL_MAP_TTL_MS ? prev.map : buildCallMap(parentId);
|
|
196
|
+
return map.get(callId) ?? buildCallMap(parentId).get(callId);
|
|
197
|
+
};
|
|
198
|
+
const matchChild = (parentId, agent, startedAt) => {
|
|
199
|
+
if (!ready() || !parentId || startedAt === void 0) return void 0;
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
let cached = childrenCache.get(parentId);
|
|
202
|
+
if (!cached || now - cached.at > CALL_MAP_TTL_MS) {
|
|
203
|
+
let list = [];
|
|
204
|
+
try {
|
|
205
|
+
list = db.query(CHILDREN_SQL).all(parentId);
|
|
206
|
+
} catch {
|
|
207
|
+
}
|
|
208
|
+
cached = { list, at: now };
|
|
209
|
+
childrenCache.set(parentId, cached);
|
|
210
|
+
}
|
|
211
|
+
let best;
|
|
212
|
+
let bestAt = Number.POSITIVE_INFINITY;
|
|
213
|
+
for (const row of cached.list) {
|
|
214
|
+
const sid = str(row.id);
|
|
215
|
+
const t = num(row.time_created);
|
|
216
|
+
if (!sid || t === void 0) continue;
|
|
217
|
+
if (t < startedAt - 2e3) continue;
|
|
218
|
+
if (agent !== void 0 && str(row.agent) !== agent) continue;
|
|
219
|
+
if (t < bestAt) {
|
|
220
|
+
bestAt = t;
|
|
221
|
+
best = sid;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return best;
|
|
225
|
+
};
|
|
226
|
+
const info = (sid) => {
|
|
227
|
+
if (!sid) return void 0;
|
|
228
|
+
const hit = infos.get(sid);
|
|
229
|
+
if (hit && Date.now() - hit.at < INFO_TTL_MS) return hit.info;
|
|
230
|
+
if (!ready()) return void 0;
|
|
231
|
+
let result;
|
|
232
|
+
try {
|
|
233
|
+
const row = db.query(CHILD_INFO_SQL).get(sid);
|
|
234
|
+
if (row) {
|
|
235
|
+
const last = db.query(LAST_ASSISTANT_SQL).get(sid);
|
|
236
|
+
let tokens = contextTokens(last ? {
|
|
237
|
+
input: last.input,
|
|
238
|
+
output: last.output,
|
|
239
|
+
reasoning: last.reasoning,
|
|
240
|
+
cache: { read: last.cache_read, write: last.cache_write }
|
|
241
|
+
} : void 0);
|
|
242
|
+
if (tokens === void 0) {
|
|
243
|
+
const agg = (num(row.tokens_input) || 0) + (num(row.tokens_output) || 0) + (num(row.tokens_reasoning) || 0) + (num(row.tokens_cache_read) || 0) + (num(row.tokens_cache_write) || 0);
|
|
244
|
+
if (agg > 0) tokens = agg;
|
|
245
|
+
}
|
|
246
|
+
let cost = num(row.cost);
|
|
247
|
+
if (!(typeof cost === "number" && cost > 0)) {
|
|
248
|
+
const aggCost = db.query(ASSISTANT_COST_SQL).get(sid);
|
|
249
|
+
if (aggCost && Number(aggCost.n) > 0) cost = Number(aggCost.total) || 0;
|
|
250
|
+
}
|
|
251
|
+
result = {
|
|
252
|
+
id: sid,
|
|
253
|
+
parentId: str(row.parent_id),
|
|
254
|
+
agent: str(row.agent),
|
|
255
|
+
title: str(row.title),
|
|
256
|
+
model: modelIdOf(last?.model_id) ?? modelIdOf(row.model),
|
|
257
|
+
cost,
|
|
258
|
+
tokens,
|
|
259
|
+
timeCreated: num(row.time_created),
|
|
260
|
+
timeIdle: num(row.time_idle),
|
|
261
|
+
idleOutcome: str(row.idle_outcome)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
result = void 0;
|
|
266
|
+
}
|
|
267
|
+
infos.set(sid, { info: result, at: Date.now() });
|
|
268
|
+
return result;
|
|
269
|
+
};
|
|
270
|
+
return { enabled: ready, resolveCall, matchChild, info };
|
|
271
|
+
}
|
|
272
|
+
|
|
5
273
|
// src/v2/v2-panel-api.ts
|
|
6
274
|
var SUBAGENT_TOOL_V2 = "subagent";
|
|
7
275
|
function normalizeMeta(meta) {
|
|
@@ -18,7 +286,11 @@ function toV1Part(p) {
|
|
|
18
286
|
if (st.output === void 0 && Array.isArray(st.content)) {
|
|
19
287
|
st.output = st.content.map((c) => c.type === "text" ? c.text : c.type === "file" ? String(c.path ?? c.filename ?? "") : JSON.stringify(c)).filter((t) => typeof t === "string" && t.length > 0).join("\n");
|
|
20
288
|
}
|
|
21
|
-
|
|
289
|
+
const meta = normalizeMeta({
|
|
290
|
+
...st.metadata ?? {},
|
|
291
|
+
...p.metadata ?? {}
|
|
292
|
+
});
|
|
293
|
+
return { type: "tool", id: p.id, tool: p.tool ?? p.name, state: st, subagent_type: p.subagent_type, metadata: meta };
|
|
22
294
|
}
|
|
23
295
|
if (p.type === "text") return { type: "text", text: String(p.text ?? "") };
|
|
24
296
|
if (p.type === "file") return { type: "file", source: p.source ?? {} };
|
|
@@ -55,7 +327,60 @@ function createPanelApi(context, settings) {
|
|
|
55
327
|
d.value = value;
|
|
56
328
|
});
|
|
57
329
|
};
|
|
330
|
+
const kvUpdate = (key, updater) => {
|
|
331
|
+
let entry = kvStore.get(key);
|
|
332
|
+
if (!entry) {
|
|
333
|
+
const created = context.storage.store(`subagent_magazine.${key}`, {
|
|
334
|
+
initial: { value: void 0 }
|
|
335
|
+
});
|
|
336
|
+
entry = created;
|
|
337
|
+
kvStore.set(key, entry);
|
|
338
|
+
}
|
|
339
|
+
const [, mutate] = entry;
|
|
340
|
+
return mutate((d) => {
|
|
341
|
+
d.value = updater(d.value);
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
const dbIndex = createSessionDbIndex(() => kvGet(SETTING_KEYS.dbSync, true) !== false);
|
|
345
|
+
const withResolvedSid = (data, meta) => {
|
|
346
|
+
if (meta.sessionID !== void 0) return meta;
|
|
347
|
+
try {
|
|
348
|
+
const parent = data.sessionID !== void 0 ? String(data.sessionID) : "";
|
|
349
|
+
const callId = data.id !== void 0 ? String(data.id) : "";
|
|
350
|
+
const sid = parent && callId ? dbIndex?.resolveCall(parent, callId) : void 0;
|
|
351
|
+
if (sid) return normalizeMeta({ ...meta, sessionID: sid });
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
return meta;
|
|
355
|
+
};
|
|
58
356
|
const isSubagentTool = (name) => name === SUBAGENT_TOOL_V2 || name === "task" || name === "delegate" || name === "call_omo_agent";
|
|
357
|
+
const findToolPart = (data) => {
|
|
358
|
+
const sid = data.sessionID;
|
|
359
|
+
const msgID = data.assistantMessageID;
|
|
360
|
+
const partID = data.id;
|
|
361
|
+
if (sid === void 0 || msgID === void 0 || partID === void 0) return void 0;
|
|
362
|
+
try {
|
|
363
|
+
const raw = context.data.session.message.list(String(sid));
|
|
364
|
+
if (!raw) return void 0;
|
|
365
|
+
for (let i = raw.length - 1; i >= 0; i--) {
|
|
366
|
+
const m = raw[i];
|
|
367
|
+
if (!m || String(m.id) !== String(msgID)) continue;
|
|
368
|
+
const content = Array.isArray(m.content) ? m.content : [];
|
|
369
|
+
for (const p of content) {
|
|
370
|
+
const pp = p;
|
|
371
|
+
if (pp && pp.type === "tool" && String(pp.id) === String(partID)) {
|
|
372
|
+
const st = pp.state ?? {};
|
|
373
|
+
const input = st.input && typeof st.input === "object" ? st.input : void 0;
|
|
374
|
+
const name = pp.name ?? pp.tool;
|
|
375
|
+
return { name: name !== void 0 ? String(name) : void 0, input };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
return void 0;
|
|
383
|
+
};
|
|
59
384
|
const toolEventToPart = (event) => {
|
|
60
385
|
const type = event.type;
|
|
61
386
|
const data = event.data;
|
|
@@ -73,20 +398,29 @@ function createPanelApi(context, settings) {
|
|
|
73
398
|
return { type: "tool", tool: normName(String(data.name)), id: String(data.id), state: { status: "pending", input: {} } };
|
|
74
399
|
}
|
|
75
400
|
if (type === "session.tool.called") {
|
|
76
|
-
|
|
401
|
+
let name = data.name ?? info?.name;
|
|
402
|
+
let input = data.input ?? info?.input;
|
|
403
|
+
if (!name) {
|
|
404
|
+
const resolved = findToolPart(data);
|
|
405
|
+
if (resolved?.name) {
|
|
406
|
+
name = resolved.name;
|
|
407
|
+
if (input === void 0) input = resolved.input;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
77
410
|
if (name && !isSubagentTool(name)) return void 0;
|
|
78
411
|
if (!name) return void 0;
|
|
79
|
-
if (key) toolInfo.set(key, { name: String(name), input:
|
|
412
|
+
if (key) toolInfo.set(key, { name: String(name), input: input ?? {} });
|
|
80
413
|
return {
|
|
81
414
|
type: "tool",
|
|
82
415
|
tool: normName(String(name)),
|
|
83
416
|
id: String(data.id),
|
|
84
|
-
subagent_type: agentOf(
|
|
85
|
-
state: { status: "running", input:
|
|
417
|
+
subagent_type: agentOf(input),
|
|
418
|
+
state: { status: "running", input: input ?? {}, metadata: withResolvedSid(data, {}) }
|
|
86
419
|
};
|
|
87
420
|
}
|
|
88
421
|
if (type === "session.tool.progress") {
|
|
89
|
-
|
|
422
|
+
let name = data.name ?? info?.name;
|
|
423
|
+
if (!name) name = findToolPart(data)?.name;
|
|
90
424
|
if (name && !isSubagentTool(name)) return void 0;
|
|
91
425
|
if (!name) return void 0;
|
|
92
426
|
return {
|
|
@@ -94,24 +428,42 @@ function createPanelApi(context, settings) {
|
|
|
94
428
|
tool: normName(String(name)),
|
|
95
429
|
id: String(data.id),
|
|
96
430
|
subagent_type: agentOf(info?.input),
|
|
97
|
-
state: { status: "running", input: info?.input ?? {}, metadata: normalizeMeta(data.metadata) }
|
|
431
|
+
state: { status: "running", input: info?.input ?? {}, metadata: withResolvedSid(data, normalizeMeta(data.metadata)) }
|
|
98
432
|
};
|
|
99
433
|
}
|
|
100
434
|
if (type === "session.tool.success" || type === "session.tool.failed") {
|
|
101
|
-
const
|
|
102
|
-
|
|
435
|
+
const eventMeta = normalizeMeta(data.metadata);
|
|
436
|
+
const meta = withResolvedSid(data, eventMeta);
|
|
103
437
|
if (type === "session.tool.failed" && meta.status === "running") return void 0;
|
|
104
|
-
|
|
438
|
+
let name = data.name ?? info?.name;
|
|
439
|
+
let input = info?.input ?? data.input;
|
|
440
|
+
if (!name || input === void 0) {
|
|
441
|
+
const resolved = findToolPart(data);
|
|
442
|
+
if (!name && resolved?.name) name = resolved.name;
|
|
443
|
+
if (input === void 0 && resolved?.input) input = resolved.input;
|
|
444
|
+
}
|
|
105
445
|
if (name && !isSubagentTool(name)) return void 0;
|
|
106
|
-
|
|
446
|
+
if (key && name) toolInfo.set(key, { name: String(name), input: input ?? info?.input ?? {} });
|
|
447
|
+
const finalInput = input ?? {};
|
|
448
|
+
if (meta.sessionID === void 0 && (finalInput.background === true || finalInput.run_in_background === true)) {
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
let status = type === "session.tool.failed" ? "error" : "completed";
|
|
452
|
+
if (meta.sessionID !== void 0 && meta.sessionID !== eventMeta.sessionID) {
|
|
453
|
+
try {
|
|
454
|
+
const childInfo = dbIndex?.info(String(meta.sessionID));
|
|
455
|
+
if (childInfo && statusOfChild(childInfo) === void 0) status = "running";
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
}
|
|
107
459
|
return {
|
|
108
460
|
type: "tool",
|
|
109
461
|
tool: normName(String(name ?? "task")),
|
|
110
462
|
id: String(data.id),
|
|
111
|
-
subagent_type: agentOf(
|
|
463
|
+
subagent_type: agentOf(finalInput),
|
|
112
464
|
state: {
|
|
113
|
-
status
|
|
114
|
-
input,
|
|
465
|
+
status,
|
|
466
|
+
input: finalInput,
|
|
115
467
|
metadata: meta
|
|
116
468
|
}
|
|
117
469
|
};
|
|
@@ -133,14 +485,55 @@ function createPanelApi(context, settings) {
|
|
|
133
485
|
}
|
|
134
486
|
return out;
|
|
135
487
|
};
|
|
488
|
+
const usageCache = /* @__PURE__ */ new Map();
|
|
489
|
+
const syncRequestedAt = /* @__PURE__ */ new Map();
|
|
490
|
+
const USAGE_CACHE_TTL_MS = 5e3;
|
|
491
|
+
const SYNC_THROTTLE_MS = 3e4;
|
|
492
|
+
const rememberUsage = (sid, patch) => {
|
|
493
|
+
const prev = usageCache.get(sid);
|
|
494
|
+
usageCache.set(sid, { ...prev, ...patch, at: Date.now() });
|
|
495
|
+
};
|
|
496
|
+
const cachedUsage = (sid) => {
|
|
497
|
+
const hit = usageCache.get(sid);
|
|
498
|
+
if (!hit) return void 0;
|
|
499
|
+
if (Date.now() - hit.at > USAGE_CACHE_TTL_MS) return void 0;
|
|
500
|
+
return hit;
|
|
501
|
+
};
|
|
502
|
+
const requestMessageSync = (sid) => {
|
|
503
|
+
const last = syncRequestedAt.get(sid) ?? 0;
|
|
504
|
+
if (Date.now() - last < SYNC_THROTTLE_MS) return;
|
|
505
|
+
syncRequestedAt.set(sid, Date.now());
|
|
506
|
+
try {
|
|
507
|
+
const sync = context.data.session.message.sync;
|
|
508
|
+
if (typeof sync === "function") void sync.call(context.data.session.message, sid).catch(() => {
|
|
509
|
+
});
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
const listedSession = (sid) => {
|
|
514
|
+
try {
|
|
515
|
+
return context.data.session.list()?.find((s) => s.id === sid);
|
|
516
|
+
} catch {
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
136
520
|
return {
|
|
137
521
|
kv: {
|
|
138
522
|
get: kvGet,
|
|
139
|
-
set: kvSet
|
|
523
|
+
set: kvSet,
|
|
524
|
+
update: kvUpdate
|
|
140
525
|
},
|
|
141
526
|
usage: {
|
|
142
527
|
readSessionTokens: (sid) => {
|
|
143
528
|
if (!sid) return void 0;
|
|
529
|
+
try {
|
|
530
|
+
const dbTokens = dbIndex?.info(sid)?.tokens;
|
|
531
|
+
if (dbTokens !== void 0) {
|
|
532
|
+
rememberUsage(sid, { tokens: dbTokens });
|
|
533
|
+
return dbTokens;
|
|
534
|
+
}
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
144
537
|
try {
|
|
145
538
|
const msgs = context.data.session.message.list(sid);
|
|
146
539
|
if (msgs) {
|
|
@@ -150,9 +543,15 @@ function createPanelApi(context, settings) {
|
|
|
150
543
|
const t = m.tokens;
|
|
151
544
|
if (!t || !(Number(t.output) > 0)) continue;
|
|
152
545
|
const ctx = (Number(t.input) || 0) + (Number(t.output) || 0) + (Number(t.reasoning) || 0) + (Number(t.cache?.read) || 0) + (Number(t.cache?.write) || 0);
|
|
153
|
-
if (ctx > 0)
|
|
546
|
+
if (ctx > 0) {
|
|
547
|
+
rememberUsage(sid, { tokens: ctx });
|
|
548
|
+
return ctx;
|
|
549
|
+
}
|
|
154
550
|
}
|
|
155
551
|
}
|
|
552
|
+
const cached = cachedUsage(sid)?.tokens;
|
|
553
|
+
if (cached !== void 0) return cached;
|
|
554
|
+
requestMessageSync(sid);
|
|
156
555
|
return void 0;
|
|
157
556
|
} catch {
|
|
158
557
|
return void 0;
|
|
@@ -160,33 +559,91 @@ function createPanelApi(context, settings) {
|
|
|
160
559
|
},
|
|
161
560
|
readSessionCost: (sid) => {
|
|
162
561
|
if (!sid) return void 0;
|
|
562
|
+
try {
|
|
563
|
+
const dbCost = dbIndex?.info(sid)?.cost;
|
|
564
|
+
if (dbCost !== void 0) {
|
|
565
|
+
rememberUsage(sid, { cost: dbCost });
|
|
566
|
+
return dbCost;
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
const direct = context.data.session.cost(sid);
|
|
572
|
+
if (typeof direct === "number" && direct > 0) {
|
|
573
|
+
rememberUsage(sid, { cost: direct });
|
|
574
|
+
return direct;
|
|
575
|
+
}
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
163
578
|
try {
|
|
164
579
|
const session = context.data.session.get(sid);
|
|
165
|
-
if (session?.cost != null && session.cost > 0)
|
|
580
|
+
if (session?.cost != null && session.cost > 0) {
|
|
581
|
+
rememberUsage(sid, { cost: session.cost });
|
|
582
|
+
return session.cost;
|
|
583
|
+
}
|
|
584
|
+
const listed = listedSession(sid);
|
|
585
|
+
if (listed?.cost != null && listed.cost > 0) {
|
|
586
|
+
rememberUsage(sid, { cost: listed.cost });
|
|
587
|
+
return listed.cost;
|
|
588
|
+
}
|
|
166
589
|
const msgs = context.data.session.message.list(sid);
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
590
|
+
if (Array.isArray(msgs) && msgs.length > 0) {
|
|
591
|
+
let total = 0;
|
|
592
|
+
let sawAssistant = false;
|
|
593
|
+
for (const m of msgs) {
|
|
594
|
+
if (m.type !== "assistant") continue;
|
|
595
|
+
sawAssistant = true;
|
|
596
|
+
if (typeof m.cost === "number") total += m.cost;
|
|
597
|
+
}
|
|
598
|
+
if (sawAssistant) {
|
|
599
|
+
rememberUsage(sid, { cost: total });
|
|
600
|
+
return total;
|
|
601
|
+
}
|
|
171
602
|
}
|
|
172
|
-
|
|
603
|
+
const cached = cachedUsage(sid)?.cost;
|
|
604
|
+
if (cached !== void 0) return cached;
|
|
605
|
+
requestMessageSync(sid);
|
|
606
|
+
return void 0;
|
|
173
607
|
} catch {
|
|
174
608
|
return void 0;
|
|
175
609
|
}
|
|
176
610
|
},
|
|
177
611
|
readSessionModel: (sid) => {
|
|
178
612
|
if (!sid) return void 0;
|
|
613
|
+
try {
|
|
614
|
+
const dbModel = dbIndex?.info(sid)?.model;
|
|
615
|
+
if (dbModel !== void 0) {
|
|
616
|
+
rememberUsage(sid, { model: dbModel });
|
|
617
|
+
return dbModel;
|
|
618
|
+
}
|
|
619
|
+
} catch {
|
|
620
|
+
}
|
|
179
621
|
try {
|
|
180
622
|
const msgs = context.data.session.message.list(sid);
|
|
181
623
|
if (msgs) {
|
|
182
624
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
183
625
|
const m = msgs[i];
|
|
184
626
|
if (m.type !== "assistant") continue;
|
|
185
|
-
const
|
|
186
|
-
if (
|
|
187
|
-
|
|
627
|
+
const id = modelIdOf(m.model);
|
|
628
|
+
if (id) {
|
|
629
|
+
rememberUsage(sid, { model: id });
|
|
630
|
+
return id;
|
|
631
|
+
}
|
|
188
632
|
}
|
|
189
633
|
}
|
|
634
|
+
const sessionModel = modelIdOf(context.data.session.get(sid)?.model);
|
|
635
|
+
if (sessionModel) {
|
|
636
|
+
rememberUsage(sid, { model: sessionModel });
|
|
637
|
+
return sessionModel;
|
|
638
|
+
}
|
|
639
|
+
const listedModel = modelIdOf(listedSession(sid)?.model);
|
|
640
|
+
if (listedModel) {
|
|
641
|
+
rememberUsage(sid, { model: listedModel });
|
|
642
|
+
return listedModel;
|
|
643
|
+
}
|
|
644
|
+
const cached = cachedUsage(sid)?.model;
|
|
645
|
+
if (cached !== void 0) return cached;
|
|
646
|
+
requestMessageSync(sid);
|
|
190
647
|
return void 0;
|
|
191
648
|
} catch {
|
|
192
649
|
return void 0;
|
|
@@ -198,7 +655,14 @@ function createPanelApi(context, settings) {
|
|
|
198
655
|
session: {
|
|
199
656
|
get: (sid) => {
|
|
200
657
|
try {
|
|
201
|
-
|
|
658
|
+
const direct = context.data.session.get(sid);
|
|
659
|
+
if (direct) return direct;
|
|
660
|
+
const list = context.data.session.list?.();
|
|
661
|
+
if (Array.isArray(list)) {
|
|
662
|
+
const found = list.find((s) => s?.id === sid);
|
|
663
|
+
if (found) return found;
|
|
664
|
+
}
|
|
665
|
+
return void 0;
|
|
202
666
|
} catch {
|
|
203
667
|
return void 0;
|
|
204
668
|
}
|
|
@@ -206,11 +670,17 @@ function createPanelApi(context, settings) {
|
|
|
206
670
|
status: (sid) => {
|
|
207
671
|
try {
|
|
208
672
|
const st = context.data.session.status(sid);
|
|
209
|
-
|
|
210
|
-
|
|
673
|
+
if (st) {
|
|
674
|
+
const type = st === "running" ? "busy" : st;
|
|
675
|
+
return { type };
|
|
676
|
+
}
|
|
211
677
|
} catch {
|
|
212
|
-
return void 0;
|
|
213
678
|
}
|
|
679
|
+
try {
|
|
680
|
+
if (statusOfChild(dbIndex?.info(sid)) !== void 0) return { type: "idle" };
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
return void 0;
|
|
214
684
|
},
|
|
215
685
|
messages: (sid) => {
|
|
216
686
|
try {
|
|
@@ -229,6 +699,15 @@ function createPanelApi(context, settings) {
|
|
|
229
699
|
return [];
|
|
230
700
|
}
|
|
231
701
|
return msg.content.map((p) => toV1Part(p));
|
|
702
|
+
},
|
|
703
|
+
resolveChild: (input) => {
|
|
704
|
+
try {
|
|
705
|
+
const sid = input.callId ? dbIndex?.resolveCall(input.parentId, input.callId) : void 0;
|
|
706
|
+
if (sid) return sid;
|
|
707
|
+
return dbIndex?.matchChild(input.parentId, input.agent, input.startedAt);
|
|
708
|
+
} catch {
|
|
709
|
+
return void 0;
|
|
710
|
+
}
|
|
232
711
|
}
|
|
233
712
|
},
|
|
234
713
|
event: {
|
|
@@ -239,9 +718,11 @@ function createPanelApi(context, settings) {
|
|
|
239
718
|
for (const evt of ["session.tool.input.started", "session.tool.called", "session.tool.progress", "session.tool.success", "session.tool.failed"]) {
|
|
240
719
|
unsubs.push(context.data.on(evt, (e) => {
|
|
241
720
|
const part = toolEventToPart(e);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
721
|
+
const evtData = e.data;
|
|
722
|
+
if (!part) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const sid = evtData?.sessionID !== void 0 ? String(evtData.sessionID) : void 0;
|
|
245
726
|
if (!sid) return;
|
|
246
727
|
cb({ type, scope: "global", payload: { part, sessionID: sid } });
|
|
247
728
|
}));
|
|
@@ -298,39 +779,90 @@ function createPanelApi(context, settings) {
|
|
|
298
779
|
};
|
|
299
780
|
}
|
|
300
781
|
|
|
301
|
-
// src/core/
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
782
|
+
// src/core/format.ts
|
|
783
|
+
function charColumns(c) {
|
|
784
|
+
const code = c.codePointAt(0) ?? 0;
|
|
785
|
+
if (code < 32) return 0;
|
|
786
|
+
if (code < 127) return 1;
|
|
787
|
+
if (code < 160) return 0;
|
|
788
|
+
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 128591 || code >= 131072 && code <= 262141)
|
|
789
|
+
return 2;
|
|
790
|
+
return 1;
|
|
791
|
+
}
|
|
792
|
+
function visualWidth(s) {
|
|
793
|
+
let w = 0;
|
|
794
|
+
for (const c of s) w += charColumns(c);
|
|
795
|
+
return w;
|
|
796
|
+
}
|
|
797
|
+
function truncate(text, maxCols) {
|
|
798
|
+
if (visualWidth(text) <= maxCols) return text;
|
|
799
|
+
let cols = 0;
|
|
800
|
+
let i = 0;
|
|
801
|
+
for (const c of text) {
|
|
802
|
+
const w = charColumns(c);
|
|
803
|
+
if (cols + w > maxCols - 1) break;
|
|
804
|
+
cols += w;
|
|
805
|
+
i += c.length;
|
|
319
806
|
}
|
|
807
|
+
return text.slice(0, i) + "\u2026";
|
|
320
808
|
}
|
|
321
|
-
function
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
809
|
+
function fmtDurationShort(ms, running) {
|
|
810
|
+
if (running && ms < 2e3) return "";
|
|
811
|
+
if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
|
|
812
|
+
if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
|
|
813
|
+
const m = Math.floor(ms / 6e4);
|
|
814
|
+
const s = Math.round(ms % 6e4 / 1e3);
|
|
815
|
+
return `${m}m${s}s`;
|
|
816
|
+
}
|
|
817
|
+
var TIME_FORMATS = ["short", "decimal", "clock", "compact", "seconds"];
|
|
818
|
+
var TIME_FORMAT_SAMPLES = {
|
|
819
|
+
decimal: "45.3s \xB7 9.5m \xB7 1.9h",
|
|
820
|
+
short: "45.32s \xB7 112m35s",
|
|
821
|
+
clock: "0:45 \xB7 1:52:35",
|
|
822
|
+
compact: "45s \xB7 1h52m",
|
|
823
|
+
seconds: "45s \xB7 6755s"
|
|
824
|
+
};
|
|
825
|
+
function fmtDuration(ms, running, mode = "short") {
|
|
826
|
+
if (running && ms < 2e3) return "";
|
|
827
|
+
const t = Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
828
|
+
switch (mode) {
|
|
829
|
+
case "short":
|
|
830
|
+
return fmtDurationShort(t, false);
|
|
831
|
+
case "compact": {
|
|
832
|
+
if (t < 6e4) return `${Math.floor(t / 1e3)}s`;
|
|
833
|
+
if (t < 36e5) return `${Math.floor(t / 6e4)}m${Math.floor(t % 6e4 / 1e3)}s`;
|
|
834
|
+
return `${Math.floor(t / 36e5)}h${Math.floor(t % 36e5 / 6e4)}m`;
|
|
835
|
+
}
|
|
836
|
+
case "clock": {
|
|
837
|
+
const s = Math.floor(t % 6e4 / 1e3);
|
|
838
|
+
if (t < 36e5) return `${Math.floor(t / 6e4)}:${String(s).padStart(2, "0")}`;
|
|
839
|
+
const m = Math.floor(t % 36e5 / 6e4);
|
|
840
|
+
return `${Math.floor(t / 36e5)}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
841
|
+
}
|
|
842
|
+
case "seconds":
|
|
843
|
+
return `${Math.floor(t / 1e3)}s`;
|
|
844
|
+
case "decimal":
|
|
845
|
+
default: {
|
|
846
|
+
if (t < 59950) return `${(t / 1e3).toFixed(1)}s`;
|
|
847
|
+
if (t < 3597e3) return `${(t / 6e4).toFixed(1)}m`;
|
|
848
|
+
return `${(t / 36e5).toFixed(1)}h`;
|
|
849
|
+
}
|
|
325
850
|
}
|
|
326
851
|
}
|
|
327
|
-
function
|
|
328
|
-
|
|
329
|
-
|
|
852
|
+
function fmtTokens(n) {
|
|
853
|
+
if (n < 1e3) return `${n}`;
|
|
854
|
+
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
855
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
856
|
+
}
|
|
857
|
+
function safeErrorMsg(err) {
|
|
858
|
+
if (!err) return "";
|
|
859
|
+
if (typeof err === "string") return err;
|
|
860
|
+
if (typeof err === "object") return String(err.message || err.code || "");
|
|
861
|
+
return "";
|
|
330
862
|
}
|
|
331
863
|
|
|
332
864
|
// src/_version.ts
|
|
333
|
-
var PLUGIN_VERSION = "1.
|
|
865
|
+
var PLUGIN_VERSION = "1.7.0";
|
|
334
866
|
|
|
335
867
|
// src/i18n.ts
|
|
336
868
|
var ZH_T = {
|
|
@@ -374,6 +906,7 @@ var ZH_T = {
|
|
|
374
906
|
"clear.prompt": "\u786E\u5B9A\u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6240\u6709\u5B50\u4EE3\u7406\u8BB0\u5F55\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",
|
|
375
907
|
"clear.prompt_running": "\u5F53\u524D\u6709 {n} \u4E2A\u8FD0\u884C\u4E2D\u7684\u5B50\u4EE3\u7406\uFF0C\u6E05\u9664\u540E\u5C06\u4E0D\u53EF\u6062\u590D\u3002\u786E\u5B9A\u7EE7\u7EED\uFF1F",
|
|
376
908
|
"clear.done": "\u5DF2\u6E05\u9664 {n} \u6761\u5B50\u4EE3\u7406\u8BB0\u5F55",
|
|
909
|
+
"clear.failed": "\u6E05\u9664\u5931\u8D25",
|
|
377
910
|
"clear.empty": "\u5F53\u524D\u4F1A\u8BDD\u65E0\u5B50\u4EE3\u7406\u8BB0\u5F55",
|
|
378
911
|
"cancel.no_session": "\u5B50\u4F1A\u8BDD ID \u4E0D\u53EF\u7528",
|
|
379
912
|
"cancel.not_child": "\u76EE\u6807\u4E0D\u662F\u5B50\u4F1A\u8BDD",
|
|
@@ -384,7 +917,25 @@ var ZH_T = {
|
|
|
384
917
|
"cancel.sent": "\u5DF2\u53D1\u9001\u53D6\u6D88\u6307\u4EE4",
|
|
385
918
|
"cancel.failed": "\u53D6\u6D88\u5931\u8D25",
|
|
386
919
|
"borderShown": "\u9762\u677F\u8FB9\u6846 \u5DF2\u663E\u793A",
|
|
387
|
-
"borderHidden": "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF"
|
|
920
|
+
"borderHidden": "\u9762\u677F\u8FB9\u6846 \u5DF2\u9690\u85CF",
|
|
921
|
+
"settings.title": "SubAgent Magazine \u2014 \u8BBE\u7F6E",
|
|
922
|
+
"settings.section.general": "\u901A\u7528",
|
|
923
|
+
"settings.lang": "\u8BED\u8A00",
|
|
924
|
+
"settings.max": "\u6700\u5927\u6761\u76EE\u6570",
|
|
925
|
+
"settings.order": "\u6392\u5E8F\u65B9\u5F0F",
|
|
926
|
+
"settings.scroll": "\u6EDA\u52A8\u6A21\u5F0F",
|
|
927
|
+
"settings.ttl": "\u4FDD\u7559\u671F\u9650",
|
|
928
|
+
"settings.border": "\u9762\u677F\u8FB9\u6846",
|
|
929
|
+
"settings.showEntryCost": "\u6BCF\u6761\u5B50\u4EE3\u7406\u663E\u793A\u8D39\u7528",
|
|
930
|
+
"settings.showEntryTime": "\u5217\u8868\u4E2D\u663E\u793A\u8017\u65F6",
|
|
931
|
+
"settings.showEntryTokens": "\u5217\u8868\u4E2D\u663E\u793A Token \u6570",
|
|
932
|
+
"settings.timeFormat": "\u65F6\u95F4\u683C\u5F0F",
|
|
933
|
+
"settings.dbSync": "\u672C\u5730\u6570\u636E\u5E93\u8865\u5168",
|
|
934
|
+
"settings.on": "\u5F00",
|
|
935
|
+
"settings.off": "\u5173",
|
|
936
|
+
"settings.saved": "\u5DF2\u4FDD\u5B58 {item}\uFF1A{value}",
|
|
937
|
+
"settings.invalid": "\u65E0\u6548\u7684\u6570\u503C",
|
|
938
|
+
"settings.close": "\u5173\u95ED"
|
|
388
939
|
};
|
|
389
940
|
var EN_T = {
|
|
390
941
|
"panel.title": "SubAgent",
|
|
@@ -427,6 +978,7 @@ var EN_T = {
|
|
|
427
978
|
"clear.prompt": "Clear all sub-agent records for this session? This cannot be undone.",
|
|
428
979
|
"clear.prompt_running": "{n} sub-agent(s) are still running. Clearing will discard them permanently. Continue?",
|
|
429
980
|
"clear.done": "Cleared {n} sub-agent record(s)",
|
|
981
|
+
"clear.failed": "Clear failed",
|
|
430
982
|
"clear.empty": "No sub-agent records in this session",
|
|
431
983
|
"cancel.no_session": "Child session ID is unavailable",
|
|
432
984
|
"cancel.not_child": "Target is not a child session",
|
|
@@ -437,7 +989,25 @@ var EN_T = {
|
|
|
437
989
|
"cancel.sent": "Cancel instruction sent",
|
|
438
990
|
"cancel.failed": "Cancellation failed",
|
|
439
991
|
"borderShown": "Panel border shown",
|
|
440
|
-
"borderHidden": "Panel border hidden"
|
|
992
|
+
"borderHidden": "Panel border hidden",
|
|
993
|
+
"settings.title": "SubAgent Magazine \u2014 Settings",
|
|
994
|
+
"settings.section.general": "General",
|
|
995
|
+
"settings.lang": "Language",
|
|
996
|
+
"settings.max": "Max entries",
|
|
997
|
+
"settings.order": "Sort order",
|
|
998
|
+
"settings.scroll": "Scroll mode",
|
|
999
|
+
"settings.ttl": "Retention",
|
|
1000
|
+
"settings.border": "Panel border",
|
|
1001
|
+
"settings.showEntryCost": "Cost per sub-agent",
|
|
1002
|
+
"settings.showEntryTime": "Elapsed time in list",
|
|
1003
|
+
"settings.showEntryTokens": "Token count in list",
|
|
1004
|
+
"settings.timeFormat": "Time format",
|
|
1005
|
+
"settings.dbSync": "Local database enrichment",
|
|
1006
|
+
"settings.on": "on",
|
|
1007
|
+
"settings.off": "off",
|
|
1008
|
+
"settings.saved": "Saved {item}: {value}",
|
|
1009
|
+
"settings.invalid": "Invalid value",
|
|
1010
|
+
"settings.close": "Close"
|
|
441
1011
|
};
|
|
442
1012
|
var JA_T = {
|
|
443
1013
|
"panel.title": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8",
|
|
@@ -480,6 +1050,7 @@ var JA_T = {
|
|
|
480
1050
|
"clear.prompt": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5168\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F\u3053\u306E\u64CD\u4F5C\u306F\u5143\u306B\u623B\u305B\u307E\u305B\u3093\u3002",
|
|
481
1051
|
"clear.prompt_running": "\u5B9F\u884C\u4E2D\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C {n} \u500B\u3042\u308A\u307E\u3059\u3002\u524A\u9664\u3059\u308B\u3068\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
|
|
482
1052
|
"clear.done": "{n} \u4EF6\u306E\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
1053
|
+
"clear.failed": "\u30AF\u30EA\u30A2\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
483
1054
|
"clear.empty": "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306B\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u8A18\u9332\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
484
1055
|
"cancel.no_session": "\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3 ID \u304C\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
485
1056
|
"cancel.not_child": "\u5BFE\u8C61\u306F\u5B50\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
@@ -490,7 +1061,25 @@ var JA_T = {
|
|
|
490
1061
|
"cancel.sent": "\u30AD\u30E3\u30F3\u30BB\u30EB\u6307\u793A\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
|
|
491
1062
|
"cancel.failed": "\u30AD\u30E3\u30F3\u30BB\u30EB\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
492
1063
|
"borderShown": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u8868\u793A",
|
|
493
|
-
"borderHidden": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A"
|
|
1064
|
+
"borderHidden": "\u30D1\u30CD\u30EB\u67A0\u7DDA \u975E\u8868\u793A",
|
|
1065
|
+
"settings.title": "SubAgent Magazine \u2014 \u8A2D\u5B9A",
|
|
1066
|
+
"settings.section.general": "\u4E00\u822C",
|
|
1067
|
+
"settings.lang": "\u8A00\u8A9E",
|
|
1068
|
+
"settings.max": "\u6700\u5927\u8868\u793A\u6570",
|
|
1069
|
+
"settings.order": "\u4E26\u3073\u9806",
|
|
1070
|
+
"settings.scroll": "\u30B9\u30AF\u30ED\u30FC\u30EB",
|
|
1071
|
+
"settings.ttl": "\u4FDD\u6301\u671F\u9593",
|
|
1072
|
+
"settings.border": "\u30D1\u30CD\u30EB\u67A0",
|
|
1073
|
+
"settings.showEntryCost": "\u30B5\u30D6\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u6BCE\u306E\u8CBB\u7528",
|
|
1074
|
+
"settings.showEntryTime": "\u4E00\u89A7\u306B\u6240\u8981\u6642\u9593\u3092\u8868\u793A",
|
|
1075
|
+
"settings.showEntryTokens": "\u4E00\u89A7\u306B\u30C8\u30FC\u30AF\u30F3\u6570\u3092\u8868\u793A",
|
|
1076
|
+
"settings.timeFormat": "\u6642\u9593\u5F62\u5F0F",
|
|
1077
|
+
"settings.dbSync": "\u30ED\u30FC\u30AB\u30EBDB\u88DC\u5B8C",
|
|
1078
|
+
"settings.on": "\u30AA\u30F3",
|
|
1079
|
+
"settings.off": "\u30AA\u30D5",
|
|
1080
|
+
"settings.saved": "\u4FDD\u5B58\u3057\u307E\u3057\u305F {item}\uFF1A{value}",
|
|
1081
|
+
"settings.invalid": "\u7121\u52B9\u306A\u5024\u3067\u3059",
|
|
1082
|
+
"settings.close": "\u9589\u3058\u308B"
|
|
494
1083
|
};
|
|
495
1084
|
var KO_T = {
|
|
496
1085
|
"panel.title": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8",
|
|
@@ -533,6 +1122,7 @@ var KO_T = {
|
|
|
533
1122
|
"clear.prompt": "\uC774 \uC138\uC158\uC758 \uBAA8\uB4E0 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D\uC744 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \uC774 \uC791\uC5C5\uC740 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
534
1123
|
"clear.prompt_running": "\uC2E4\uD589 \uC911\uC778 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8\uAC00 {n}\uAC1C \uC788\uC2B5\uB2C8\uB2E4. \uC0AD\uC81C\uD558\uBA74 \uBCF5\uAD6C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
535
1124
|
"clear.done": "\uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D {n}\uAC1C\uB97C \uC0AD\uC81C\uD588\uC2B5\uB2C8\uB2E4",
|
|
1125
|
+
"clear.failed": "\uC9C0\uC6B0\uAE30\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
|
|
536
1126
|
"clear.empty": "\uC774 \uC138\uC158\uC5D0 \uC11C\uBE0C \uC5D0\uC774\uC804\uD2B8 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
537
1127
|
"cancel.no_session": "\uD558\uC704 \uC138\uC158 ID\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
538
1128
|
"cancel.not_child": "\uB300\uC0C1\uC774 \uD558\uC704 \uC138\uC158\uC774 \uC544\uB2D9\uB2C8\uB2E4",
|
|
@@ -543,7 +1133,25 @@ var KO_T = {
|
|
|
543
1133
|
"cancel.sent": "\uCDE8\uC18C \uC9C0\uC2DC\uB97C \uBCF4\uB0C8\uC2B5\uB2C8\uB2E4",
|
|
544
1134
|
"cancel.failed": "\uCDE8\uC18C\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
|
|
545
1135
|
"borderShown": "\uD328\uB110 \uD14C\uB450\uB9AC \uD45C\uC2DC",
|
|
546
|
-
"borderHidden": "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40"
|
|
1136
|
+
"borderHidden": "\uD328\uB110 \uD14C\uB450\uB9AC \uC228\uAE40",
|
|
1137
|
+
"settings.title": "SubAgent Magazine \u2014 \uC124\uC815",
|
|
1138
|
+
"settings.section.general": "\uC77C\uBC18",
|
|
1139
|
+
"settings.lang": "\uC5B8\uC5B4",
|
|
1140
|
+
"settings.max": "\uCD5C\uB300 \uD56D\uBAA9 \uC218",
|
|
1141
|
+
"settings.order": "\uC815\uB82C \uC21C\uC11C",
|
|
1142
|
+
"settings.scroll": "\uC2A4\uD06C\uB864 \uBAA8\uB4DC",
|
|
1143
|
+
"settings.ttl": "\uBCF4\uC874 \uAE30\uAC04",
|
|
1144
|
+
"settings.border": "\uD328\uB110 \uD14C\uB450\uB9AC",
|
|
1145
|
+
"settings.showEntryCost": "\uD558\uC704 \uC5D0\uC774\uC804\uD2B8\uBCC4 \uBE44\uC6A9",
|
|
1146
|
+
"settings.showEntryTime": "\uBAA9\uB85D\uC5D0 \uC18C\uC694 \uC2DC\uAC04 \uD45C\uC2DC",
|
|
1147
|
+
"settings.showEntryTokens": "\uBAA9\uB85D\uC5D0 \uD1A0\uD070 \uC218 \uD45C\uC2DC",
|
|
1148
|
+
"settings.timeFormat": "\uC2DC\uAC04 \uD615\uC2DD",
|
|
1149
|
+
"settings.dbSync": "\uB85C\uCEEC DB \uBCF4\uC644",
|
|
1150
|
+
"settings.on": "\uCF2C",
|
|
1151
|
+
"settings.off": "\uB054",
|
|
1152
|
+
"settings.saved": "\uC800\uC7A5\uB428 {item}: {value}",
|
|
1153
|
+
"settings.invalid": "\uC798\uBABB\uB41C \uAC12",
|
|
1154
|
+
"settings.close": "\uB2EB\uAE30"
|
|
547
1155
|
};
|
|
548
1156
|
var LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
|
|
549
1157
|
var LANG_META = [
|
|
@@ -579,11 +1187,292 @@ import { createSignal } from "solid-js";
|
|
|
579
1187
|
var globalEntryCache = /* @__PURE__ */ new Map();
|
|
580
1188
|
var [clearTick, setClearTick] = createSignal(0);
|
|
581
1189
|
|
|
1190
|
+
// src/panel/entry-map.ts
|
|
1191
|
+
function findSubEntryKey(entries, id, sessionId) {
|
|
1192
|
+
if (sessionId) {
|
|
1193
|
+
for (const [key, entry] of entries) {
|
|
1194
|
+
if (entry.sessionId === sessionId) return key;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return entries.has(id) ? id : void 0;
|
|
1198
|
+
}
|
|
1199
|
+
function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
1200
|
+
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
1201
|
+
const existing = entries.get(key);
|
|
1202
|
+
const next = new Map(entries);
|
|
1203
|
+
if (key !== partial.id) next.delete(partial.id);
|
|
1204
|
+
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
1205
|
+
next.set(key, {
|
|
1206
|
+
...existing ?? { startedAt: now },
|
|
1207
|
+
...partial,
|
|
1208
|
+
id: existing?.id ?? partial.id,
|
|
1209
|
+
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
1210
|
+
endedAt: ended ? existing?.endedAt || now : void 0
|
|
1211
|
+
});
|
|
1212
|
+
return next;
|
|
1213
|
+
}
|
|
1214
|
+
function statusRank(status) {
|
|
1215
|
+
if (status === "running") return 0;
|
|
1216
|
+
if (status === "cancel_requested") return 1;
|
|
1217
|
+
return 2;
|
|
1218
|
+
}
|
|
1219
|
+
var isTerminalStatus = (status) => statusRank(status) === 2;
|
|
1220
|
+
function minDefined(a, b) {
|
|
1221
|
+
if (a === void 0) return b;
|
|
1222
|
+
if (b === void 0) return a;
|
|
1223
|
+
return Math.min(a, b);
|
|
1224
|
+
}
|
|
1225
|
+
function maxDefined(a, b) {
|
|
1226
|
+
if (a === void 0) return b;
|
|
1227
|
+
if (b === void 0) return a;
|
|
1228
|
+
return Math.max(a, b);
|
|
1229
|
+
}
|
|
1230
|
+
function mergeSubEntry(base, incoming) {
|
|
1231
|
+
const successor = statusRank(incoming.status) > statusRank(base.status) ? incoming : base;
|
|
1232
|
+
const status = successor.status;
|
|
1233
|
+
const ended = isTerminalStatus(status);
|
|
1234
|
+
return {
|
|
1235
|
+
...base,
|
|
1236
|
+
...incoming,
|
|
1237
|
+
id: base.id,
|
|
1238
|
+
status,
|
|
1239
|
+
sessionId: incoming.sessionId ?? base.sessionId,
|
|
1240
|
+
startedAt: minDefined(base.startedAt, incoming.startedAt) ?? base.startedAt,
|
|
1241
|
+
endedAt: ended ? base.endedAt ?? incoming.endedAt ?? Date.now() : void 0,
|
|
1242
|
+
tokens: maxDefined(base.tokens, incoming.tokens),
|
|
1243
|
+
cost: maxDefined(base.cost, incoming.cost),
|
|
1244
|
+
model: incoming.model ?? base.model,
|
|
1245
|
+
todoTotal: maxDefined(base.todoTotal, incoming.todoTotal),
|
|
1246
|
+
todoDone: maxDefined(base.todoDone, incoming.todoDone),
|
|
1247
|
+
error: incoming.error ?? base.error,
|
|
1248
|
+
cancelRequestedAt: minDefined(base.cancelRequestedAt, incoming.cancelRequestedAt),
|
|
1249
|
+
abortAccepted: base.abortAccepted || incoming.abortAccepted || void 0,
|
|
1250
|
+
cancelReason: incoming.cancelReason ?? base.cancelReason
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
function mergeSubEntries(base, incoming) {
|
|
1254
|
+
const out = /* @__PURE__ */ new Map();
|
|
1255
|
+
for (const entry of base) out.set(entry.id, entry);
|
|
1256
|
+
for (const entry of incoming) {
|
|
1257
|
+
const key = findSubEntryKey(out, entry.id, entry.sessionId);
|
|
1258
|
+
if (!key) {
|
|
1259
|
+
out.set(entry.id, entry);
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
out.set(key, mergeSubEntry(out.get(key), entry));
|
|
1263
|
+
}
|
|
1264
|
+
return out;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/v2/settings-menu.ts
|
|
1268
|
+
var TTL_VALUES = [3, 7, 14, 30, 0];
|
|
1269
|
+
var nextTick = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
1270
|
+
function openSettingsMenu(context, api, signals) {
|
|
1271
|
+
runSettingsMenu(context, api, signals).catch((err) => {
|
|
1272
|
+
try {
|
|
1273
|
+
context.ui.toast.show({ message: String(err), variant: "error" });
|
|
1274
|
+
} catch {
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
async function runSettingsMenu(context, api, signals) {
|
|
1279
|
+
const t = createT(() => signals.lang());
|
|
1280
|
+
const kv = api.kv;
|
|
1281
|
+
const onOff = (v) => v ? t("settings.on") : t("settings.off");
|
|
1282
|
+
const langLabel = () => LANG_META.find((m) => m.code === signals.lang())?.label ?? signals.lang();
|
|
1283
|
+
const ttlTitle = (d) => d === 0 ? t("ttl.unlimited") : t(d === 3 ? "ttl.3d" : d === 7 ? "ttl.7d" : d === 14 ? "ttl.14d" : "ttl.30d");
|
|
1284
|
+
const ttlLabel = () => ttlTitle(readTTLDays(kv));
|
|
1285
|
+
const orderLabel = () => signals.sortOrder() === "desc" ? t("order.desc") : t("order.asc");
|
|
1286
|
+
const scrollLabel = () => signals.scrollMode() === "wheel" ? t("scroll.wheel") : t("scroll.click");
|
|
1287
|
+
const toast = (item, value) => context.ui.toast.show({ message: t("settings.saved", { item, value }) });
|
|
1288
|
+
while (true) {
|
|
1289
|
+
const category = t("settings.section.general");
|
|
1290
|
+
const choice = await context.ui.dialog.select({
|
|
1291
|
+
title: t("settings.title"),
|
|
1292
|
+
options: [
|
|
1293
|
+
{ title: t("settings.lang"), value: "lang", description: langLabel(), category },
|
|
1294
|
+
{ title: t("settings.max"), value: "max", description: String(signals.maxEntries()), category },
|
|
1295
|
+
{ title: t("settings.order"), value: "order", description: orderLabel(), category },
|
|
1296
|
+
{ title: t("settings.scroll"), value: "scroll", description: scrollLabel(), category },
|
|
1297
|
+
{ title: t("settings.ttl"), value: "ttl", description: ttlLabel(), category },
|
|
1298
|
+
{ title: t("settings.border"), value: "border", description: onOff(signals.borderVisible()), category },
|
|
1299
|
+
{ title: t("settings.showEntryCost"), value: "cost", description: onOff(signals.showEntryCost()), category },
|
|
1300
|
+
{ title: t("settings.showEntryTime"), value: "time", description: onOff(signals.showEntryTime()), category },
|
|
1301
|
+
{ title: t("settings.timeFormat"), value: "timefmt", description: TIME_FORMAT_SAMPLES[signals.timeFormat()], category },
|
|
1302
|
+
{ title: t("settings.showEntryTokens"), value: "tokens", description: onOff(signals.showEntryTokens()), category },
|
|
1303
|
+
{ title: t("settings.dbSync"), value: "dbsync", description: onOff(signals.dbSync()), category },
|
|
1304
|
+
{ title: t("settings.close"), value: "close" }
|
|
1305
|
+
]
|
|
1306
|
+
});
|
|
1307
|
+
if (choice === void 0 || choice === "close") return;
|
|
1308
|
+
if (choice === "lang") {
|
|
1309
|
+
const picked = await context.ui.dialog.select({
|
|
1310
|
+
title: t("settings.lang"),
|
|
1311
|
+
options: LANG_META.map((m) => ({ title: m.label, value: m.code })),
|
|
1312
|
+
current: signals.lang()
|
|
1313
|
+
});
|
|
1314
|
+
if (picked) {
|
|
1315
|
+
signals.setLang(picked);
|
|
1316
|
+
kv.set(SETTING_KEYS.lang, picked);
|
|
1317
|
+
toast(t("settings.lang"), LANG_META.find((m) => m.code === picked)?.label ?? picked);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (choice === "max") {
|
|
1321
|
+
const val = await context.ui.dialog.prompt({
|
|
1322
|
+
title: t("settings.max"),
|
|
1323
|
+
message: "1\u201350",
|
|
1324
|
+
placeholder: String(signals.maxEntries())
|
|
1325
|
+
});
|
|
1326
|
+
if (val !== void 0) {
|
|
1327
|
+
const n = parseInt(val.trim(), 10);
|
|
1328
|
+
if (Number.isNaN(n)) {
|
|
1329
|
+
context.ui.toast.show({ message: t("settings.invalid"), variant: "warning" });
|
|
1330
|
+
} else {
|
|
1331
|
+
const clamped = Math.max(1, Math.min(50, n));
|
|
1332
|
+
signals.setMaxEntries(clamped);
|
|
1333
|
+
kv.set(SETTING_KEYS.maxEntries, clamped);
|
|
1334
|
+
toast(t("settings.max"), String(clamped));
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
if (choice === "order") {
|
|
1339
|
+
const picked = await context.ui.dialog.select({
|
|
1340
|
+
title: t("settings.order"),
|
|
1341
|
+
options: [
|
|
1342
|
+
{ title: t("order.desc"), value: "desc" },
|
|
1343
|
+
{ title: t("order.asc"), value: "asc" }
|
|
1344
|
+
],
|
|
1345
|
+
current: signals.sortOrder()
|
|
1346
|
+
});
|
|
1347
|
+
if (picked) {
|
|
1348
|
+
signals.setSortOrder(picked);
|
|
1349
|
+
kv.set(SETTING_KEYS.order, picked);
|
|
1350
|
+
toast(t("settings.order"), picked === "desc" ? t("order.desc") : t("order.asc"));
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (choice === "scroll") {
|
|
1354
|
+
const picked = await context.ui.dialog.select({
|
|
1355
|
+
title: t("settings.scroll"),
|
|
1356
|
+
options: [
|
|
1357
|
+
{ title: t("scroll.wheel"), value: "wheel" },
|
|
1358
|
+
{ title: t("scroll.click"), value: "click" }
|
|
1359
|
+
],
|
|
1360
|
+
current: signals.scrollMode()
|
|
1361
|
+
});
|
|
1362
|
+
if (picked) {
|
|
1363
|
+
signals.setScrollMode(picked);
|
|
1364
|
+
kv.set(SETTING_KEYS.scrollMode, picked);
|
|
1365
|
+
toast(t("settings.scroll"), picked === "wheel" ? t("scroll.wheel") : t("scroll.click"));
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (choice === "ttl") {
|
|
1369
|
+
const picked = await context.ui.dialog.select({
|
|
1370
|
+
title: t("settings.ttl"),
|
|
1371
|
+
options: TTL_VALUES.map((d) => ({ title: ttlTitle(d), value: d })),
|
|
1372
|
+
current: readTTLDays(kv)
|
|
1373
|
+
});
|
|
1374
|
+
if (picked !== void 0) {
|
|
1375
|
+
kv.set(SETTING_KEYS.ttlDays, String(picked));
|
|
1376
|
+
toast(t("settings.ttl"), ttlTitle(picked));
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (choice === "border") {
|
|
1380
|
+
const picked = await context.ui.dialog.select({
|
|
1381
|
+
title: t("settings.border"),
|
|
1382
|
+
options: [
|
|
1383
|
+
{ title: t("settings.on"), value: true },
|
|
1384
|
+
{ title: t("settings.off"), value: false }
|
|
1385
|
+
],
|
|
1386
|
+
current: signals.borderVisible()
|
|
1387
|
+
});
|
|
1388
|
+
if (picked !== void 0) {
|
|
1389
|
+
signals.setBorderVisible(picked);
|
|
1390
|
+
kv.set(SETTING_KEYS.border, picked);
|
|
1391
|
+
toast(t("settings.border"), onOff(picked));
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
if (choice === "cost") {
|
|
1395
|
+
const picked = await context.ui.dialog.select({
|
|
1396
|
+
title: t("settings.showEntryCost"),
|
|
1397
|
+
options: [
|
|
1398
|
+
{ title: t("settings.on"), value: true },
|
|
1399
|
+
{ title: t("settings.off"), value: false }
|
|
1400
|
+
],
|
|
1401
|
+
current: signals.showEntryCost()
|
|
1402
|
+
});
|
|
1403
|
+
if (picked !== void 0) {
|
|
1404
|
+
signals.setShowEntryCost(picked);
|
|
1405
|
+
kv.set(SETTING_KEYS.showEntryCost, picked);
|
|
1406
|
+
toast(t("settings.showEntryCost"), onOff(picked));
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (choice === "time") {
|
|
1410
|
+
const picked = await context.ui.dialog.select({
|
|
1411
|
+
title: t("settings.showEntryTime"),
|
|
1412
|
+
options: [
|
|
1413
|
+
{ title: t("settings.on"), value: true },
|
|
1414
|
+
{ title: t("settings.off"), value: false }
|
|
1415
|
+
],
|
|
1416
|
+
current: signals.showEntryTime()
|
|
1417
|
+
});
|
|
1418
|
+
if (picked !== void 0) {
|
|
1419
|
+
signals.setShowEntryTime(picked);
|
|
1420
|
+
kv.set(SETTING_KEYS.showEntryTime, picked);
|
|
1421
|
+
toast(t("settings.showEntryTime"), onOff(picked));
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
if (choice === "timefmt") {
|
|
1425
|
+
const picked = await context.ui.dialog.select({
|
|
1426
|
+
title: t("settings.timeFormat"),
|
|
1427
|
+
options: TIME_FORMATS.map((f) => ({ title: `${f} \u2014 ${TIME_FORMAT_SAMPLES[f]}`, value: f })),
|
|
1428
|
+
current: signals.timeFormat()
|
|
1429
|
+
});
|
|
1430
|
+
if (picked) {
|
|
1431
|
+
signals.setTimeFormat(picked);
|
|
1432
|
+
kv.set(SETTING_KEYS.timeFormat, picked);
|
|
1433
|
+
toast(t("settings.timeFormat"), TIME_FORMAT_SAMPLES[picked]);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
if (choice === "tokens") {
|
|
1437
|
+
const picked = await context.ui.dialog.select({
|
|
1438
|
+
title: t("settings.showEntryTokens"),
|
|
1439
|
+
options: [
|
|
1440
|
+
{ title: t("settings.on"), value: true },
|
|
1441
|
+
{ title: t("settings.off"), value: false }
|
|
1442
|
+
],
|
|
1443
|
+
current: signals.showEntryTokens()
|
|
1444
|
+
});
|
|
1445
|
+
if (picked !== void 0) {
|
|
1446
|
+
signals.setShowEntryTokens(picked);
|
|
1447
|
+
kv.set(SETTING_KEYS.showEntryTokens, picked);
|
|
1448
|
+
toast(t("settings.showEntryTokens"), onOff(picked));
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
if (choice === "dbsync") {
|
|
1452
|
+
const picked = await context.ui.dialog.select({
|
|
1453
|
+
title: t("settings.dbSync"),
|
|
1454
|
+
options: [
|
|
1455
|
+
{ title: t("settings.on"), value: true },
|
|
1456
|
+
{ title: t("settings.off"), value: false }
|
|
1457
|
+
],
|
|
1458
|
+
current: signals.dbSync()
|
|
1459
|
+
});
|
|
1460
|
+
if (picked !== void 0) {
|
|
1461
|
+
signals.setDbSync(picked);
|
|
1462
|
+
kv.set(SETTING_KEYS.dbSync, picked);
|
|
1463
|
+
toast(t("settings.dbSync"), onOff(picked));
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
await nextTick();
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
582
1470
|
// src/v2/commands.ts
|
|
583
1471
|
function makeCommands(context, api, signals) {
|
|
584
1472
|
const t = createT(() => signals.lang());
|
|
585
1473
|
const kv = api.kv;
|
|
586
1474
|
const clampMax = (n) => Math.max(1, Math.min(50, n));
|
|
1475
|
+
const onOff = (v) => v ? t("settings.on") : t("settings.off");
|
|
587
1476
|
const resolveParent = (sid) => {
|
|
588
1477
|
try {
|
|
589
1478
|
const session = api.session.get(sid);
|
|
@@ -670,6 +1559,111 @@ function makeCommands(context, api, signals) {
|
|
|
670
1559
|
api.ui.toast(`Max entries: ${n}`);
|
|
671
1560
|
}
|
|
672
1561
|
},
|
|
1562
|
+
{
|
|
1563
|
+
id: "opencode-subagent-magazine.subagent.config",
|
|
1564
|
+
title: "SubAgent Magazine: Settings",
|
|
1565
|
+
description: "Show current settings (change each via its own command)",
|
|
1566
|
+
slash: { name: "subagent-config" },
|
|
1567
|
+
palette: true,
|
|
1568
|
+
run: () => {
|
|
1569
|
+
const ttl = readTTLDays(kv);
|
|
1570
|
+
const langLabel = LANG_META.find((m) => m.code === signals.lang())?.label ?? signals.lang();
|
|
1571
|
+
const message = [
|
|
1572
|
+
`${t("settings.lang")}: ${langLabel}`,
|
|
1573
|
+
`${t("settings.max")}: ${signals.maxEntries()}`,
|
|
1574
|
+
`${t("settings.order")}: ${signals.sortOrder() === "desc" ? t("order.desc") : t("order.asc")}`,
|
|
1575
|
+
`${t("settings.scroll")}: ${signals.scrollMode() === "wheel" ? t("scroll.wheel") : t("scroll.click")}`,
|
|
1576
|
+
`${t("settings.ttl")}: ${ttl === 0 ? t("ttl.unlimited") : `${ttl}d`}`,
|
|
1577
|
+
`${t("settings.border")}: ${onOff(signals.borderVisible())}`,
|
|
1578
|
+
`${t("settings.showEntryCost")}: ${onOff(signals.showEntryCost())}`,
|
|
1579
|
+
`${t("settings.showEntryTime")}: ${onOff(signals.showEntryTime())}`,
|
|
1580
|
+
`${t("settings.showEntryTokens")}: ${onOff(signals.showEntryTokens())}`,
|
|
1581
|
+
`${t("settings.timeFormat")}: ${TIME_FORMAT_SAMPLES[signals.timeFormat()]}`,
|
|
1582
|
+
`${t("settings.dbSync")}: ${onOff(signals.dbSync())}`
|
|
1583
|
+
].join("\n");
|
|
1584
|
+
context.ui.toast.show({ title: t("settings.title"), message });
|
|
1585
|
+
}
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
id: "opencode-subagent-magazine.subagent.sections",
|
|
1589
|
+
title: "SubAgent Magazine: Settings Menu",
|
|
1590
|
+
description: "Open the interactive settings menu (Esc to close)",
|
|
1591
|
+
slash: { name: "subagent-sections" },
|
|
1592
|
+
palette: true,
|
|
1593
|
+
run: () => {
|
|
1594
|
+
openSettingsMenu(context, api, signals);
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
id: "opencode-subagent-magazine.subagent.cost",
|
|
1599
|
+
title: "SubAgent Magazine: Toggle Entry Cost",
|
|
1600
|
+
description: "Show or hide the per-sub-agent cost in the sidebar",
|
|
1601
|
+
slash: { name: "subagent-cost" },
|
|
1602
|
+
palette: true,
|
|
1603
|
+
run: () => {
|
|
1604
|
+
const v = !signals.showEntryCost();
|
|
1605
|
+
signals.setShowEntryCost(v);
|
|
1606
|
+
kv.set(SETTING_KEYS.showEntryCost, v);
|
|
1607
|
+
api.ui.toast(`${t("settings.showEntryCost")}: ${onOff(v)}`);
|
|
1608
|
+
}
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
id: "opencode-subagent-magazine.subagent.time",
|
|
1612
|
+
title: "SubAgent Magazine: Toggle Entry Time",
|
|
1613
|
+
description: "Show or hide the elapsed time in the sidebar list",
|
|
1614
|
+
slash: { name: "subagent-time" },
|
|
1615
|
+
palette: true,
|
|
1616
|
+
run: () => {
|
|
1617
|
+
const v = !signals.showEntryTime();
|
|
1618
|
+
signals.setShowEntryTime(v);
|
|
1619
|
+
kv.set(SETTING_KEYS.showEntryTime, v);
|
|
1620
|
+
api.ui.toast(`${t("settings.showEntryTime")}: ${onOff(v)}`);
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
id: "opencode-subagent-magazine.subagent.time-format",
|
|
1625
|
+
title: "SubAgent Magazine: Time Format",
|
|
1626
|
+
description: "Set how elapsed time is displayed (short / decimal / clock / compact / seconds)",
|
|
1627
|
+
slash: { name: "subagent-time-format" },
|
|
1628
|
+
palette: true,
|
|
1629
|
+
run: async () => {
|
|
1630
|
+
const picked = await context.ui.dialog.select({
|
|
1631
|
+
title: t("settings.timeFormat"),
|
|
1632
|
+
options: TIME_FORMATS.map((f) => ({ title: `${f} \u2014 ${TIME_FORMAT_SAMPLES[f]}`, value: f })),
|
|
1633
|
+
current: signals.timeFormat()
|
|
1634
|
+
});
|
|
1635
|
+
if (!picked) return;
|
|
1636
|
+
signals.setTimeFormat(picked);
|
|
1637
|
+
kv.set(SETTING_KEYS.timeFormat, picked);
|
|
1638
|
+
api.ui.toast(`${t("settings.timeFormat")}: ${TIME_FORMAT_SAMPLES[picked]}`);
|
|
1639
|
+
}
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
id: "opencode-subagent-magazine.subagent.tokens",
|
|
1643
|
+
title: "SubAgent Magazine: Toggle Entry Tokens",
|
|
1644
|
+
description: "Show or hide the token count in the sidebar list",
|
|
1645
|
+
slash: { name: "subagent-tokens" },
|
|
1646
|
+
palette: true,
|
|
1647
|
+
run: () => {
|
|
1648
|
+
const v = !signals.showEntryTokens();
|
|
1649
|
+
signals.setShowEntryTokens(v);
|
|
1650
|
+
kv.set(SETTING_KEYS.showEntryTokens, v);
|
|
1651
|
+
api.ui.toast(`${t("settings.showEntryTokens")}: ${onOff(v)}`);
|
|
1652
|
+
}
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
id: "opencode-subagent-magazine.subagent.db",
|
|
1656
|
+
title: "SubAgent Magazine: Toggle Local DB",
|
|
1657
|
+
description: "Enrich sub-agent entries from the local OpenCode database (V2)",
|
|
1658
|
+
slash: { name: "subagent-db" },
|
|
1659
|
+
palette: true,
|
|
1660
|
+
run: () => {
|
|
1661
|
+
const v = !signals.dbSync();
|
|
1662
|
+
signals.setDbSync(v);
|
|
1663
|
+
kv.set(SETTING_KEYS.dbSync, v);
|
|
1664
|
+
api.ui.toast(`${t("settings.dbSync")}: ${onOff(v)}`);
|
|
1665
|
+
}
|
|
1666
|
+
},
|
|
673
1667
|
{
|
|
674
1668
|
id: "opencode-subagent-magazine.subagent.version",
|
|
675
1669
|
title: "SubAgent Magazine: Version",
|
|
@@ -712,27 +1706,43 @@ function makeCommands(context, api, signals) {
|
|
|
712
1706
|
}
|
|
713
1707
|
}
|
|
714
1708
|
if (count > 0) {
|
|
1709
|
+
const ok = () => api.ui.toast(signals.lang() === "zh" ? `\u5DF2\u6807\u8BB0 ${count} \u4E2A\u8FD0\u884C\u4E2D\u7684\u6761\u76EE\u4E3A\u5B8C\u6210` : `Marked ${count} running entries as done`);
|
|
1710
|
+
const fail = () => {
|
|
1711
|
+
try {
|
|
1712
|
+
api.ui.toast(t("clear.failed"));
|
|
1713
|
+
} catch {
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
715
1716
|
try {
|
|
716
|
-
const data = loadSessionData(kv);
|
|
717
1717
|
const { parentSid, isChild } = resolveParent(sid);
|
|
718
|
-
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1718
|
+
const done = updateSessionData(kv, (data) => {
|
|
1719
|
+
if (isChild) {
|
|
1720
|
+
if (!data[parentSid]) data[parentSid] = { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} };
|
|
1721
|
+
if (!data[parentSid].children) data[parentSid].children = {};
|
|
1722
|
+
const prev = data[parentSid].children[sid]?.entries ?? [];
|
|
1723
|
+
const merged = [...mergeSubEntries(prev, entries.values()).values()].map(
|
|
1724
|
+
(e) => e.status === "running" || e.status === "cancel_requested" ? { ...e, status: "done", endedAt: e.endedAt ?? Date.now() } : e
|
|
1725
|
+
);
|
|
1726
|
+
data[parentSid].children[sid] = {
|
|
1727
|
+
...data[parentSid].children[sid] ?? { scroll: 0, expanded: "" },
|
|
1728
|
+
entries: merged
|
|
1729
|
+
};
|
|
1730
|
+
} else {
|
|
1731
|
+
const rec = data[parentSid] ?? { ts: Date.now(), entries: [], scroll: 0, expanded: "", children: {} };
|
|
1732
|
+
const merged = [...mergeSubEntries(rec.entries ?? [], entries.values()).values()].map(
|
|
1733
|
+
(e) => e.status === "running" || e.status === "cancel_requested" ? { ...e, status: "done", endedAt: e.endedAt ?? Date.now() } : e
|
|
1734
|
+
);
|
|
1735
|
+
data[parentSid] = { ...rec, ts: Date.now(), entries: merged, children: rec.children ?? {} };
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
if (done && typeof done.then === "function") {
|
|
1739
|
+
void done.then(ok, fail);
|
|
723
1740
|
} else {
|
|
724
|
-
|
|
725
|
-
ts: Date.now(),
|
|
726
|
-
entries: [...entries.values()],
|
|
727
|
-
scroll: data[sid]?.scroll ?? 0,
|
|
728
|
-
expanded: data[sid]?.expanded ?? "",
|
|
729
|
-
children: data[sid]?.children ?? {}
|
|
730
|
-
};
|
|
1741
|
+
ok();
|
|
731
1742
|
}
|
|
732
|
-
saveSessionData(kv, data);
|
|
733
1743
|
} catch {
|
|
1744
|
+
fail();
|
|
734
1745
|
}
|
|
735
|
-
api.ui.toast(signals.lang() === "zh" ? `\u5DF2\u6807\u8BB0 ${count} \u4E2A\u8FD0\u884C\u4E2D\u7684\u6761\u76EE\u4E3A\u5B8C\u6210` : `Marked ${count} running entries as done`);
|
|
736
1746
|
} else {
|
|
737
1747
|
api.ui.toast(signals.lang() === "zh" ? "\u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684\u8FD0\u884C\u4E2D\u6761\u76EE" : "No running entries to clear");
|
|
738
1748
|
}
|
|
@@ -789,32 +1799,45 @@ function makeCommands(context, api, signals) {
|
|
|
789
1799
|
});
|
|
790
1800
|
if (choice !== "yes") return;
|
|
791
1801
|
try {
|
|
792
|
-
const data = loadSessionData(kv);
|
|
793
1802
|
let count = 0;
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
child
|
|
802
|
-
|
|
1803
|
+
const finish = () => {
|
|
1804
|
+
globalEntryCache.delete(sid);
|
|
1805
|
+
setClearTick((v) => v + 1);
|
|
1806
|
+
api.ui.toast(t("clear.done", { n: count }));
|
|
1807
|
+
};
|
|
1808
|
+
const done = updateSessionData(kv, (data) => {
|
|
1809
|
+
if (parentID) {
|
|
1810
|
+
const child = data[parentID]?.children?.[sid];
|
|
1811
|
+
if (child) {
|
|
1812
|
+
const ids = child.entries?.map((e) => e.id) ?? [];
|
|
1813
|
+
count = ids.length;
|
|
1814
|
+
child.entries = [];
|
|
1815
|
+
child.scroll = 0;
|
|
1816
|
+
child.expanded = "";
|
|
1817
|
+
child.clearedIds = [.../* @__PURE__ */ new Set([...child.clearedIds ?? [], ...ids])];
|
|
1818
|
+
}
|
|
1819
|
+
} else {
|
|
1820
|
+
const rec = data[sid];
|
|
1821
|
+
if (rec) {
|
|
1822
|
+
const ids = rec.entries?.map((e) => e.id) ?? [];
|
|
1823
|
+
count = ids.length;
|
|
1824
|
+
rec.entries = [];
|
|
1825
|
+
rec.scroll = 0;
|
|
1826
|
+
rec.expanded = "";
|
|
1827
|
+
rec.clearedIds = [.../* @__PURE__ */ new Set([...rec.clearedIds ?? [], ...ids])];
|
|
1828
|
+
}
|
|
803
1829
|
}
|
|
1830
|
+
});
|
|
1831
|
+
if (done && typeof done.then === "function") {
|
|
1832
|
+
void done.then(finish, () => {
|
|
1833
|
+
try {
|
|
1834
|
+
api.ui.toast(t("clear.failed"));
|
|
1835
|
+
} catch {
|
|
1836
|
+
}
|
|
1837
|
+
});
|
|
804
1838
|
} else {
|
|
805
|
-
|
|
806
|
-
if (data[sid]) {
|
|
807
|
-
const ids = data[sid].entries?.map((e) => e.id) ?? [];
|
|
808
|
-
data[sid].entries = [];
|
|
809
|
-
data[sid].scroll = 0;
|
|
810
|
-
data[sid].expanded = "";
|
|
811
|
-
data[sid].clearedIds = [.../* @__PURE__ */ new Set([...data[sid].clearedIds ?? [], ...ids])];
|
|
812
|
-
}
|
|
1839
|
+
finish();
|
|
813
1840
|
}
|
|
814
|
-
saveSessionData(kv, data);
|
|
815
|
-
globalEntryCache.delete(sid);
|
|
816
|
-
setClearTick((v) => v + 1);
|
|
817
|
-
api.ui.toast(t("clear.done", { n: count }));
|
|
818
1841
|
} catch {
|
|
819
1842
|
}
|
|
820
1843
|
}
|
|
@@ -961,53 +1984,6 @@ async function copyText(text) {
|
|
|
961
1984
|
// src/core/types.ts
|
|
962
1985
|
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "subagent", "delegate", "call_omo_agent"]);
|
|
963
1986
|
|
|
964
|
-
// src/core/format.ts
|
|
965
|
-
function charColumns(c) {
|
|
966
|
-
const code = c.codePointAt(0) ?? 0;
|
|
967
|
-
if (code < 32) return 0;
|
|
968
|
-
if (code < 127) return 1;
|
|
969
|
-
if (code < 160) return 0;
|
|
970
|
-
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 128591 || code >= 131072 && code <= 262141)
|
|
971
|
-
return 2;
|
|
972
|
-
return 1;
|
|
973
|
-
}
|
|
974
|
-
function visualWidth(s) {
|
|
975
|
-
let w = 0;
|
|
976
|
-
for (const c of s) w += charColumns(c);
|
|
977
|
-
return w;
|
|
978
|
-
}
|
|
979
|
-
function truncate(text, maxCols) {
|
|
980
|
-
if (visualWidth(text) <= maxCols) return text;
|
|
981
|
-
let cols = 0;
|
|
982
|
-
let i = 0;
|
|
983
|
-
for (const c of text) {
|
|
984
|
-
const w = charColumns(c);
|
|
985
|
-
if (cols + w > maxCols - 1) break;
|
|
986
|
-
cols += w;
|
|
987
|
-
i += c.length;
|
|
988
|
-
}
|
|
989
|
-
return text.slice(0, i) + "\u2026";
|
|
990
|
-
}
|
|
991
|
-
function fmtDurationShort(ms, running) {
|
|
992
|
-
if (running && ms < 2e3) return "";
|
|
993
|
-
if (ms < 1e3) return (ms / 1e3).toFixed(2) + "s";
|
|
994
|
-
if (ms < 6e4) return (ms / 1e3).toFixed(2) + "s";
|
|
995
|
-
const m = Math.floor(ms / 6e4);
|
|
996
|
-
const s = Math.round(ms % 6e4 / 1e3);
|
|
997
|
-
return `${m}m${s}s`;
|
|
998
|
-
}
|
|
999
|
-
function fmtTokens(n) {
|
|
1000
|
-
if (n < 1e3) return `${n}`;
|
|
1001
|
-
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
1002
|
-
return `${(n / 1e6).toFixed(1)}M`;
|
|
1003
|
-
}
|
|
1004
|
-
function safeErrorMsg(err) {
|
|
1005
|
-
if (!err) return "";
|
|
1006
|
-
if (typeof err === "string") return err;
|
|
1007
|
-
if (typeof err === "object") return String(err.message || err.code || "");
|
|
1008
|
-
return "";
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
1987
|
// src/core/color.ts
|
|
1012
1988
|
function rgb(raw) {
|
|
1013
1989
|
if (typeof raw === "string" && raw.startsWith("#")) {
|
|
@@ -1063,52 +2039,38 @@ function dimColor(hex, factor = 0.5) {
|
|
|
1063
2039
|
const r = Math.round(c.r * factor);
|
|
1064
2040
|
const g = Math.round(c.g * factor);
|
|
1065
2041
|
const b = Math.round(c.b * factor);
|
|
1066
|
-
return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
1067
|
-
}
|
|
1068
|
-
var FALLBACK = {
|
|
1069
|
-
primary: "#8B9DAF",
|
|
1070
|
-
text: "#C5C5BB",
|
|
1071
|
-
muted: "#7A7A72",
|
|
1072
|
-
success: "#9CAF8B",
|
|
1073
|
-
warning: "#C5B88D",
|
|
1074
|
-
error: "#B08A8A",
|
|
1075
|
-
border: "#6B6B63"
|
|
1076
|
-
};
|
|
1077
|
-
var MAX_SAT = 0.28;
|
|
1078
|
-
|
|
1079
|
-
// src/panel/session-routing.ts
|
|
1080
|
-
function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
1081
|
-
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
// src/panel/entry-map.ts
|
|
1085
|
-
function findSubEntryKey(entries, id, sessionId) {
|
|
1086
|
-
if (sessionId) {
|
|
1087
|
-
for (const [key, entry] of entries) {
|
|
1088
|
-
if (entry.sessionId === sessionId) return key;
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
return entries.has(id) ? id : void 0;
|
|
1092
|
-
}
|
|
1093
|
-
function upsertSubEntry(entries, partial, now = Date.now()) {
|
|
1094
|
-
const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id;
|
|
1095
|
-
const existing = entries.get(key);
|
|
1096
|
-
const next = new Map(entries);
|
|
1097
|
-
if (key !== partial.id) next.delete(partial.id);
|
|
1098
|
-
const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled";
|
|
1099
|
-
next.set(key, {
|
|
1100
|
-
...existing ?? { startedAt: now },
|
|
1101
|
-
...partial,
|
|
1102
|
-
id: existing?.id ?? partial.id,
|
|
1103
|
-
startedAt: existing?.startedAt || partial.startedAt || now,
|
|
1104
|
-
endedAt: ended ? existing?.endedAt || now : void 0
|
|
1105
|
-
});
|
|
1106
|
-
return next;
|
|
2042
|
+
return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
2043
|
+
}
|
|
2044
|
+
var FALLBACK = {
|
|
2045
|
+
primary: "#8B9DAF",
|
|
2046
|
+
text: "#C5C5BB",
|
|
2047
|
+
muted: "#7A7A72",
|
|
2048
|
+
success: "#9CAF8B",
|
|
2049
|
+
warning: "#C5B88D",
|
|
2050
|
+
error: "#B08A8A",
|
|
2051
|
+
border: "#6B6B63"
|
|
2052
|
+
};
|
|
2053
|
+
var MAX_SAT = 0.28;
|
|
2054
|
+
|
|
2055
|
+
// src/panel/session-routing.ts
|
|
2056
|
+
function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
2057
|
+
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
1107
2058
|
}
|
|
1108
2059
|
|
|
1109
2060
|
// src/panel/SubAgentPanel.tsx
|
|
1110
2061
|
var LEFT_PAD = 4;
|
|
1111
2062
|
var INDENT = 2;
|
|
2063
|
+
function partMetadata(part, st) {
|
|
2064
|
+
const partMeta = part?.metadata;
|
|
2065
|
+
if (partMeta && Object.keys(partMeta).length > 0) return partMeta;
|
|
2066
|
+
const stateMeta = st?.metadata;
|
|
2067
|
+
return stateMeta && Object.keys(stateMeta).length > 0 ? stateMeta : void 0;
|
|
2068
|
+
}
|
|
2069
|
+
function validModel(model) {
|
|
2070
|
+
if (typeof model !== "string") return false;
|
|
2071
|
+
const s = model.trim();
|
|
2072
|
+
return s.length > 0 && s !== "[object Object]" && !s.startsWith("{");
|
|
2073
|
+
}
|
|
1112
2074
|
function SubAgentPanel(props) {
|
|
1113
2075
|
const t = createT(() => props.lang());
|
|
1114
2076
|
const SESSION_DATA_KEY2 = `${KV_PREFIX}.session_data`;
|
|
@@ -1123,11 +2085,32 @@ function SubAgentPanel(props) {
|
|
|
1123
2085
|
return {};
|
|
1124
2086
|
}
|
|
1125
2087
|
};
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
2088
|
+
const mutateSessionData = (mutator) => {
|
|
2089
|
+
void Promise.resolve(updateSessionData(props.api.kv, mutator)).catch(() => {
|
|
2090
|
+
});
|
|
2091
|
+
};
|
|
2092
|
+
const ensureRootRecord = (data, sid) => {
|
|
2093
|
+
const rec = data[sid];
|
|
2094
|
+
if (rec) return rec;
|
|
2095
|
+
const created = {
|
|
2096
|
+
ts: Date.now(),
|
|
2097
|
+
entries: [],
|
|
2098
|
+
scroll: 0,
|
|
2099
|
+
expanded: "",
|
|
2100
|
+
children: {}
|
|
2101
|
+
};
|
|
2102
|
+
data[sid] = created;
|
|
2103
|
+
return created;
|
|
2104
|
+
};
|
|
2105
|
+
const ensureChildRecord = (data, parentSid, sid) => {
|
|
2106
|
+
const parent = ensureRootRecord(data, parentSid);
|
|
2107
|
+
if (!parent.children) parent.children = {};
|
|
2108
|
+
if (!parent.children[sid]) parent.children[sid] = {
|
|
2109
|
+
scroll: 0,
|
|
2110
|
+
expanded: "",
|
|
2111
|
+
entries: []
|
|
2112
|
+
};
|
|
2113
|
+
return parent.children[sid];
|
|
1131
2114
|
};
|
|
1132
2115
|
const resolveParent = (sid) => {
|
|
1133
2116
|
try {
|
|
@@ -1169,129 +2152,71 @@ function SubAgentPanel(props) {
|
|
|
1169
2152
|
clearTimeout(persistTimer);
|
|
1170
2153
|
persistTimer = setTimeout(() => {
|
|
1171
2154
|
try {
|
|
1172
|
-
|
|
2155
|
+
mutateSessionData((data) => {
|
|
2156
|
+
const {
|
|
2157
|
+
parentSid,
|
|
2158
|
+
isChild
|
|
2159
|
+
} = resolveParent(sid);
|
|
2160
|
+
if (isChild) {
|
|
2161
|
+
const parent = ensureRootRecord(data, parentSid);
|
|
2162
|
+
const child = ensureChildRecord(data, parentSid, sid);
|
|
2163
|
+
child.entries = [...mergeSubEntries(child.entries ?? [], entries2.values()).values()];
|
|
2164
|
+
parent.ts = Date.now();
|
|
2165
|
+
} else {
|
|
2166
|
+
const rec = ensureRootRecord(data, sid);
|
|
2167
|
+
rec.ts = Date.now();
|
|
2168
|
+
rec.entries = [...mergeSubEntries(rec.entries ?? [], entries2.values()).values()];
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
} catch {
|
|
2172
|
+
}
|
|
2173
|
+
}, 200);
|
|
2174
|
+
};
|
|
2175
|
+
const persistScroll = (sid, scroll) => {
|
|
2176
|
+
try {
|
|
2177
|
+
mutateSessionData((data) => {
|
|
1173
2178
|
const {
|
|
1174
2179
|
parentSid,
|
|
1175
2180
|
isChild
|
|
1176
2181
|
} = resolveParent(sid);
|
|
1177
2182
|
if (isChild) {
|
|
1178
|
-
|
|
1179
|
-
ts: Date.now(),
|
|
1180
|
-
entries: [],
|
|
1181
|
-
scroll: 0,
|
|
1182
|
-
expanded: "",
|
|
1183
|
-
children: {}
|
|
1184
|
-
};
|
|
1185
|
-
if (!data[parentSid].children) data[parentSid].children = {};
|
|
1186
|
-
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
|
|
1187
|
-
scroll: 0,
|
|
1188
|
-
expanded: "",
|
|
1189
|
-
entries: []
|
|
1190
|
-
};
|
|
1191
|
-
data[parentSid].children[sid] = {
|
|
1192
|
-
...data[parentSid].children[sid],
|
|
1193
|
-
entries: [...entries2.values()]
|
|
1194
|
-
};
|
|
2183
|
+
ensureChildRecord(data, parentSid, sid).scroll = scroll;
|
|
1195
2184
|
} else {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
entries: [...entries2.values()],
|
|
1200
|
-
children: data[sid]?.children ?? {}
|
|
1201
|
-
};
|
|
2185
|
+
const rec = ensureRootRecord(data, sid);
|
|
2186
|
+
rec.ts = Date.now();
|
|
2187
|
+
rec.scroll = scroll;
|
|
1202
2188
|
}
|
|
1203
|
-
|
|
1204
|
-
} catch {
|
|
1205
|
-
}
|
|
1206
|
-
}, 200);
|
|
1207
|
-
};
|
|
1208
|
-
const persistScroll = (sid, scroll) => {
|
|
1209
|
-
try {
|
|
1210
|
-
const data = loadSessionData2();
|
|
1211
|
-
const {
|
|
1212
|
-
parentSid,
|
|
1213
|
-
isChild
|
|
1214
|
-
} = resolveParent(sid);
|
|
1215
|
-
if (isChild) {
|
|
1216
|
-
if (!data[parentSid]) data[parentSid] = {
|
|
1217
|
-
ts: Date.now(),
|
|
1218
|
-
entries: [],
|
|
1219
|
-
scroll: 0,
|
|
1220
|
-
expanded: "",
|
|
1221
|
-
children: {}
|
|
1222
|
-
};
|
|
1223
|
-
if (!data[parentSid].children) data[parentSid].children = {};
|
|
1224
|
-
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
|
|
1225
|
-
scroll: 0,
|
|
1226
|
-
expanded: "",
|
|
1227
|
-
entries: []
|
|
1228
|
-
};
|
|
1229
|
-
data[parentSid].children[sid] = {
|
|
1230
|
-
...data[parentSid].children[sid],
|
|
1231
|
-
scroll
|
|
1232
|
-
};
|
|
1233
|
-
} else {
|
|
1234
|
-
data[sid] = {
|
|
1235
|
-
...data[sid],
|
|
1236
|
-
ts: Date.now(),
|
|
1237
|
-
scroll,
|
|
1238
|
-
children: data[sid]?.children ?? {}
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
saveSessionData2(data);
|
|
2189
|
+
});
|
|
1242
2190
|
} catch {
|
|
1243
2191
|
}
|
|
1244
2192
|
};
|
|
1245
2193
|
const persistExpanded = (sid, expanded2) => {
|
|
1246
2194
|
try {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
expanded
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (!data[parentSid].children) data[parentSid].children = {};
|
|
1261
|
-
if (!data[parentSid].children[sid]) data[parentSid].children[sid] = {
|
|
1262
|
-
scroll: 0,
|
|
1263
|
-
expanded: "",
|
|
1264
|
-
entries: []
|
|
1265
|
-
};
|
|
1266
|
-
data[parentSid].children[sid] = {
|
|
1267
|
-
...data[parentSid].children[sid],
|
|
1268
|
-
expanded: expanded2
|
|
1269
|
-
};
|
|
1270
|
-
} else {
|
|
1271
|
-
data[sid] = {
|
|
1272
|
-
...data[sid],
|
|
1273
|
-
ts: Date.now(),
|
|
1274
|
-
expanded: expanded2,
|
|
1275
|
-
children: data[sid]?.children ?? {}
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
saveSessionData2(data);
|
|
2195
|
+
mutateSessionData((data) => {
|
|
2196
|
+
const {
|
|
2197
|
+
parentSid,
|
|
2198
|
+
isChild
|
|
2199
|
+
} = resolveParent(sid);
|
|
2200
|
+
if (isChild) {
|
|
2201
|
+
ensureChildRecord(data, parentSid, sid).expanded = expanded2;
|
|
2202
|
+
} else {
|
|
2203
|
+
const rec = ensureRootRecord(data, sid);
|
|
2204
|
+
rec.ts = Date.now();
|
|
2205
|
+
rec.expanded = expanded2;
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
1279
2208
|
} catch {
|
|
1280
2209
|
}
|
|
1281
2210
|
};
|
|
1282
2211
|
const cleanupOldSessions = () => {
|
|
1283
2212
|
if (ttlDays <= 0) return;
|
|
1284
2213
|
try {
|
|
1285
|
-
const data = loadSessionData2();
|
|
1286
2214
|
const cutoff = Date.now() - TTL_MS;
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
delete data[sid];
|
|
1291
|
-
changed = true;
|
|
2215
|
+
mutateSessionData((data) => {
|
|
2216
|
+
for (const sid of Object.keys(data)) {
|
|
2217
|
+
if (data[sid].ts < cutoff) delete data[sid];
|
|
1292
2218
|
}
|
|
1293
|
-
}
|
|
1294
|
-
if (changed) saveSessionData2(data);
|
|
2219
|
+
});
|
|
1295
2220
|
} catch {
|
|
1296
2221
|
}
|
|
1297
2222
|
};
|
|
@@ -1311,38 +2236,22 @@ function SubAgentPanel(props) {
|
|
|
1311
2236
|
if (needsImmediateFlush) {
|
|
1312
2237
|
clearTimeout(persistTimer);
|
|
1313
2238
|
try {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
entries
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
expanded: "",
|
|
1331
|
-
entries: []
|
|
1332
|
-
};
|
|
1333
|
-
data[parentSid].children[props.sessionId] = {
|
|
1334
|
-
...data[parentSid].children[props.sessionId],
|
|
1335
|
-
entries: [...next.values()]
|
|
1336
|
-
};
|
|
1337
|
-
} else {
|
|
1338
|
-
data[props.sessionId] = {
|
|
1339
|
-
...data[props.sessionId],
|
|
1340
|
-
ts: Date.now(),
|
|
1341
|
-
entries: [...next.values()],
|
|
1342
|
-
children: data[props.sessionId]?.children ?? {}
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
saveSessionData2(data);
|
|
2239
|
+
mutateSessionData((data) => {
|
|
2240
|
+
const {
|
|
2241
|
+
parentSid,
|
|
2242
|
+
isChild
|
|
2243
|
+
} = resolveParent(props.sessionId);
|
|
2244
|
+
if (isChild) {
|
|
2245
|
+
const parent = ensureRootRecord(data, parentSid);
|
|
2246
|
+
const child = ensureChildRecord(data, parentSid, props.sessionId);
|
|
2247
|
+
child.entries = [...mergeSubEntries(child.entries ?? [], next.values()).values()];
|
|
2248
|
+
parent.ts = Date.now();
|
|
2249
|
+
} else {
|
|
2250
|
+
const rec = ensureRootRecord(data, props.sessionId);
|
|
2251
|
+
rec.ts = Date.now();
|
|
2252
|
+
rec.entries = [...mergeSubEntries(rec.entries ?? [], next.values()).values()];
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
1346
2255
|
} catch {
|
|
1347
2256
|
}
|
|
1348
2257
|
} else {
|
|
@@ -1510,7 +2419,9 @@ function SubAgentPanel(props) {
|
|
|
1510
2419
|
const part = event.payload?.part;
|
|
1511
2420
|
if (!part) return;
|
|
1512
2421
|
const eventSid = event.payload?.sessionID !== void 0 ? String(event.payload.sessionID) : void 0;
|
|
1513
|
-
if (event.scope === "global" && eventSid !== props.sessionId)
|
|
2422
|
+
if (event.scope === "global" && eventSid !== props.sessionId) {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
1514
2425
|
if (part.type === "subtask") {
|
|
1515
2426
|
const agent = String(part.agent ?? "?");
|
|
1516
2427
|
const prompt = String(part.prompt ?? "");
|
|
@@ -1539,7 +2450,7 @@ function SubAgentPanel(props) {
|
|
|
1539
2450
|
if (rawStatus === "error") {
|
|
1540
2451
|
const id2 = `tool:${String(part.id ?? "")}`;
|
|
1541
2452
|
if (!part.id) return;
|
|
1542
|
-
const stMeta2 = st
|
|
2453
|
+
const stMeta2 = partMetadata(part, st);
|
|
1543
2454
|
const errorSid = stMeta2?.session_id !== void 0 ? String(stMeta2.session_id) : stMeta2?.sessionId !== void 0 ? String(stMeta2.sessionId) : void 0;
|
|
1544
2455
|
const key = findSubEntryKey(entryMap(), id2, errorSid);
|
|
1545
2456
|
const existing = key ? entryMap().get(key) : void 0;
|
|
@@ -1559,7 +2470,7 @@ function SubAgentPanel(props) {
|
|
|
1559
2470
|
let status = "running";
|
|
1560
2471
|
if (rawStatus === "completed") status = "done";
|
|
1561
2472
|
if ((input?.run_in_background === true || input?.background === true) && status === "done") {
|
|
1562
|
-
const stMetaCheck = st
|
|
2473
|
+
const stMetaCheck = partMetadata(part, st);
|
|
1563
2474
|
const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
|
|
1564
2475
|
if (hasChild) status = "running";
|
|
1565
2476
|
}
|
|
@@ -1568,7 +2479,7 @@ function SubAgentPanel(props) {
|
|
|
1568
2479
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1569
2480
|
const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
|
|
1570
2481
|
const id = `tool:${String(part.id ?? crypto.randomUUID())}`;
|
|
1571
|
-
const stMeta = st
|
|
2482
|
+
const stMeta = partMetadata(part, st);
|
|
1572
2483
|
const subSid = stMeta?.session_id !== void 0 ? String(stMeta.session_id) : stMeta?.sessionId !== void 0 ? String(stMeta.sessionId) : void 0;
|
|
1573
2484
|
upsertEntry({
|
|
1574
2485
|
id,
|
|
@@ -1758,28 +2669,37 @@ function SubAgentPanel(props) {
|
|
|
1758
2669
|
if (!found) {
|
|
1759
2670
|
const data = loadSessionData2();
|
|
1760
2671
|
const rec = data[parentSid];
|
|
1761
|
-
if (rec?.entries) {
|
|
2672
|
+
if (rec?.entries?.length) {
|
|
1762
2673
|
const fallbackMap = new Map(rec.entries.map((e) => [e.id, e]));
|
|
1763
2674
|
found = tryMatchAndUpdate(fallbackMap, sid, status, nowTs);
|
|
1764
2675
|
if (found) {
|
|
1765
|
-
data[parentSid] = {
|
|
1766
|
-
...rec,
|
|
1767
|
-
ts: nowTs,
|
|
1768
|
-
entries: [...fallbackMap.values()]
|
|
1769
|
-
};
|
|
1770
|
-
saveSessionData2(data);
|
|
1771
2676
|
globalEntryCache.set(parentSid, fallbackMap);
|
|
2677
|
+
mutateSessionData((latest) => {
|
|
2678
|
+
const latestRec = latest[parentSid];
|
|
2679
|
+
latest[parentSid] = {
|
|
2680
|
+
...latestRec ?? rec,
|
|
2681
|
+
ts: nowTs,
|
|
2682
|
+
entries: [...mergeSubEntries(latestRec?.entries ?? [], fallbackMap.values()).values()]
|
|
2683
|
+
};
|
|
2684
|
+
});
|
|
1772
2685
|
}
|
|
1773
2686
|
}
|
|
1774
2687
|
}
|
|
1775
2688
|
if (found && parentCache) {
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2689
|
+
mutateSessionData((data) => {
|
|
2690
|
+
const rec = data[parentSid];
|
|
2691
|
+
data[parentSid] = {
|
|
2692
|
+
...rec ?? {
|
|
2693
|
+
entries: [],
|
|
2694
|
+
scroll: 0,
|
|
2695
|
+
expanded: "",
|
|
2696
|
+
children: {},
|
|
2697
|
+
ts: nowTs
|
|
2698
|
+
},
|
|
2699
|
+
ts: nowTs,
|
|
2700
|
+
entries: [...mergeSubEntries(rec?.entries ?? [], parentCache.values()).values()]
|
|
2701
|
+
};
|
|
2702
|
+
});
|
|
1783
2703
|
}
|
|
1784
2704
|
}
|
|
1785
2705
|
} catch {
|
|
@@ -1818,52 +2738,357 @@ function SubAgentPanel(props) {
|
|
|
1818
2738
|
}, 150);
|
|
1819
2739
|
};
|
|
1820
2740
|
const bump = () => setRenderTick((v) => v + 1);
|
|
2741
|
+
const settleIdleEntries = (prev) => {
|
|
2742
|
+
let changed = false;
|
|
2743
|
+
const next = new Map(prev);
|
|
2744
|
+
for (const [id, entry] of next) {
|
|
2745
|
+
if (entry.status !== "running" && entry.status !== "cancel_requested") continue;
|
|
2746
|
+
let sid = entry.sessionId;
|
|
2747
|
+
if (!sid) {
|
|
2748
|
+
try {
|
|
2749
|
+
const callId = String(entry.id).replace(/^tool:/, "");
|
|
2750
|
+
sid = props.api.session.resolveChild?.({
|
|
2751
|
+
parentId: props.sessionId,
|
|
2752
|
+
callId,
|
|
2753
|
+
agent: entry.agent,
|
|
2754
|
+
startedAt: entry.startedAt
|
|
2755
|
+
});
|
|
2756
|
+
} catch {
|
|
2757
|
+
}
|
|
2758
|
+
if (!sid) continue;
|
|
2759
|
+
next.set(id, {
|
|
2760
|
+
...entry,
|
|
2761
|
+
sessionId: sid
|
|
2762
|
+
});
|
|
2763
|
+
changed = true;
|
|
2764
|
+
}
|
|
2765
|
+
try {
|
|
2766
|
+
const st = props.api.session.status(sid);
|
|
2767
|
+
if (!st || st.type !== "idle") continue;
|
|
2768
|
+
const tokens = props.api.usage.readSessionTokens(sid);
|
|
2769
|
+
const cost = props.api.usage.readSessionCost(sid);
|
|
2770
|
+
const finalStatus = entry.status === "cancel_requested" && entry.abortAccepted ? "cancelled" : "done";
|
|
2771
|
+
next.set(id, {
|
|
2772
|
+
...entry,
|
|
2773
|
+
sessionId: sid,
|
|
2774
|
+
status: finalStatus,
|
|
2775
|
+
endedAt: Date.now(),
|
|
2776
|
+
tokens: tokens ?? entry.tokens,
|
|
2777
|
+
cost: cost ?? entry.cost
|
|
2778
|
+
});
|
|
2779
|
+
changed = true;
|
|
2780
|
+
} catch {
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
return changed ? next : prev;
|
|
2784
|
+
};
|
|
2785
|
+
const reconcileIdleEntries = () => {
|
|
2786
|
+
try {
|
|
2787
|
+
const before = entryMap();
|
|
2788
|
+
const after = settleIdleEntries(before);
|
|
2789
|
+
if (after !== before) setEntryMap(after);
|
|
2790
|
+
} catch {
|
|
2791
|
+
}
|
|
2792
|
+
};
|
|
2793
|
+
const runSessionScan = (sid, replace) => {
|
|
2794
|
+
let msgs;
|
|
2795
|
+
try {
|
|
2796
|
+
msgs = props.api.session.messages(sid);
|
|
2797
|
+
} catch {
|
|
2798
|
+
}
|
|
2799
|
+
if (!msgs || msgs.length === 0) return false;
|
|
2800
|
+
setEntryMapRaw((prev) => {
|
|
2801
|
+
const next = replace ? new Map(globalEntryCache.get(sid) ?? loadEntries(sid)) : new Map(prev);
|
|
2802
|
+
const {
|
|
2803
|
+
parentSid: scanPSid,
|
|
2804
|
+
isChild: scanChild
|
|
2805
|
+
} = resolveParent(sid);
|
|
2806
|
+
const scanRec = loadSessionData2()[scanPSid];
|
|
2807
|
+
const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds);
|
|
2808
|
+
try {
|
|
2809
|
+
for (const msg of msgs) {
|
|
2810
|
+
const parts = props.api.session.part(msg.id) ?? [];
|
|
2811
|
+
for (const partRaw of parts) {
|
|
2812
|
+
const part = partRaw;
|
|
2813
|
+
if (part.type === "tool") {
|
|
2814
|
+
const tool = String(part.tool ?? "");
|
|
2815
|
+
if (!SUBAGENT_TOOLS.has(tool)) continue;
|
|
2816
|
+
const id = `tool:${String(part.id ?? "")}`;
|
|
2817
|
+
if (!part.id) continue;
|
|
2818
|
+
const st = part.state;
|
|
2819
|
+
const rawStatus = String(st?.status ?? "");
|
|
2820
|
+
const scanInput = st?.input;
|
|
2821
|
+
const scanMeta = partMetadata(part, st);
|
|
2822
|
+
let scanSubSid = scanMeta?.session_id !== void 0 ? String(scanMeta.session_id) : scanMeta?.sessionId !== void 0 ? String(scanMeta.sessionId) : scanMeta?.sessionID !== void 0 ? String(scanMeta.sessionID) : void 0;
|
|
2823
|
+
if (!scanSubSid) {
|
|
2824
|
+
try {
|
|
2825
|
+
scanSubSid = props.api.session.resolveChild?.({
|
|
2826
|
+
parentId: sid,
|
|
2827
|
+
callId: String(part.id)
|
|
2828
|
+
});
|
|
2829
|
+
} catch {
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
2833
|
+
const exists = existingKey ? next.get(existingKey) : void 0;
|
|
2834
|
+
if (!exists && clearedIds.has(id)) continue;
|
|
2835
|
+
if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
|
|
2836
|
+
if (rawStatus === "error") {
|
|
2837
|
+
if (exists && exists.status === "running") {
|
|
2838
|
+
next.set(existingKey ?? id, {
|
|
2839
|
+
...exists,
|
|
2840
|
+
status: "error",
|
|
2841
|
+
endedAt: Date.now()
|
|
2842
|
+
});
|
|
2843
|
+
}
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
const settled = exists !== void 0 && exists.status !== "running" && exists.status !== "cancel_requested";
|
|
2847
|
+
const needsRepair = settled && (exists.sessionId === void 0 || exists.tokens === void 0 || exists.cost === void 0 || !validModel(exists.model));
|
|
2848
|
+
if (settled && !needsRepair) continue;
|
|
2849
|
+
let status = "running";
|
|
2850
|
+
if (rawStatus === "completed") status = "done";
|
|
2851
|
+
if ((scanInput?.run_in_background === true || scanInput?.background === true) && status === "done") {
|
|
2852
|
+
if (scanSubSid !== void 0) status = "running";
|
|
2853
|
+
}
|
|
2854
|
+
if (status === "done" && scanSubSid) {
|
|
2855
|
+
try {
|
|
2856
|
+
const childStatus = props.api.session.status(scanSubSid);
|
|
2857
|
+
if (childStatus && childStatus.type !== "idle") status = "running";
|
|
2858
|
+
} catch {
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
if (settled) {
|
|
2862
|
+
status = exists.status;
|
|
2863
|
+
} else if (exists && status === "running") {
|
|
2864
|
+
if (!rawStatus) {
|
|
2865
|
+
const msgTokens = msg?.tokens;
|
|
2866
|
+
if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
|
|
2867
|
+
status = "done";
|
|
2868
|
+
} else {
|
|
2869
|
+
continue;
|
|
2870
|
+
}
|
|
2871
|
+
} else {
|
|
2872
|
+
continue;
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
const agent = String(part.subagent_type ?? scanInput?.agent ?? scanInput?.subagent_type ?? tool);
|
|
2876
|
+
const prompt = String(scanInput?.prompt ?? part.description ?? "");
|
|
2877
|
+
const desc = scanInput?.description !== void 0 ? String(scanInput.description) : "";
|
|
2878
|
+
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
2879
|
+
let tokens;
|
|
2880
|
+
if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
2881
|
+
let cost;
|
|
2882
|
+
if (scanSubSid) cost = props.api.usage.readSessionCost(scanSubSid);
|
|
2883
|
+
let model;
|
|
2884
|
+
if (scanSubSid) model = props.api.usage.readSessionModel(scanSubSid);
|
|
2885
|
+
const entryKey = existingKey ?? id;
|
|
2886
|
+
next.set(entryKey, {
|
|
2887
|
+
...exists ?? {
|
|
2888
|
+
id
|
|
2889
|
+
},
|
|
2890
|
+
title: title || exists?.title || "",
|
|
2891
|
+
agent: exists?.agent ?? agent,
|
|
2892
|
+
prompt: prompt || exists?.prompt || "",
|
|
2893
|
+
// Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
|
|
2894
|
+
tokens: exists?.tokens ?? tokens,
|
|
2895
|
+
cost: exists?.cost ?? cost,
|
|
2896
|
+
model: validModel(exists?.model) ? exists.model : model,
|
|
2897
|
+
sessionId: exists?.sessionId ?? scanSubSid,
|
|
2898
|
+
status,
|
|
2899
|
+
startedAt: exists?.startedAt || Date.now(),
|
|
2900
|
+
endedAt: exists?.endedAt ?? (status === "done" ? Date.now() : void 0)
|
|
2901
|
+
});
|
|
2902
|
+
if (entryKey !== id) next.delete(id);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
} catch {
|
|
2907
|
+
}
|
|
2908
|
+
return next;
|
|
2909
|
+
});
|
|
2910
|
+
setEntryMapRaw((prev) => settleIdleEntries(prev));
|
|
2911
|
+
const snapshot = entryMap();
|
|
2912
|
+
if (snapshot.size > 0) {
|
|
2913
|
+
globalEntryCache.set(sid, new Map(snapshot));
|
|
2914
|
+
persistEntries(sid, snapshot);
|
|
2915
|
+
}
|
|
2916
|
+
return true;
|
|
2917
|
+
};
|
|
2918
|
+
let rescanTimer;
|
|
2919
|
+
const missingNewestToolPart = () => {
|
|
2920
|
+
try {
|
|
2921
|
+
const msgs = props.api.session.messages(props.sessionId);
|
|
2922
|
+
if (!msgs || msgs.length === 0) return false;
|
|
2923
|
+
const map = entryMap();
|
|
2924
|
+
const from = Math.max(0, msgs.length - 6);
|
|
2925
|
+
for (let i = msgs.length - 1; i >= from; i--) {
|
|
2926
|
+
const parts = props.api.session.part(msgs[i]?.id) ?? [];
|
|
2927
|
+
for (const part of parts) {
|
|
2928
|
+
if (!part || part.type !== "tool") continue;
|
|
2929
|
+
if (!SUBAGENT_TOOLS.has(String(part.tool ?? ""))) continue;
|
|
2930
|
+
const partId = String(part.id ?? "");
|
|
2931
|
+
if (!partId) continue;
|
|
2932
|
+
const st = part.state;
|
|
2933
|
+
const rawStatus = String(st?.status ?? "");
|
|
2934
|
+
if (rawStatus !== "running" && rawStatus !== "completed") continue;
|
|
2935
|
+
const id = `tool:${partId}`;
|
|
2936
|
+
const meta = partMetadata(part, st);
|
|
2937
|
+
const subSid = meta?.session_id !== void 0 ? String(meta.session_id) : meta?.sessionId !== void 0 ? String(meta.sessionId) : void 0;
|
|
2938
|
+
const key = findSubEntryKey(map, id, subSid);
|
|
2939
|
+
const entry = key ? map.get(key) : void 0;
|
|
2940
|
+
if (!entry) return true;
|
|
2941
|
+
const input = st?.input;
|
|
2942
|
+
const isBackground = input?.background === true || input?.run_in_background === true;
|
|
2943
|
+
const stillRunning = entry.status === "running" || entry.status === "cancel_requested";
|
|
2944
|
+
if (rawStatus === "completed" && !isBackground && stillRunning) return true;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
} catch {
|
|
2948
|
+
}
|
|
2949
|
+
return false;
|
|
2950
|
+
};
|
|
2951
|
+
const scheduleRescan = () => {
|
|
2952
|
+
if (disposed) return;
|
|
2953
|
+
clearTimeout(rescanTimer);
|
|
2954
|
+
rescanTimer = setTimeout(() => {
|
|
2955
|
+
if (disposed) return;
|
|
2956
|
+
untrack(() => {
|
|
2957
|
+
if (!missingNewestToolPart()) return;
|
|
2958
|
+
runSessionScan(props.sessionId, false);
|
|
2959
|
+
bump();
|
|
2960
|
+
});
|
|
2961
|
+
}, 400);
|
|
2962
|
+
};
|
|
2963
|
+
let enrichCursor = 0;
|
|
1821
2964
|
onMount(() => {
|
|
1822
2965
|
const clock = setInterval(() => {
|
|
1823
2966
|
setNow(Date.now());
|
|
1824
2967
|
bump();
|
|
1825
2968
|
}, 100);
|
|
2969
|
+
let tick = 0;
|
|
1826
2970
|
const tokenTimer = setInterval(() => {
|
|
2971
|
+
tick++;
|
|
1827
2972
|
untrack(() => {
|
|
2973
|
+
let enriched;
|
|
2974
|
+
let attempted = 0;
|
|
1828
2975
|
setEntryMapRaw((prev) => {
|
|
1829
2976
|
let changed = false;
|
|
1830
2977
|
const next = new Map(prev);
|
|
2978
|
+
const candidates = [];
|
|
1831
2979
|
for (const [id, entry] of next) {
|
|
1832
|
-
|
|
2980
|
+
const running = entry.status === "running" || entry.status === "cancel_requested";
|
|
2981
|
+
const missing = entry.tokens === void 0 || entry.cost === void 0 || !validModel(entry.model);
|
|
2982
|
+
if (entry.sessionId && !running && !missing) continue;
|
|
2983
|
+
candidates.push(id);
|
|
2984
|
+
}
|
|
2985
|
+
if (candidates.length > 0) {
|
|
2986
|
+
const start = enrichCursor % candidates.length;
|
|
2987
|
+
let budget = 4;
|
|
2988
|
+
for (let k = 0; k < candidates.length && budget > 0; k++) {
|
|
2989
|
+
const id = candidates[(start + k) % candidates.length];
|
|
2990
|
+
let entry = next.get(id);
|
|
2991
|
+
if (!entry) continue;
|
|
2992
|
+
if (!entry.sessionId) {
|
|
2993
|
+
try {
|
|
2994
|
+
const callId = String(entry.id).replace(/^tool:/, "");
|
|
2995
|
+
const sid = props.api.session.resolveChild?.({
|
|
2996
|
+
parentId: props.sessionId,
|
|
2997
|
+
callId,
|
|
2998
|
+
agent: entry.agent,
|
|
2999
|
+
startedAt: entry.startedAt
|
|
3000
|
+
});
|
|
3001
|
+
if (sid) {
|
|
3002
|
+
entry = {
|
|
3003
|
+
...entry,
|
|
3004
|
+
sessionId: sid
|
|
3005
|
+
};
|
|
3006
|
+
next.set(id, entry);
|
|
3007
|
+
changed = true;
|
|
3008
|
+
}
|
|
3009
|
+
} catch {
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
if (!entry.sessionId) continue;
|
|
3013
|
+
const childSid = entry.sessionId;
|
|
1833
3014
|
let isChild = false;
|
|
1834
3015
|
try {
|
|
1835
|
-
const s = props.api.session.get(
|
|
1836
|
-
isChild = s
|
|
3016
|
+
const s = props.api.session.get(childSid);
|
|
3017
|
+
isChild = !s || s.parentID === props.sessionId;
|
|
1837
3018
|
} catch {
|
|
3019
|
+
isChild = true;
|
|
1838
3020
|
}
|
|
1839
3021
|
if (!isChild) continue;
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
const
|
|
3022
|
+
budget--;
|
|
3023
|
+
attempted++;
|
|
3024
|
+
const running = entry.status === "running" || entry.status === "cancel_requested";
|
|
3025
|
+
let entryChanged = false;
|
|
1843
3026
|
const nextEntry = {
|
|
1844
3027
|
...entry
|
|
1845
3028
|
};
|
|
1846
|
-
if (
|
|
1847
|
-
|
|
1848
|
-
|
|
3029
|
+
if (running || entry.tokens === void 0) {
|
|
3030
|
+
const total = props.api.usage.readSessionTokens(childSid);
|
|
3031
|
+
if (total !== void 0 && total !== entry.tokens) {
|
|
3032
|
+
nextEntry.tokens = total;
|
|
3033
|
+
entryChanged = true;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
if (running || entry.cost === void 0) {
|
|
3037
|
+
const cost = props.api.usage.readSessionCost(childSid);
|
|
3038
|
+
if (cost !== void 0 && cost !== entry.cost) {
|
|
3039
|
+
nextEntry.cost = cost;
|
|
3040
|
+
entryChanged = true;
|
|
3041
|
+
}
|
|
1849
3042
|
}
|
|
1850
|
-
if (
|
|
1851
|
-
|
|
3043
|
+
if (running || !validModel(entry.model)) {
|
|
3044
|
+
const model = props.api.usage.readSessionModel(childSid);
|
|
3045
|
+
if (model && model !== entry.model) {
|
|
3046
|
+
nextEntry.model = model;
|
|
3047
|
+
entryChanged = true;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
if (running) {
|
|
3051
|
+
const todo = props.api.usage.readSessionTodo(childSid);
|
|
3052
|
+
if (todo !== void 0 && (todo.total !== entry.todoTotal || todo.done !== entry.todoDone)) {
|
|
1852
3053
|
nextEntry.todoTotal = todo.total;
|
|
1853
3054
|
nextEntry.todoDone = todo.done;
|
|
1854
|
-
|
|
3055
|
+
entryChanged = true;
|
|
1855
3056
|
}
|
|
1856
3057
|
}
|
|
1857
|
-
if (
|
|
1858
|
-
|
|
3058
|
+
if (entryChanged) {
|
|
3059
|
+
next.set(id, nextEntry);
|
|
1859
3060
|
changed = true;
|
|
1860
3061
|
}
|
|
1861
|
-
if (changed) next.set(id, nextEntry);
|
|
1862
3062
|
}
|
|
3063
|
+
enrichCursor = (start + Math.max(1, attempted)) % candidates.length;
|
|
1863
3064
|
}
|
|
1864
|
-
|
|
3065
|
+
if (!changed) return prev;
|
|
3066
|
+
enriched = next;
|
|
3067
|
+
return next;
|
|
1865
3068
|
});
|
|
3069
|
+
if (enriched) {
|
|
3070
|
+
globalEntryCache.set(props.sessionId, new Map(enriched));
|
|
3071
|
+
persistEntries(props.sessionId, enriched);
|
|
3072
|
+
}
|
|
1866
3073
|
});
|
|
3074
|
+
reconcileIdleEntries();
|
|
3075
|
+
if (tick % 4 === 0) {
|
|
3076
|
+
untrack(() => {
|
|
3077
|
+
if (missingNewestToolPart()) {
|
|
3078
|
+
runSessionScan(props.sessionId, false);
|
|
3079
|
+
bump();
|
|
3080
|
+
}
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
if (tick % 60 === 0) {
|
|
3084
|
+
untrack(() => {
|
|
3085
|
+
const hasRunning = entryList().some((e) => e.status === "running" || e.status === "cancel_requested");
|
|
3086
|
+
if (hasRunning) {
|
|
3087
|
+
runSessionScan(props.sessionId, false);
|
|
3088
|
+
bump();
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
1867
3092
|
bump();
|
|
1868
3093
|
}, 500);
|
|
1869
3094
|
bump();
|
|
@@ -1871,7 +3096,10 @@ function SubAgentPanel(props) {
|
|
|
1871
3096
|
handlePartUpdated(e);
|
|
1872
3097
|
bump();
|
|
1873
3098
|
});
|
|
1874
|
-
const unsubMsg = props.api.event.on("message.updated", () =>
|
|
3099
|
+
const unsubMsg = props.api.event.on("message.updated", () => {
|
|
3100
|
+
bump();
|
|
3101
|
+
scheduleRescan();
|
|
3102
|
+
});
|
|
1875
3103
|
const unsubIdle = props.api.event.on("session.idle", (e) => {
|
|
1876
3104
|
handleSessionEnd(e, "done");
|
|
1877
3105
|
bump();
|
|
@@ -1882,6 +3110,7 @@ function SubAgentPanel(props) {
|
|
|
1882
3110
|
});
|
|
1883
3111
|
onCleanup(() => {
|
|
1884
3112
|
disposed = true;
|
|
3113
|
+
clearTimeout(rescanTimer);
|
|
1885
3114
|
clearInterval(clock);
|
|
1886
3115
|
clearInterval(tokenTimer);
|
|
1887
3116
|
unsubPart();
|
|
@@ -1899,7 +3128,9 @@ function SubAgentPanel(props) {
|
|
|
1899
3128
|
const tick = clearTick();
|
|
1900
3129
|
const forceReload = tick !== lastTick && !switched;
|
|
1901
3130
|
lastTick = tick;
|
|
1902
|
-
|
|
3131
|
+
let attempts = 0;
|
|
3132
|
+
let retryTimer;
|
|
3133
|
+
const run = () => {
|
|
1903
3134
|
untrack(() => {
|
|
1904
3135
|
if (switched) {
|
|
1905
3136
|
const {
|
|
@@ -1910,128 +3141,25 @@ function SubAgentPanel(props) {
|
|
|
1910
3141
|
const saved = isChild ? data[parentSid]?.children?.[sid]?.scroll ?? 0 : data[sid]?.scroll ?? 0;
|
|
1911
3142
|
setScrollOffset(saved);
|
|
1912
3143
|
if (!isChild && data[sid]?.entries?.length) {
|
|
1913
|
-
|
|
1914
|
-
|
|
3144
|
+
void Promise.resolve(updateSessionData(props.api.kv, (latest) => {
|
|
3145
|
+
if (latest[sid]?.entries?.length) latest[sid].ts = Date.now();
|
|
3146
|
+
})).catch(() => {
|
|
3147
|
+
});
|
|
1915
3148
|
}
|
|
1916
3149
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
} = resolveParent(sid);
|
|
1923
|
-
const scanRec = loadSessionData2()[scanPSid];
|
|
1924
|
-
const clearedIds = new Set(scanChild ? scanRec?.children?.[sid]?.clearedIds : scanRec?.clearedIds);
|
|
1925
|
-
try {
|
|
1926
|
-
const msgs = props.api.session.messages(sid);
|
|
1927
|
-
if (msgs && msgs.length) {
|
|
1928
|
-
for (const msg of msgs) {
|
|
1929
|
-
const parts = props.api.session.part(msg.id) ?? [];
|
|
1930
|
-
for (const partRaw of parts) {
|
|
1931
|
-
const part = partRaw;
|
|
1932
|
-
if (part.type === "tool") {
|
|
1933
|
-
const tool = String(part.tool ?? "");
|
|
1934
|
-
if (!SUBAGENT_TOOLS.has(tool)) continue;
|
|
1935
|
-
const id = `tool:${String(part.id ?? "")}`;
|
|
1936
|
-
if (!part.id) continue;
|
|
1937
|
-
const st = part.state;
|
|
1938
|
-
const rawStatus = String(st?.status ?? "");
|
|
1939
|
-
const scanStMeta = st?.metadata;
|
|
1940
|
-
const scanSubSid = scanStMeta?.session_id !== void 0 ? String(scanStMeta.session_id) : scanStMeta?.sessionId !== void 0 ? String(scanStMeta.sessionId) : void 0;
|
|
1941
|
-
const existingKey = findSubEntryKey(next, id, scanSubSid);
|
|
1942
|
-
const exists = existingKey ? next.get(existingKey) : void 0;
|
|
1943
|
-
if (!exists && clearedIds.has(id)) continue;
|
|
1944
|
-
if ((rawStatus === "pending" || rawStatus === "") && !exists) continue;
|
|
1945
|
-
if (rawStatus === "error") {
|
|
1946
|
-
if (exists && exists.status === "running") {
|
|
1947
|
-
next.set(existingKey ?? id, {
|
|
1948
|
-
...exists,
|
|
1949
|
-
status: "error",
|
|
1950
|
-
endedAt: Date.now()
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
continue;
|
|
1954
|
-
}
|
|
1955
|
-
let status = "running";
|
|
1956
|
-
if (rawStatus === "completed") status = "done";
|
|
1957
|
-
if ((st?.input?.run_in_background === true || st?.input?.background === true) && status === "done") {
|
|
1958
|
-
const scanStMeta2 = st?.metadata;
|
|
1959
|
-
const scanHasChild = scanStMeta2?.session_id !== void 0 || scanStMeta2?.sessionId !== void 0;
|
|
1960
|
-
if (scanHasChild) status = "running";
|
|
1961
|
-
}
|
|
1962
|
-
if (exists && existingKey === id && exists.status !== "running" && exists.status !== "cancel_requested") continue;
|
|
1963
|
-
if (exists && status === "running") {
|
|
1964
|
-
if (!rawStatus) {
|
|
1965
|
-
const msgTokens = msg?.tokens;
|
|
1966
|
-
if (msgTokens && (Number(msgTokens.input) > 0 || Number(msgTokens.output) > 0)) {
|
|
1967
|
-
status = "done";
|
|
1968
|
-
} else if (Date.now() - exists.startedAt > 30 * 60 * 1e3) {
|
|
1969
|
-
status = "done";
|
|
1970
|
-
} else {
|
|
1971
|
-
continue;
|
|
1972
|
-
}
|
|
1973
|
-
} else {
|
|
1974
|
-
continue;
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
const input = st?.input;
|
|
1978
|
-
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? tool);
|
|
1979
|
-
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
1980
|
-
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1981
|
-
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
1982
|
-
let tokens;
|
|
1983
|
-
if (scanSubSid) tokens = props.api.usage.readSessionTokens(scanSubSid);
|
|
1984
|
-
const ended = status === "done";
|
|
1985
|
-
const entryKey = existingKey ?? id;
|
|
1986
|
-
next.set(entryKey, {
|
|
1987
|
-
id: exists?.id ?? id,
|
|
1988
|
-
title,
|
|
1989
|
-
agent,
|
|
1990
|
-
prompt,
|
|
1991
|
-
// Preserve existing values (from handleSessionEnd / KV) — scan must not overwrite
|
|
1992
|
-
tokens: exists?.tokens ?? tokens,
|
|
1993
|
-
sessionId: exists?.sessionId ?? scanSubSid,
|
|
1994
|
-
status,
|
|
1995
|
-
startedAt: exists?.startedAt || Date.now(),
|
|
1996
|
-
endedAt: ended ? exists?.endedAt || Date.now() : void 0
|
|
1997
|
-
});
|
|
1998
|
-
if (entryKey !== id) next.delete(id);
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
} catch {
|
|
2004
|
-
}
|
|
2005
|
-
return next;
|
|
2006
|
-
});
|
|
2007
|
-
setEntryMapRaw((prev) => {
|
|
2008
|
-
let changed = false;
|
|
2009
|
-
const next = new Map(prev);
|
|
2010
|
-
for (const [id, entry] of next) {
|
|
2011
|
-
if (entry.status !== "running" && entry.status !== "cancel_requested" || !entry.sessionId) continue;
|
|
2012
|
-
try {
|
|
2013
|
-
const st = props.api.session.status(entry.sessionId);
|
|
2014
|
-
if (!st || st.type !== "idle") continue;
|
|
2015
|
-
const tokens = props.api.usage.readSessionTokens(entry.sessionId);
|
|
2016
|
-
const cost = props.api.usage.readSessionCost(entry.sessionId);
|
|
2017
|
-
const finalStatus = entry.status === "cancel_requested" && entry.abortAccepted ? "cancelled" : "done";
|
|
2018
|
-
next.set(id, {
|
|
2019
|
-
...entry,
|
|
2020
|
-
status: finalStatus,
|
|
2021
|
-
endedAt: Date.now(),
|
|
2022
|
-
tokens: tokens ?? entry.tokens,
|
|
2023
|
-
cost: cost ?? entry.cost
|
|
2024
|
-
});
|
|
2025
|
-
changed = true;
|
|
2026
|
-
} catch {
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
return changed ? next : prev;
|
|
2030
|
-
});
|
|
2031
|
-
bump();
|
|
3150
|
+
const ok = runSessionScan(sid, switched || forceReload);
|
|
3151
|
+
if (!ok && attempts < 4) {
|
|
3152
|
+
attempts++;
|
|
3153
|
+
retryTimer = setTimeout(run, 400);
|
|
3154
|
+
}
|
|
2032
3155
|
});
|
|
2033
|
-
|
|
2034
|
-
|
|
3156
|
+
bump();
|
|
3157
|
+
};
|
|
3158
|
+
const t2 = setTimeout(run, 150);
|
|
3159
|
+
onCleanup(() => {
|
|
3160
|
+
clearTimeout(t2);
|
|
3161
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
3162
|
+
});
|
|
2035
3163
|
});
|
|
2036
3164
|
const pal = createMemo(() => {
|
|
2037
3165
|
const th = props.theme;
|
|
@@ -2410,14 +3538,17 @@ function SubAgentPanel(props) {
|
|
|
2410
3538
|
return "#" + [r, g, bl].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
2411
3539
|
};
|
|
2412
3540
|
const timeColor = () => isActiveRunning ? pal().warning : isError ? pal().error : pal().muted;
|
|
2413
|
-
const tokenText = () => !isExpanded() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
|
|
2414
|
-
const
|
|
3541
|
+
const tokenText = () => !isExpanded() && props.showEntryTokens() && entry.tokens !== void 0 && entry.tokens > 0 ? ` ${fmtTokens(entry.tokens)}` : "";
|
|
3542
|
+
const costText = () => !isExpanded() && props.showEntryCost() && entry.cost !== void 0 && entry.cost > 0 ? ` $${entry.cost.toFixed(4)}` : "";
|
|
3543
|
+
const timeText = () => !isExpanded() && props.showEntryTime() && (elapsed() >= 2e3 || entry.endedAt !== void 0) ? fmtDuration(elapsed(), isActiveRunning, props.timeFormat()) : "";
|
|
2415
3544
|
const suffixW = () => {
|
|
2416
3545
|
let w = 0;
|
|
2417
3546
|
const t2 = timeText();
|
|
2418
3547
|
if (t2) w += 1 + visualWidth(t2);
|
|
2419
3548
|
const tk = tokenText();
|
|
2420
3549
|
if (tk) w += visualWidth(tk);
|
|
3550
|
+
const c = costText();
|
|
3551
|
+
if (c) w += visualWidth(c);
|
|
2421
3552
|
return w;
|
|
2422
3553
|
};
|
|
2423
3554
|
const labelAvail = () => Math.max(6, panelWidth() - gutter() - LEFT_PAD - suffixW());
|
|
@@ -2461,6 +3592,17 @@ function SubAgentPanel(props) {
|
|
|
2461
3592
|
return _el$83;
|
|
2462
3593
|
})() : null;
|
|
2463
3594
|
})(), null);
|
|
3595
|
+
_$insert(_el$25, (() => {
|
|
3596
|
+
var _c$4 = _$memo(() => !!costText());
|
|
3597
|
+
return () => _c$4() ? (() => {
|
|
3598
|
+
var _el$84 = _$createElement("span");
|
|
3599
|
+
_$insert(_el$84, costText);
|
|
3600
|
+
_$effect((_$p) => _$setProp(_el$84, "style", {
|
|
3601
|
+
fg: pal().warning
|
|
3602
|
+
}, _$p));
|
|
3603
|
+
return _el$84;
|
|
3604
|
+
})() : null;
|
|
3605
|
+
})(), null);
|
|
2464
3606
|
_$effect((_p$) => {
|
|
2465
3607
|
var _v$3 = {
|
|
2466
3608
|
fg: pal().muted
|
|
@@ -2553,7 +3695,7 @@ function SubAgentPanel(props) {
|
|
|
2553
3695
|
_$insertNode(_el$45, _el$46);
|
|
2554
3696
|
_$insert(_el$45, () => t("time.label"), _el$46);
|
|
2555
3697
|
_$insert(_el$47, () => " ".repeat(expandedPad(t("time.label"))));
|
|
2556
|
-
_$insert(_el$48, () =>
|
|
3698
|
+
_$insert(_el$48, () => fmtDuration(elapsed(), isActiveRunning, props.timeFormat()));
|
|
2557
3699
|
_$effect((_p$) => {
|
|
2558
3700
|
var _v$10 = {
|
|
2559
3701
|
fg: pal().primary
|
|
@@ -2647,16 +3789,16 @@ function SubAgentPanel(props) {
|
|
|
2647
3789
|
return (() => {
|
|
2648
3790
|
const cost = entry.cost;
|
|
2649
3791
|
return (() => {
|
|
2650
|
-
var _el$
|
|
2651
|
-
_$insertNode(_el$
|
|
2652
|
-
_$insertNode(_el$
|
|
2653
|
-
_$insertNode(_el$
|
|
2654
|
-
_$insertNode(_el$
|
|
2655
|
-
_$insertNode(_el$
|
|
2656
|
-
_$insert(_el$
|
|
2657
|
-
_$insert(_el$
|
|
2658
|
-
_$insertNode(_el$
|
|
2659
|
-
_$insert(_el$
|
|
3792
|
+
var _el$85 = _$createElement("text"), _el$86 = _$createTextNode(` `), _el$87 = _$createElement("span"), _el$88 = _$createTextNode(`: `), _el$89 = _$createElement("span"), _el$90 = _$createElement("span"), _el$91 = _$createTextNode(`$`);
|
|
3793
|
+
_$insertNode(_el$85, _el$86);
|
|
3794
|
+
_$insertNode(_el$85, _el$87);
|
|
3795
|
+
_$insertNode(_el$85, _el$89);
|
|
3796
|
+
_$insertNode(_el$85, _el$90);
|
|
3797
|
+
_$insertNode(_el$87, _el$88);
|
|
3798
|
+
_$insert(_el$87, () => t("cost.label"), _el$88);
|
|
3799
|
+
_$insert(_el$89, () => " ".repeat(expandedPad(t("cost.label"))));
|
|
3800
|
+
_$insertNode(_el$90, _el$91);
|
|
3801
|
+
_$insert(_el$90, () => cost.toFixed(4), null);
|
|
2660
3802
|
_$effect((_p$) => {
|
|
2661
3803
|
var _v$29 = {
|
|
2662
3804
|
fg: pal().primary
|
|
@@ -2665,22 +3807,22 @@ function SubAgentPanel(props) {
|
|
|
2665
3807
|
}, _v$31 = {
|
|
2666
3808
|
fg: pal().muted
|
|
2667
3809
|
};
|
|
2668
|
-
_v$29 !== _p$.e && (_p$.e = _$setProp(_el$
|
|
2669
|
-
_v$30 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
2670
|
-
_v$31 !== _p$.a && (_p$.a = _$setProp(_el$
|
|
3810
|
+
_v$29 !== _p$.e && (_p$.e = _$setProp(_el$87, "style", _v$29, _p$.e));
|
|
3811
|
+
_v$30 !== _p$.t && (_p$.t = _$setProp(_el$89, "style", _v$30, _p$.t));
|
|
3812
|
+
_v$31 !== _p$.a && (_p$.a = _$setProp(_el$90, "style", _v$31, _p$.a));
|
|
2671
3813
|
return _p$;
|
|
2672
3814
|
}, {
|
|
2673
3815
|
e: void 0,
|
|
2674
3816
|
t: void 0,
|
|
2675
3817
|
a: void 0
|
|
2676
3818
|
});
|
|
2677
|
-
return _el$
|
|
3819
|
+
return _el$85;
|
|
2678
3820
|
})();
|
|
2679
3821
|
})();
|
|
2680
3822
|
}
|
|
2681
3823
|
}), _$createComponent(Show, {
|
|
2682
3824
|
get when() {
|
|
2683
|
-
return entry.model;
|
|
3825
|
+
return _$memo(() => !!validModel(entry.model))() ? entry.model : void 0;
|
|
2684
3826
|
},
|
|
2685
3827
|
get children() {
|
|
2686
3828
|
var _el$61 = _$createElement("text"), _el$62 = _$createTextNode(` `), _el$63 = _$createElement("span"), _el$64 = _$createTextNode(`: `), _el$65 = _$createElement("span"), _el$66 = _$createElement("span");
|
|
@@ -2819,68 +3961,68 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2819
3961
|
const rightW = (isRunning ? visualWidth(dismissLabel()) : 0) + (isRunning && entry.sessionId ? visualWidth(cancelLabel()) : 0);
|
|
2820
3962
|
const spacerW = () => Math.max(1, panelWidth() - gutter() - openW() - rightW - 2);
|
|
2821
3963
|
return (() => {
|
|
2822
|
-
var _el$
|
|
2823
|
-
_$insertNode(_el$
|
|
2824
|
-
_$setProp(_el$
|
|
2825
|
-
_$insert(_el$
|
|
3964
|
+
var _el$92 = _$createElement("box"), _el$96 = _$createElement("text");
|
|
3965
|
+
_$insertNode(_el$92, _el$96);
|
|
3966
|
+
_$setProp(_el$92, "flexDirection", "row");
|
|
3967
|
+
_$insert(_el$92, _$createComponent(Show, {
|
|
2826
3968
|
get when() {
|
|
2827
3969
|
return entry.sessionId;
|
|
2828
3970
|
},
|
|
2829
3971
|
get children() {
|
|
2830
|
-
var _el$
|
|
2831
|
-
_$insertNode(_el$
|
|
2832
|
-
_$insertNode(_el$
|
|
2833
|
-
_$setProp(_el$
|
|
2834
|
-
_$setProp(_el$
|
|
2835
|
-
_$setProp(_el$
|
|
3972
|
+
var _el$93 = _$createElement("text"), _el$94 = _$createElement("span"), _el$95 = _$createElement("span");
|
|
3973
|
+
_$insertNode(_el$93, _el$94);
|
|
3974
|
+
_$insertNode(_el$93, _el$95);
|
|
3975
|
+
_$setProp(_el$93, "onMouseOver", () => setHoveredOpen(entry.id));
|
|
3976
|
+
_$setProp(_el$93, "onMouseOut", () => setHoveredOpen(void 0));
|
|
3977
|
+
_$setProp(_el$93, "onMouseUp", () => {
|
|
2836
3978
|
if (entry.sessionId) {
|
|
2837
3979
|
props.api.route.navigateSession(entry.sessionId);
|
|
2838
3980
|
}
|
|
2839
3981
|
});
|
|
2840
|
-
_$insert(_el$
|
|
2841
|
-
_$insert(_el$
|
|
3982
|
+
_$insert(_el$94, openPrefix);
|
|
3983
|
+
_$insert(_el$95, () => t("open.label"));
|
|
2842
3984
|
_$effect((_p$) => {
|
|
2843
3985
|
var _v$32 = {
|
|
2844
3986
|
fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
|
|
2845
3987
|
}, _v$33 = {
|
|
2846
3988
|
fg: hoveredOpen() === entry.id ? pal().warning : pal().primary
|
|
2847
3989
|
};
|
|
2848
|
-
_v$32 !== _p$.e && (_p$.e = _$setProp(_el$
|
|
2849
|
-
_v$33 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
3990
|
+
_v$32 !== _p$.e && (_p$.e = _$setProp(_el$94, "style", _v$32, _p$.e));
|
|
3991
|
+
_v$33 !== _p$.t && (_p$.t = _$setProp(_el$95, "style", _v$33, _p$.t));
|
|
2850
3992
|
return _p$;
|
|
2851
3993
|
}, {
|
|
2852
3994
|
e: void 0,
|
|
2853
3995
|
t: void 0
|
|
2854
3996
|
});
|
|
2855
|
-
return _el$
|
|
3997
|
+
return _el$93;
|
|
2856
3998
|
}
|
|
2857
|
-
}), _el$
|
|
2858
|
-
_$insert(_el$
|
|
2859
|
-
_$insert(_el$
|
|
3999
|
+
}), _el$96);
|
|
4000
|
+
_$insert(_el$96, () => " ".repeat(spacerW()));
|
|
4001
|
+
_$insert(_el$92, _$createComponent(Show, {
|
|
2860
4002
|
get when() {
|
|
2861
4003
|
return isRunning && entry.sessionId;
|
|
2862
4004
|
},
|
|
2863
4005
|
get children() {
|
|
2864
|
-
var _el$
|
|
2865
|
-
_$insertNode(_el$
|
|
2866
|
-
_$setProp(_el$
|
|
2867
|
-
_$setProp(_el$
|
|
2868
|
-
_$setProp(_el$
|
|
2869
|
-
_$insert(_el$
|
|
2870
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4006
|
+
var _el$97 = _$createElement("text"), _el$98 = _$createElement("span");
|
|
4007
|
+
_$insertNode(_el$97, _el$98);
|
|
4008
|
+
_$setProp(_el$97, "onMouseOver", () => setHoveredCancel(entry.id));
|
|
4009
|
+
_$setProp(_el$97, "onMouseOut", () => setHoveredCancel(void 0));
|
|
4010
|
+
_$setProp(_el$97, "onMouseUp", () => cancelEntry(entry));
|
|
4011
|
+
_$insert(_el$98, cancelLabel);
|
|
4012
|
+
_$effect((_$p) => _$setProp(_el$98, "style", {
|
|
2871
4013
|
fg: hoveredCancel() === entry.id ? pal().warning : pal().error
|
|
2872
4014
|
}, _$p));
|
|
2873
|
-
return _el$
|
|
4015
|
+
return _el$97;
|
|
2874
4016
|
}
|
|
2875
4017
|
}), null);
|
|
2876
|
-
_$insert(_el$
|
|
4018
|
+
_$insert(_el$92, _$createComponent(Show, {
|
|
2877
4019
|
when: isRunning,
|
|
2878
4020
|
get children() {
|
|
2879
|
-
var _el$
|
|
2880
|
-
_$insertNode(_el$
|
|
2881
|
-
_$setProp(_el$
|
|
2882
|
-
_$setProp(_el$
|
|
2883
|
-
_$setProp(_el$
|
|
4021
|
+
var _el$99 = _$createElement("text"), _el$100 = _$createElement("span");
|
|
4022
|
+
_$insertNode(_el$99, _el$100);
|
|
4023
|
+
_$setProp(_el$99, "onMouseOver", () => setHoveredDismiss(entry.id));
|
|
4024
|
+
_$setProp(_el$99, "onMouseOut", () => setHoveredDismiss(void 0));
|
|
4025
|
+
_$setProp(_el$99, "onMouseUp", () => {
|
|
2884
4026
|
upsertEntry({
|
|
2885
4027
|
id: entry.id,
|
|
2886
4028
|
title: entry.title,
|
|
@@ -2889,17 +4031,17 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2889
4031
|
status: "done"
|
|
2890
4032
|
});
|
|
2891
4033
|
});
|
|
2892
|
-
_$insert(_el$
|
|
2893
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4034
|
+
_$insert(_el$100, dismissLabel);
|
|
4035
|
+
_$effect((_$p) => _$setProp(_el$100, "style", {
|
|
2894
4036
|
fg: hoveredDismiss() === entry.id ? pal().warning : pal().muted
|
|
2895
4037
|
}, _$p));
|
|
2896
|
-
return _el$
|
|
4038
|
+
return _el$99;
|
|
2897
4039
|
}
|
|
2898
4040
|
}), null);
|
|
2899
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4041
|
+
_$effect((_$p) => _$setProp(_el$96, "style", {
|
|
2900
4042
|
fg: pal().muted
|
|
2901
4043
|
}, _$p));
|
|
2902
|
-
return _el$
|
|
4044
|
+
return _el$92;
|
|
2903
4045
|
})();
|
|
2904
4046
|
})();
|
|
2905
4047
|
}
|
|
@@ -2920,13 +4062,13 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2920
4062
|
const right = props.sortOrder() === "desc" ? `\u2191 ${t("scroll.top")}` : `\u2193 ${t("scroll.bottom")}`;
|
|
2921
4063
|
const pad = showTop ? Math.max(1, panelWidth() - gutter() - visualWidth(left) - visualWidth(right)) : 0;
|
|
2922
4064
|
return (() => {
|
|
2923
|
-
var _el$
|
|
2924
|
-
_$insertNode(_el$100, _el$101);
|
|
2925
|
-
_$setProp(_el$100, "flexDirection", "row");
|
|
4065
|
+
var _el$101 = _$createElement("box"), _el$102 = _$createElement("text"), _el$103 = _$createElement("span");
|
|
2926
4066
|
_$insertNode(_el$101, _el$102);
|
|
2927
|
-
_$setProp(_el$101, "
|
|
2928
|
-
_$
|
|
2929
|
-
_$setProp(_el$
|
|
4067
|
+
_$setProp(_el$101, "flexDirection", "row");
|
|
4068
|
+
_$insertNode(_el$102, _el$103);
|
|
4069
|
+
_$setProp(_el$102, "onMouseOver", () => showMore && setHoveredMoreBelow(true));
|
|
4070
|
+
_$setProp(_el$102, "onMouseOut", () => setHoveredMoreBelow(false));
|
|
4071
|
+
_$setProp(_el$102, "onMouseUp", () => {
|
|
2930
4072
|
if (!showMore) return;
|
|
2931
4073
|
const total = entryList().length;
|
|
2932
4074
|
const m = max();
|
|
@@ -2938,20 +4080,20 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2938
4080
|
}
|
|
2939
4081
|
setHoveredMoreBelow(false);
|
|
2940
4082
|
});
|
|
2941
|
-
_$insert(_el$
|
|
2942
|
-
_$insert(_el$
|
|
2943
|
-
var _el$
|
|
2944
|
-
_$insert(_el$
|
|
2945
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4083
|
+
_$insert(_el$103, left);
|
|
4084
|
+
_$insert(_el$101, showTop ? [(() => {
|
|
4085
|
+
var _el$104 = _$createElement("text");
|
|
4086
|
+
_$insert(_el$104, () => " ".repeat(pad));
|
|
4087
|
+
_$effect((_$p) => _$setProp(_el$104, "style", {
|
|
2946
4088
|
fg: pal().muted
|
|
2947
4089
|
}, _$p));
|
|
2948
|
-
return _el$
|
|
4090
|
+
return _el$104;
|
|
2949
4091
|
})(), (() => {
|
|
2950
|
-
var _el$
|
|
2951
|
-
_$insertNode(_el$
|
|
2952
|
-
_$setProp(_el$
|
|
2953
|
-
_$setProp(_el$
|
|
2954
|
-
_$setProp(_el$
|
|
4092
|
+
var _el$105 = _$createElement("text"), _el$106 = _$createElement("span");
|
|
4093
|
+
_$insertNode(_el$105, _el$106);
|
|
4094
|
+
_$setProp(_el$105, "onMouseOver", () => setHoveredTop(true));
|
|
4095
|
+
_$setProp(_el$105, "onMouseOut", () => setHoveredTop(false));
|
|
4096
|
+
_$setProp(_el$105, "onMouseUp", () => {
|
|
2955
4097
|
const total = entryList().length;
|
|
2956
4098
|
const m = max();
|
|
2957
4099
|
if (props.sortOrder() === "desc") {
|
|
@@ -2961,16 +4103,16 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2961
4103
|
}
|
|
2962
4104
|
setHoveredTop(false);
|
|
2963
4105
|
});
|
|
2964
|
-
_$insert(_el$
|
|
2965
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4106
|
+
_$insert(_el$106, right);
|
|
4107
|
+
_$effect((_$p) => _$setProp(_el$106, "style", {
|
|
2966
4108
|
fg: hoveredTop() ? pal().warning : pal().muted
|
|
2967
4109
|
}, _$p));
|
|
2968
|
-
return _el$
|
|
4110
|
+
return _el$105;
|
|
2969
4111
|
})()] : null, null);
|
|
2970
|
-
_$effect((_$p) => _$setProp(_el$
|
|
4112
|
+
_$effect((_$p) => _$setProp(_el$103, "style", {
|
|
2971
4113
|
fg: showMore && hoveredMoreBelow() ? pal().warning : pal().muted
|
|
2972
4114
|
}, _$p));
|
|
2973
|
-
return _el$
|
|
4115
|
+
return _el$101;
|
|
2974
4116
|
})();
|
|
2975
4117
|
})();
|
|
2976
4118
|
}
|
|
@@ -2998,11 +4140,14 @@ ${t("session.toast.copy_failed")}`, {
|
|
|
2998
4140
|
}
|
|
2999
4141
|
|
|
3000
4142
|
// src/v2/index.tsx
|
|
3001
|
-
function
|
|
4143
|
+
function CommandRoot(props) {
|
|
3002
4144
|
props.context.keymap.layer(() => ({
|
|
3003
4145
|
mode: "global",
|
|
3004
4146
|
commands: makeCommands(props.context, props.api, props.signals)
|
|
3005
4147
|
}));
|
|
4148
|
+
return null;
|
|
4149
|
+
}
|
|
4150
|
+
function PluginRoot(props) {
|
|
3006
4151
|
return _$createComponent2(SubAgentPanel, {
|
|
3007
4152
|
get api() {
|
|
3008
4153
|
return props.api;
|
|
@@ -3025,6 +4170,18 @@ function PluginRoot(props) {
|
|
|
3025
4170
|
get borderVisible() {
|
|
3026
4171
|
return props.signals.borderVisible;
|
|
3027
4172
|
},
|
|
4173
|
+
get showEntryCost() {
|
|
4174
|
+
return props.signals.showEntryCost;
|
|
4175
|
+
},
|
|
4176
|
+
get showEntryTime() {
|
|
4177
|
+
return props.signals.showEntryTime;
|
|
4178
|
+
},
|
|
4179
|
+
get showEntryTokens() {
|
|
4180
|
+
return props.signals.showEntryTokens;
|
|
4181
|
+
},
|
|
4182
|
+
get timeFormat() {
|
|
4183
|
+
return props.signals.timeFormat;
|
|
4184
|
+
},
|
|
3028
4185
|
get sessionId() {
|
|
3029
4186
|
return props.sessionID;
|
|
3030
4187
|
}
|
|
@@ -3050,6 +4207,12 @@ var mod = {
|
|
|
3050
4207
|
const [orderSignal, setSortOrder] = createSignal3(String(api.kv.get(SETTING_KEYS.order, "desc")) === "asc" ? "asc" : "desc");
|
|
3051
4208
|
const [scrollSignal, setScrollMode] = createSignal3(String(api.kv.get(SETTING_KEYS.scrollMode, "wheel")) === "click" ? "click" : "wheel");
|
|
3052
4209
|
const [borderSignal, setBorderVisible] = createSignal3(api.kv.get(SETTING_KEYS.border, false) === true);
|
|
4210
|
+
const [showEntryCostSignal, setShowEntryCost] = createSignal3(api.kv.get(SETTING_KEYS.showEntryCost, false) === true);
|
|
4211
|
+
const [showEntryTimeSignal, setShowEntryTime] = createSignal3(api.kv.get(SETTING_KEYS.showEntryTime, true) !== false);
|
|
4212
|
+
const [showEntryTokensSignal, setShowEntryTokens] = createSignal3(api.kv.get(SETTING_KEYS.showEntryTokens, true) !== false);
|
|
4213
|
+
const storedTimeFormat = String(api.kv.get(SETTING_KEYS.timeFormat, "short"));
|
|
4214
|
+
const [timeFormatSignal, setTimeFormat] = createSignal3(TIME_FORMATS.includes(storedTimeFormat) ? storedTimeFormat : "short");
|
|
4215
|
+
const [dbSyncSignal, setDbSync] = createSignal3(api.kv.get(SETTING_KEYS.dbSync, true) !== false);
|
|
3053
4216
|
lang = langSignal;
|
|
3054
4217
|
maxEntries = maxSignal;
|
|
3055
4218
|
sortOrder = orderSignal;
|
|
@@ -3065,8 +4228,26 @@ var mod = {
|
|
|
3065
4228
|
setScrollMode,
|
|
3066
4229
|
borderVisible: borderSignal,
|
|
3067
4230
|
setBorderVisible,
|
|
4231
|
+
showEntryCost: showEntryCostSignal,
|
|
4232
|
+
setShowEntryCost,
|
|
4233
|
+
showEntryTime: showEntryTimeSignal,
|
|
4234
|
+
setShowEntryTime,
|
|
4235
|
+
showEntryTokens: showEntryTokensSignal,
|
|
4236
|
+
setShowEntryTokens,
|
|
4237
|
+
timeFormat: timeFormatSignal,
|
|
4238
|
+
setTimeFormat,
|
|
4239
|
+
dbSync: dbSyncSignal,
|
|
4240
|
+
setDbSync,
|
|
3068
4241
|
sessionId: ""
|
|
3069
4242
|
};
|
|
4243
|
+
context.ui.slot({
|
|
4244
|
+
append: "app",
|
|
4245
|
+
render: () => _$createComponent2(CommandRoot, {
|
|
4246
|
+
context,
|
|
4247
|
+
api,
|
|
4248
|
+
signals
|
|
4249
|
+
})
|
|
4250
|
+
});
|
|
3070
4251
|
context.ui.slot({
|
|
3071
4252
|
prepend: "sidebar.content",
|
|
3072
4253
|
render: (props) => {
|