opencode-subagent-magazine 1.6.0-beta.0 → 1.6.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/types.js +1 -1
- package/dist/panel/SubAgentPanel.js +22 -6
- package/dist/panel/panel-api.d.ts +3 -0
- package/dist/panel/session-routing.d.ts +3 -0
- package/dist/panel/session-routing.js +4 -0
- package/dist/tui.js +24 -8
- package/dist/v2/v2-panel-api.js +11 -4
- package/dist/v2.js +31 -11
- package/package.json +2 -1
- package/src/_version.ts +1 -1
- package/src/core/types.ts +58 -58
- package/src/panel/SubAgentPanel.tsx +18 -6
- package/src/panel/panel-api.ts +75 -72
- package/src/panel/session-routing.ts +10 -0
- package/src/v2/v2-panel-api.ts +307 -301
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "1.6.0-beta.
|
|
1
|
+
export declare const PLUGIN_VERSION = "1.6.0-beta.2";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION = "1.6.0-beta.
|
|
2
|
+
export const PLUGIN_VERSION = "1.6.0-beta.2";
|
package/dist/core/types.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** OpenCode built-in tool names that spawn sub-agents or delegate tasks. */
|
|
2
|
-
export const SUBAGENT_TOOLS = new Set(["task", "delegate", "call_omo_agent"]);
|
|
2
|
+
export const SUBAGENT_TOOLS = new Set(["task", "subagent", "delegate", "call_omo_agent"]);
|
|
@@ -8,6 +8,7 @@ import { visualWidth, truncate, fmtDurationShort, fmtTokens, safeErrorMsg } from
|
|
|
8
8
|
import { rgb, desaturateTo, dimColor, FALLBACK, MAX_SAT } from "../core/color";
|
|
9
9
|
import { KV_PREFIX } from "../core/kv";
|
|
10
10
|
import { globalEntryCache, clearTick } from "./store";
|
|
11
|
+
import { isDirectChildSession } from "./session-routing";
|
|
11
12
|
/** Entry line left prefix: icon + space + status dot + space */
|
|
12
13
|
const LEFT_PAD = 4;
|
|
13
14
|
/** Detail row indent: two spaces */
|
|
@@ -334,6 +335,11 @@ export function SubAgentPanel(props) {
|
|
|
334
335
|
const part = event.payload?.part;
|
|
335
336
|
if (!part)
|
|
336
337
|
return;
|
|
338
|
+
// V2 事件流是全局的——若 payload 带发起会话 ID,则只归账到正在查看的会话,
|
|
339
|
+
// 避免其他会话的子代理写进当前侧边栏;V1 宿主已按会话 scope,不传 sessionID。
|
|
340
|
+
const eventSid = event.payload?.sessionID !== undefined ? String(event.payload.sessionID) : undefined;
|
|
341
|
+
if (event.scope === "global" && eventSid !== props.sessionId)
|
|
342
|
+
return;
|
|
337
343
|
// SubtaskPart
|
|
338
344
|
if (part.type === "subtask") {
|
|
339
345
|
const agent = String(part.agent ?? "?");
|
|
@@ -384,7 +390,7 @@ export function SubAgentPanel(props) {
|
|
|
384
390
|
if (hasChild)
|
|
385
391
|
status = "running";
|
|
386
392
|
}
|
|
387
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
|
|
393
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? input?.category ?? tool);
|
|
388
394
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
389
395
|
const desc = input?.description !== undefined ? String(input.description) : "";
|
|
390
396
|
const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
|
|
@@ -403,6 +409,17 @@ export function SubAgentPanel(props) {
|
|
|
403
409
|
const sid = String(props_?.sessionID ?? "");
|
|
404
410
|
if (!sid)
|
|
405
411
|
return;
|
|
412
|
+
const isGlobalEvent = event.scope === "global";
|
|
413
|
+
let eventSession;
|
|
414
|
+
if (isGlobalEvent) {
|
|
415
|
+
try {
|
|
416
|
+
eventSession = props.api.session.get(sid);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
eventSession = undefined;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const isCurrentChild = !isGlobalEvent || isDirectChildSession(props.sessionId, sid, eventSession);
|
|
406
423
|
const sessionTokens = props.api.usage.readSessionTokens(sid);
|
|
407
424
|
const sessionCost = props.api.usage.readSessionCost(sid);
|
|
408
425
|
const sessionModel = props.api.usage.readSessionModel(sid);
|
|
@@ -496,7 +513,7 @@ export function SubAgentPanel(props) {
|
|
|
496
513
|
let changed = false;
|
|
497
514
|
const next = new Map(prev);
|
|
498
515
|
for (const [id, entry] of next) {
|
|
499
|
-
if (entry.sessionId !== sid)
|
|
516
|
+
if (!isCurrentChild || entry.sessionId !== sid)
|
|
500
517
|
continue;
|
|
501
518
|
if (entry.status !== "running" && entry.status !== "done" && entry.status !== "cancel_requested")
|
|
502
519
|
continue;
|
|
@@ -518,7 +535,7 @@ export function SubAgentPanel(props) {
|
|
|
518
535
|
});
|
|
519
536
|
changed = true;
|
|
520
537
|
}
|
|
521
|
-
if (!changed && sessionAgent) {
|
|
538
|
+
if (!changed && sessionAgent && isCurrentChild) {
|
|
522
539
|
const nowTs = Date.now();
|
|
523
540
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
|
|
524
541
|
const saNorm = normalize(sessionAgent);
|
|
@@ -567,8 +584,7 @@ export function SubAgentPanel(props) {
|
|
|
567
584
|
// 当子代理所属的父 session 与当前视图不同时,通过模块级缓存定位
|
|
568
585
|
// 并更新父 session 的 entry 状态,随后写回 KV。
|
|
569
586
|
try {
|
|
570
|
-
const
|
|
571
|
-
const parentSid = sessionObj?.parentID;
|
|
587
|
+
const parentSid = (isGlobalEvent ? eventSession : props.api.session.get(sid))?.parentID;
|
|
572
588
|
if (parentSid && parentSid !== props.sessionId) {
|
|
573
589
|
// 优先从模块级缓存获取父 session 的 entries,不受当前视图切换影响
|
|
574
590
|
const parentCache = globalEntryCache.get(parentSid);
|
|
@@ -813,7 +829,7 @@ export function SubAgentPanel(props) {
|
|
|
813
829
|
// If already tracked as running but tool state says completed/error → update
|
|
814
830
|
// If not tracked → add fresh
|
|
815
831
|
const input = st?.input;
|
|
816
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? tool);
|
|
832
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? tool);
|
|
817
833
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
818
834
|
const desc = input?.description !== undefined ? String(input.description) : "";
|
|
819
835
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
|
@@ -16,8 +16,11 @@ export interface SessionStatusLike {
|
|
|
16
16
|
* into these shapes so the panel component never touches host APIs directly.
|
|
17
17
|
*/
|
|
18
18
|
export type PanelEventType = "part.updated" | "message.updated" | "session.idle" | "session.error";
|
|
19
|
+
export type PanelEventScope = "session" | "global";
|
|
19
20
|
export interface PanelEvent {
|
|
20
21
|
type: PanelEventType;
|
|
22
|
+
/** Global V2 events need explicit session ownership checks. */
|
|
23
|
+
scope?: PanelEventScope;
|
|
21
24
|
/** part payload for part.updated; session properties for idle/error. */
|
|
22
25
|
payload?: Record<string, unknown>;
|
|
23
26
|
}
|
package/dist/tui.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
|
6
6
|
import { createSignal as createSignal3 } from "solid-js";
|
|
7
7
|
|
|
8
8
|
// src/_version.ts
|
|
9
|
-
var PLUGIN_VERSION = "1.6.0-beta.
|
|
9
|
+
var PLUGIN_VERSION = "1.6.0-beta.2";
|
|
10
10
|
|
|
11
11
|
// src/i18n.ts
|
|
12
12
|
var ZH_T = {
|
|
@@ -363,7 +363,7 @@ async function copyText(text) {
|
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
// src/core/types.ts
|
|
366
|
-
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "delegate", "call_omo_agent"]);
|
|
366
|
+
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "subagent", "delegate", "call_omo_agent"]);
|
|
367
367
|
|
|
368
368
|
// src/core/format.ts
|
|
369
369
|
function charColumns(c) {
|
|
@@ -485,6 +485,11 @@ import { createSignal } from "solid-js";
|
|
|
485
485
|
var globalEntryCache = /* @__PURE__ */ new Map();
|
|
486
486
|
var [clearTick, setClearTick] = createSignal(0);
|
|
487
487
|
|
|
488
|
+
// src/panel/session-routing.ts
|
|
489
|
+
function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
490
|
+
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
491
|
+
}
|
|
492
|
+
|
|
488
493
|
// src/panel/SubAgentPanel.tsx
|
|
489
494
|
var LEFT_PAD = 4;
|
|
490
495
|
var INDENT = 2;
|
|
@@ -901,6 +906,8 @@ function SubAgentPanel(props) {
|
|
|
901
906
|
const handlePartUpdated = (event) => {
|
|
902
907
|
const part = event.payload?.part;
|
|
903
908
|
if (!part) return;
|
|
909
|
+
const eventSid = event.payload?.sessionID !== void 0 ? String(event.payload.sessionID) : void 0;
|
|
910
|
+
if (event.scope === "global" && eventSid !== props.sessionId) return;
|
|
904
911
|
if (part.type === "subtask") {
|
|
905
912
|
const agent = String(part.agent ?? "?");
|
|
906
913
|
const prompt = String(part.prompt ?? "");
|
|
@@ -949,7 +956,7 @@ function SubAgentPanel(props) {
|
|
|
949
956
|
const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
|
|
950
957
|
if (hasChild) status = "running";
|
|
951
958
|
}
|
|
952
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
|
|
959
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? input?.category ?? tool);
|
|
953
960
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
954
961
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
955
962
|
const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
|
|
@@ -970,6 +977,16 @@ function SubAgentPanel(props) {
|
|
|
970
977
|
const props_ = event.payload;
|
|
971
978
|
const sid = String(props_?.sessionID ?? "");
|
|
972
979
|
if (!sid) return;
|
|
980
|
+
const isGlobalEvent = event.scope === "global";
|
|
981
|
+
let eventSession;
|
|
982
|
+
if (isGlobalEvent) {
|
|
983
|
+
try {
|
|
984
|
+
eventSession = props.api.session.get(sid);
|
|
985
|
+
} catch {
|
|
986
|
+
eventSession = void 0;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const isCurrentChild = !isGlobalEvent || isDirectChildSession(props.sessionId, sid, eventSession);
|
|
973
990
|
const sessionTokens = props.api.usage.readSessionTokens(sid);
|
|
974
991
|
const sessionCost = props.api.usage.readSessionCost(sid);
|
|
975
992
|
const sessionModel = props.api.usage.readSessionModel(sid);
|
|
@@ -1058,7 +1075,7 @@ function SubAgentPanel(props) {
|
|
|
1058
1075
|
let changed = false;
|
|
1059
1076
|
const next = new Map(prev);
|
|
1060
1077
|
for (const [id, entry] of next) {
|
|
1061
|
-
if (entry.sessionId !== sid) continue;
|
|
1078
|
+
if (!isCurrentChild || entry.sessionId !== sid) continue;
|
|
1062
1079
|
if (entry.status !== "running" && entry.status !== "done" && entry.status !== "cancel_requested") continue;
|
|
1063
1080
|
if (sid === props.sessionId) continue;
|
|
1064
1081
|
const alreadySettled = entry.status !== "running" && entry.status !== "cancel_requested";
|
|
@@ -1078,7 +1095,7 @@ function SubAgentPanel(props) {
|
|
|
1078
1095
|
});
|
|
1079
1096
|
changed = true;
|
|
1080
1097
|
}
|
|
1081
|
-
if (!changed && sessionAgent) {
|
|
1098
|
+
if (!changed && sessionAgent && isCurrentChild) {
|
|
1082
1099
|
const nowTs = Date.now();
|
|
1083
1100
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
|
|
1084
1101
|
const saNorm = normalize(sessionAgent);
|
|
@@ -1123,8 +1140,7 @@ function SubAgentPanel(props) {
|
|
|
1123
1140
|
return changed ? next : prev;
|
|
1124
1141
|
});
|
|
1125
1142
|
try {
|
|
1126
|
-
const
|
|
1127
|
-
const parentSid = sessionObj?.parentID;
|
|
1143
|
+
const parentSid = (isGlobalEvent ? eventSession : props.api.session.get(sid))?.parentID;
|
|
1128
1144
|
if (parentSid && parentSid !== props.sessionId) {
|
|
1129
1145
|
const parentCache = globalEntryCache.get(parentSid);
|
|
1130
1146
|
const nowTs = Date.now();
|
|
@@ -1349,7 +1365,7 @@ function SubAgentPanel(props) {
|
|
|
1349
1365
|
}
|
|
1350
1366
|
}
|
|
1351
1367
|
const input = st?.input;
|
|
1352
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? tool);
|
|
1368
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? tool);
|
|
1353
1369
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
1354
1370
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1355
1371
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
package/dist/v2/v2-panel-api.js
CHANGED
|
@@ -289,8 +289,15 @@ export function createPanelApi(context, settings) {
|
|
|
289
289
|
for (const evt of ["session.tool.input.started", "session.tool.called", "session.tool.progress", "session.tool.success", "session.tool.failed"]) {
|
|
290
290
|
unsubs.push(context.data.on(evt, (e) => {
|
|
291
291
|
const part = toolEventToPart(e);
|
|
292
|
-
if (part)
|
|
293
|
-
|
|
292
|
+
if (!part)
|
|
293
|
+
return;
|
|
294
|
+
// V2 事件流是全局的(跨所有会话);把发起会话 ID 一并传给面板,
|
|
295
|
+
// 让面板只归账到正在查看的会话(V1 宿主已按会话 scope,无需该字段)。
|
|
296
|
+
const evt = e.data;
|
|
297
|
+
const sid = evt?.sessionID !== undefined ? String(evt.sessionID) : undefined;
|
|
298
|
+
if (!sid)
|
|
299
|
+
return;
|
|
300
|
+
cb({ type, scope: "global", payload: { part, sessionID: sid } });
|
|
294
301
|
}));
|
|
295
302
|
}
|
|
296
303
|
return () => { for (const u of unsubs)
|
|
@@ -312,7 +319,7 @@ export function createPanelApi(context, settings) {
|
|
|
312
319
|
unsubs.push(context.data.on(evt, (e) => {
|
|
313
320
|
const sid = String(e.data?.sessionID ?? "");
|
|
314
321
|
if (sid)
|
|
315
|
-
cb({ type, payload: { sessionID: sid } });
|
|
322
|
+
cb({ type, scope: "global", payload: { sessionID: sid } });
|
|
316
323
|
}));
|
|
317
324
|
}
|
|
318
325
|
return () => { for (const u of unsubs)
|
|
@@ -323,7 +330,7 @@ export function createPanelApi(context, settings) {
|
|
|
323
330
|
const evt = e.data;
|
|
324
331
|
const sid = String(evt?.sessionID ?? "");
|
|
325
332
|
if (sid)
|
|
326
|
-
cb({ type, payload: { sessionID: sid, error: evt?.error } });
|
|
333
|
+
cb({ type, scope: "global", payload: { sessionID: sid, error: evt?.error } });
|
|
327
334
|
});
|
|
328
335
|
default:
|
|
329
336
|
return () => { };
|
package/dist/v2.js
CHANGED
|
@@ -239,7 +239,11 @@ function createPanelApi(context, settings) {
|
|
|
239
239
|
for (const evt of ["session.tool.input.started", "session.tool.called", "session.tool.progress", "session.tool.success", "session.tool.failed"]) {
|
|
240
240
|
unsubs.push(context.data.on(evt, (e) => {
|
|
241
241
|
const part = toolEventToPart(e);
|
|
242
|
-
if (part)
|
|
242
|
+
if (!part) return;
|
|
243
|
+
const evt2 = e.data;
|
|
244
|
+
const sid = evt2?.sessionID !== void 0 ? String(evt2.sessionID) : void 0;
|
|
245
|
+
if (!sid) return;
|
|
246
|
+
cb({ type, scope: "global", payload: { part, sessionID: sid } });
|
|
243
247
|
}));
|
|
244
248
|
}
|
|
245
249
|
return () => {
|
|
@@ -260,7 +264,7 @@ function createPanelApi(context, settings) {
|
|
|
260
264
|
for (const evt of ["session.execution.succeeded", "session.execution.interrupted"]) {
|
|
261
265
|
unsubs.push(context.data.on(evt, (e) => {
|
|
262
266
|
const sid = String(e.data?.sessionID ?? "");
|
|
263
|
-
if (sid) cb({ type, payload: { sessionID: sid } });
|
|
267
|
+
if (sid) cb({ type, scope: "global", payload: { sessionID: sid } });
|
|
264
268
|
}));
|
|
265
269
|
}
|
|
266
270
|
return () => {
|
|
@@ -271,7 +275,7 @@ function createPanelApi(context, settings) {
|
|
|
271
275
|
return context.data.on("session.execution.failed", (e) => {
|
|
272
276
|
const evt = e.data;
|
|
273
277
|
const sid = String(evt?.sessionID ?? "");
|
|
274
|
-
if (sid) cb({ type, payload: { sessionID: sid, error: evt?.error } });
|
|
278
|
+
if (sid) cb({ type, scope: "global", payload: { sessionID: sid, error: evt?.error } });
|
|
275
279
|
});
|
|
276
280
|
default:
|
|
277
281
|
return () => {
|
|
@@ -325,7 +329,7 @@ function readTTLDays(kv) {
|
|
|
325
329
|
}
|
|
326
330
|
|
|
327
331
|
// src/_version.ts
|
|
328
|
-
var PLUGIN_VERSION = "1.6.0-beta.
|
|
332
|
+
var PLUGIN_VERSION = "1.6.0-beta.2";
|
|
329
333
|
|
|
330
334
|
// src/i18n.ts
|
|
331
335
|
var ZH_T = {
|
|
@@ -931,7 +935,7 @@ async function copyText(text) {
|
|
|
931
935
|
}
|
|
932
936
|
|
|
933
937
|
// src/core/types.ts
|
|
934
|
-
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "delegate", "call_omo_agent"]);
|
|
938
|
+
var SUBAGENT_TOOLS = /* @__PURE__ */ new Set(["task", "subagent", "delegate", "call_omo_agent"]);
|
|
935
939
|
|
|
936
940
|
// src/core/format.ts
|
|
937
941
|
function charColumns(c) {
|
|
@@ -1048,6 +1052,11 @@ var FALLBACK = {
|
|
|
1048
1052
|
};
|
|
1049
1053
|
var MAX_SAT = 0.28;
|
|
1050
1054
|
|
|
1055
|
+
// src/panel/session-routing.ts
|
|
1056
|
+
function isDirectChildSession(currentSessionId, eventSessionId, eventSession) {
|
|
1057
|
+
return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1051
1060
|
// src/panel/SubAgentPanel.tsx
|
|
1052
1061
|
var LEFT_PAD = 4;
|
|
1053
1062
|
var INDENT = 2;
|
|
@@ -1464,6 +1473,8 @@ function SubAgentPanel(props) {
|
|
|
1464
1473
|
const handlePartUpdated = (event) => {
|
|
1465
1474
|
const part = event.payload?.part;
|
|
1466
1475
|
if (!part) return;
|
|
1476
|
+
const eventSid = event.payload?.sessionID !== void 0 ? String(event.payload.sessionID) : void 0;
|
|
1477
|
+
if (event.scope === "global" && eventSid !== props.sessionId) return;
|
|
1467
1478
|
if (part.type === "subtask") {
|
|
1468
1479
|
const agent = String(part.agent ?? "?");
|
|
1469
1480
|
const prompt = String(part.prompt ?? "");
|
|
@@ -1512,7 +1523,7 @@ function SubAgentPanel(props) {
|
|
|
1512
1523
|
const hasChild = stMetaCheck?.session_id !== void 0 || stMetaCheck?.sessionId !== void 0;
|
|
1513
1524
|
if (hasChild) status = "running";
|
|
1514
1525
|
}
|
|
1515
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? input?.category ?? tool);
|
|
1526
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? input?.category ?? tool);
|
|
1516
1527
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
1517
1528
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1518
1529
|
const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40);
|
|
@@ -1533,6 +1544,16 @@ function SubAgentPanel(props) {
|
|
|
1533
1544
|
const props_ = event.payload;
|
|
1534
1545
|
const sid = String(props_?.sessionID ?? "");
|
|
1535
1546
|
if (!sid) return;
|
|
1547
|
+
const isGlobalEvent = event.scope === "global";
|
|
1548
|
+
let eventSession;
|
|
1549
|
+
if (isGlobalEvent) {
|
|
1550
|
+
try {
|
|
1551
|
+
eventSession = props.api.session.get(sid);
|
|
1552
|
+
} catch {
|
|
1553
|
+
eventSession = void 0;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
const isCurrentChild = !isGlobalEvent || isDirectChildSession(props.sessionId, sid, eventSession);
|
|
1536
1557
|
const sessionTokens = props.api.usage.readSessionTokens(sid);
|
|
1537
1558
|
const sessionCost = props.api.usage.readSessionCost(sid);
|
|
1538
1559
|
const sessionModel = props.api.usage.readSessionModel(sid);
|
|
@@ -1621,7 +1642,7 @@ function SubAgentPanel(props) {
|
|
|
1621
1642
|
let changed = false;
|
|
1622
1643
|
const next = new Map(prev);
|
|
1623
1644
|
for (const [id, entry] of next) {
|
|
1624
|
-
if (entry.sessionId !== sid) continue;
|
|
1645
|
+
if (!isCurrentChild || entry.sessionId !== sid) continue;
|
|
1625
1646
|
if (entry.status !== "running" && entry.status !== "done" && entry.status !== "cancel_requested") continue;
|
|
1626
1647
|
if (sid === props.sessionId) continue;
|
|
1627
1648
|
const alreadySettled = entry.status !== "running" && entry.status !== "cancel_requested";
|
|
@@ -1641,7 +1662,7 @@ function SubAgentPanel(props) {
|
|
|
1641
1662
|
});
|
|
1642
1663
|
changed = true;
|
|
1643
1664
|
}
|
|
1644
|
-
if (!changed && sessionAgent) {
|
|
1665
|
+
if (!changed && sessionAgent && isCurrentChild) {
|
|
1645
1666
|
const nowTs = Date.now();
|
|
1646
1667
|
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9-]/g, "");
|
|
1647
1668
|
const saNorm = normalize(sessionAgent);
|
|
@@ -1686,8 +1707,7 @@ function SubAgentPanel(props) {
|
|
|
1686
1707
|
return changed ? next : prev;
|
|
1687
1708
|
});
|
|
1688
1709
|
try {
|
|
1689
|
-
const
|
|
1690
|
-
const parentSid = sessionObj?.parentID;
|
|
1710
|
+
const parentSid = (isGlobalEvent ? eventSession : props.api.session.get(sid))?.parentID;
|
|
1691
1711
|
if (parentSid && parentSid !== props.sessionId) {
|
|
1692
1712
|
const parentCache = globalEntryCache.get(parentSid);
|
|
1693
1713
|
const nowTs = Date.now();
|
|
@@ -1912,7 +1932,7 @@ function SubAgentPanel(props) {
|
|
|
1912
1932
|
}
|
|
1913
1933
|
}
|
|
1914
1934
|
const input = st?.input;
|
|
1915
|
-
const agent = String(part.subagent_type ?? input?.subagent_type ?? tool);
|
|
1935
|
+
const agent = String(part.subagent_type ?? input?.agent ?? input?.subagent_type ?? tool);
|
|
1916
1936
|
const prompt = String(input?.prompt ?? part.description ?? "");
|
|
1917
1937
|
const desc = input?.description !== void 0 ? String(input.description) : "";
|
|
1918
1938
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-subagent-magazine",
|
|
3
|
-
"version": "1.6.0-beta.
|
|
3
|
+
"version": "1.6.0-beta.2",
|
|
4
4
|
"description": "OpenCode TUI plugin monitoring sub-agent invocation status in real time",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"build": "tsc && node build.tui.mjs",
|
|
31
31
|
"build:tui": "node build.tui.mjs",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "tsx --test tests/*.test.ts",
|
|
33
34
|
"version": "node -e \"require('fs').writeFileSync('src/_version.ts','// auto-generated\\nexport const PLUGIN_VERSION='+JSON.stringify(require('./package.json').version)+';\\n')\"",
|
|
34
35
|
"prepublishOnly": "tsc"
|
|
35
36
|
},
|
package/src/_version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION="1.6.0-beta.
|
|
2
|
+
export const PLUGIN_VERSION="1.6.0-beta.2";
|
package/src/core/types.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import type { LangCode } from "../i18n"
|
|
2
|
-
|
|
3
|
-
export type SubStatus = "running" | "done" | "error" | "cancel_requested" | "cancelled"
|
|
4
|
-
|
|
5
|
-
export interface SubEntry {
|
|
6
|
-
id: string
|
|
7
|
-
title: string
|
|
8
|
-
agent: string
|
|
9
|
-
prompt: string
|
|
10
|
-
error?: string
|
|
11
|
-
tokens?: number
|
|
12
|
-
cost?: number
|
|
13
|
-
status: SubStatus
|
|
14
|
-
sessionId?: string
|
|
15
|
-
startedAt: number
|
|
16
|
-
endedAt?: number
|
|
17
|
-
model?: string
|
|
18
|
-
todoTotal?: number
|
|
19
|
-
todoDone?: number
|
|
20
|
-
cancelRequestedAt?: number
|
|
21
|
-
abortAccepted?: boolean
|
|
22
|
-
cancelReason?: "manual"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export type Lang = LangCode
|
|
26
|
-
export type SortOrder = "desc" | "asc"
|
|
27
|
-
export type ScrollMode = "wheel" | "click"
|
|
28
|
-
|
|
29
|
-
/** OpenCode built-in tool names that spawn sub-agents or delegate tasks. */
|
|
30
|
-
export const SUBAGENT_TOOLS = new Set(["task", "delegate", "call_omo_agent"])
|
|
31
|
-
|
|
32
|
-
export interface ChildRecord {
|
|
33
|
-
scroll: number
|
|
34
|
-
expanded: string
|
|
35
|
-
entries: SubEntry[]
|
|
36
|
-
clearedIds?: string[]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface SessionRecord {
|
|
40
|
-
ts: number
|
|
41
|
-
entries: SubEntry[]
|
|
42
|
-
scroll: number
|
|
43
|
-
expanded: string
|
|
44
|
-
children: Record<string, ChildRecord>
|
|
45
|
-
clearedIds?: string[]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface SharedSignals {
|
|
49
|
-
lang: () => Lang
|
|
50
|
-
setLang: (l: Lang) => void
|
|
51
|
-
maxEntries: () => number
|
|
52
|
-
setMaxEntries: (n: number) => void
|
|
53
|
-
sortOrder: () => SortOrder
|
|
54
|
-
setSortOrder: (o: SortOrder) => void
|
|
55
|
-
scrollMode: () => ScrollMode
|
|
56
|
-
setScrollMode: (m: ScrollMode) => void
|
|
57
|
-
sessionId: string
|
|
58
|
-
}
|
|
1
|
+
import type { LangCode } from "../i18n"
|
|
2
|
+
|
|
3
|
+
export type SubStatus = "running" | "done" | "error" | "cancel_requested" | "cancelled"
|
|
4
|
+
|
|
5
|
+
export interface SubEntry {
|
|
6
|
+
id: string
|
|
7
|
+
title: string
|
|
8
|
+
agent: string
|
|
9
|
+
prompt: string
|
|
10
|
+
error?: string
|
|
11
|
+
tokens?: number
|
|
12
|
+
cost?: number
|
|
13
|
+
status: SubStatus
|
|
14
|
+
sessionId?: string
|
|
15
|
+
startedAt: number
|
|
16
|
+
endedAt?: number
|
|
17
|
+
model?: string
|
|
18
|
+
todoTotal?: number
|
|
19
|
+
todoDone?: number
|
|
20
|
+
cancelRequestedAt?: number
|
|
21
|
+
abortAccepted?: boolean
|
|
22
|
+
cancelReason?: "manual"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Lang = LangCode
|
|
26
|
+
export type SortOrder = "desc" | "asc"
|
|
27
|
+
export type ScrollMode = "wheel" | "click"
|
|
28
|
+
|
|
29
|
+
/** OpenCode built-in tool names that spawn sub-agents or delegate tasks. */
|
|
30
|
+
export const SUBAGENT_TOOLS = new Set(["task", "subagent", "delegate", "call_omo_agent"])
|
|
31
|
+
|
|
32
|
+
export interface ChildRecord {
|
|
33
|
+
scroll: number
|
|
34
|
+
expanded: string
|
|
35
|
+
entries: SubEntry[]
|
|
36
|
+
clearedIds?: string[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SessionRecord {
|
|
40
|
+
ts: number
|
|
41
|
+
entries: SubEntry[]
|
|
42
|
+
scroll: number
|
|
43
|
+
expanded: string
|
|
44
|
+
children: Record<string, ChildRecord>
|
|
45
|
+
clearedIds?: string[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SharedSignals {
|
|
49
|
+
lang: () => Lang
|
|
50
|
+
setLang: (l: Lang) => void
|
|
51
|
+
maxEntries: () => number
|
|
52
|
+
setMaxEntries: (n: number) => void
|
|
53
|
+
sortOrder: () => SortOrder
|
|
54
|
+
setSortOrder: (o: SortOrder) => void
|
|
55
|
+
scrollMode: () => ScrollMode
|
|
56
|
+
setScrollMode: (m: ScrollMode) => void
|
|
57
|
+
sessionId: string
|
|
58
|
+
}
|
|
@@ -21,6 +21,7 @@ import { rgb, desaturateTo, dimColor, FALLBACK, MAX_SAT } from "../core/color"
|
|
|
21
21
|
import { KV_PREFIX } from "../core/kv"
|
|
22
22
|
import type { PanelApi, PanelEvent } from "./panel-api"
|
|
23
23
|
import { globalEntryCache, clearTick } from "./store"
|
|
24
|
+
import { isDirectChildSession } from "./session-routing"
|
|
24
25
|
|
|
25
26
|
/** Entry line left prefix: icon + space + status dot + space */
|
|
26
27
|
const LEFT_PAD = 4
|
|
@@ -360,6 +361,11 @@ export function SubAgentPanel(props: {
|
|
|
360
361
|
const part = event.payload?.part as Record<string, unknown> | undefined
|
|
361
362
|
if (!part) return
|
|
362
363
|
|
|
364
|
+
// V2 事件流是全局的——若 payload 带发起会话 ID,则只归账到正在查看的会话,
|
|
365
|
+
// 避免其他会话的子代理写进当前侧边栏;V1 宿主已按会话 scope,不传 sessionID。
|
|
366
|
+
const eventSid = event.payload?.sessionID !== undefined ? String(event.payload.sessionID) : undefined
|
|
367
|
+
if (event.scope === "global" && eventSid !== props.sessionId) return
|
|
368
|
+
|
|
363
369
|
// SubtaskPart
|
|
364
370
|
if (part.type === "subtask") {
|
|
365
371
|
const agent = String(part.agent ?? "?")
|
|
@@ -411,7 +417,7 @@ export function SubAgentPanel(props: {
|
|
|
411
417
|
if (hasChild) status = "running"
|
|
412
418
|
}
|
|
413
419
|
|
|
414
|
-
const agent = String((part as any).subagent_type ?? input?.subagent_type ?? input?.category ?? tool)
|
|
420
|
+
const agent = String((part as any).subagent_type ?? input?.agent ?? input?.subagent_type ?? input?.category ?? tool)
|
|
415
421
|
const prompt = String(input?.prompt ?? (part as any).description ?? "")
|
|
416
422
|
const desc = input?.description !== undefined ? String(input.description) : ""
|
|
417
423
|
const title = desc || truncate(prompt.replace(/\n/g, " ").replace(/\s+/g, " ").trim(), 40)
|
|
@@ -432,6 +438,13 @@ export function SubAgentPanel(props: {
|
|
|
432
438
|
const sid = String(props_?.sessionID ?? "")
|
|
433
439
|
if (!sid) return
|
|
434
440
|
|
|
441
|
+
const isGlobalEvent = event.scope === "global"
|
|
442
|
+
let eventSession: ReturnType<PanelApi["session"]["get"]>
|
|
443
|
+
if (isGlobalEvent) {
|
|
444
|
+
try { eventSession = props.api.session.get(sid) } catch { eventSession = undefined }
|
|
445
|
+
}
|
|
446
|
+
const isCurrentChild = !isGlobalEvent || isDirectChildSession(props.sessionId, sid, eventSession)
|
|
447
|
+
|
|
435
448
|
const sessionTokens = props.api.usage.readSessionTokens(sid)
|
|
436
449
|
const sessionCost = props.api.usage.readSessionCost(sid)
|
|
437
450
|
const sessionModel = props.api.usage.readSessionModel(sid)
|
|
@@ -524,7 +537,7 @@ export function SubAgentPanel(props: {
|
|
|
524
537
|
let changed = false
|
|
525
538
|
const next = new Map(prev)
|
|
526
539
|
for (const [id, entry] of next) {
|
|
527
|
-
if (entry.sessionId !== sid) continue
|
|
540
|
+
if (!isCurrentChild || entry.sessionId !== sid) continue
|
|
528
541
|
if (entry.status !== "running" && entry.status !== "done" && entry.status !== "cancel_requested") continue
|
|
529
542
|
// Skip parent session idle — subagent entries belong to child sessions only
|
|
530
543
|
if (sid === props.sessionId) continue
|
|
@@ -543,7 +556,7 @@ export function SubAgentPanel(props: {
|
|
|
543
556
|
})
|
|
544
557
|
changed = true
|
|
545
558
|
}
|
|
546
|
-
if (!changed && sessionAgent) {
|
|
559
|
+
if (!changed && sessionAgent && isCurrentChild) {
|
|
547
560
|
const nowTs = Date.now()
|
|
548
561
|
const normalize = (s: string) => s.toLowerCase().replace(/[^a-z0-9-]/g, "")
|
|
549
562
|
const saNorm = normalize(sessionAgent)
|
|
@@ -589,8 +602,7 @@ export function SubAgentPanel(props: {
|
|
|
589
602
|
// 当子代理所属的父 session 与当前视图不同时,通过模块级缓存定位
|
|
590
603
|
// 并更新父 session 的 entry 状态,随后写回 KV。
|
|
591
604
|
try {
|
|
592
|
-
const
|
|
593
|
-
const parentSid = sessionObj?.parentID
|
|
605
|
+
const parentSid = (isGlobalEvent ? eventSession : props.api.session.get(sid))?.parentID
|
|
594
606
|
if (parentSid && parentSid !== props.sessionId) {
|
|
595
607
|
// 优先从模块级缓存获取父 session 的 entries,不受当前视图切换影响
|
|
596
608
|
const parentCache = globalEntryCache.get(parentSid)
|
|
@@ -829,7 +841,7 @@ export function SubAgentPanel(props: {
|
|
|
829
841
|
// If not tracked → add fresh
|
|
830
842
|
|
|
831
843
|
const input = st?.input as Record<string, unknown> | undefined
|
|
832
|
-
const agent = String((part as any).subagent_type ?? input?.subagent_type ?? tool)
|
|
844
|
+
const agent = String((part as any).subagent_type ?? input?.agent ?? input?.subagent_type ?? tool)
|
|
833
845
|
const prompt = String(input?.prompt ?? (part as any).description ?? "")
|
|
834
846
|
const desc = input?.description !== undefined ? String(input.description) : ""
|
|
835
847
|
const title = desc || truncate(prompt.replace(/\n/g, " ").trim(), 40)
|