opencode-subagent-magazine 1.6.0-beta.2 → 1.6.0-beta.4
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 +3 -1
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/kv.d.ts +1 -0
- package/dist/core/kv.js +1 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +8 -0
- package/dist/index.js +17 -2
- package/dist/panel/SubAgentPanel.d.ts +1 -0
- package/dist/panel/SubAgentPanel.js +54 -37
- package/dist/panel/entry-map.d.ts +6 -0
- package/dist/panel/entry-map.js +25 -0
- package/dist/tui.js +128 -56
- package/dist/v2/commands.js +13 -0
- package/dist/v2/index.js +3 -2
- package/dist/v2.js +124 -56
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/core/color.ts +70 -70
- package/src/core/format.ts +61 -61
- package/src/core/index.ts +6 -6
- package/src/core/kv.ts +37 -36
- package/src/core/state-machine.ts +46 -46
- package/src/core/types.ts +2 -0
- package/src/core/usage.ts +16 -16
- package/src/i18n.ts +8 -0
- package/src/index.tsx +472 -454
- package/src/panel/SubAgentPanel.tsx +59 -37
- package/src/panel/entry-map.ts +39 -0
- package/src/panel/store.ts +8 -8
- package/src/v2/commands.ts +264 -251
- package/src/v2/index.tsx +102 -98
- package/src/v2/theme.ts +14 -14
- package/src/v2/types.ts +165 -165
- package/tui/index.js +11 -11
package/src/core/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from "./types"
|
|
2
|
-
export * from "./format"
|
|
3
|
-
export * from "./color"
|
|
4
|
-
export * from "./kv"
|
|
5
|
-
export * from "./usage"
|
|
6
|
-
export * from "./state-machine"
|
|
1
|
+
export * from "./types"
|
|
2
|
+
export * from "./format"
|
|
3
|
+
export * from "./color"
|
|
4
|
+
export * from "./kv"
|
|
5
|
+
export * from "./usage"
|
|
6
|
+
export * from "./state-machine"
|
package/src/core/kv.ts
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
import type { SessionRecord } from "./types"
|
|
2
|
-
|
|
3
|
-
export const KV_PREFIX = "subagent_magazine"
|
|
4
|
-
export const SESSION_DATA_KEY = `${KV_PREFIX}.session_data`
|
|
5
|
-
|
|
6
|
-
/** Minimal KV surface used by the magazine's persistence layer. */
|
|
7
|
-
export interface KVApi {
|
|
8
|
-
get(key: string, fallback?: unknown): unknown
|
|
9
|
-
set(key: string, value: unknown): void
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** Setting keys shared by V1/V2 (values are raw strings in KV). */
|
|
13
|
-
export const SETTING_KEYS = {
|
|
14
|
-
lang: `${KV_PREFIX}.lang`,
|
|
15
|
-
maxEntries: `${KV_PREFIX}.max_entries`,
|
|
16
|
-
order: `${KV_PREFIX}.order`,
|
|
17
|
-
scrollMode: `${KV_PREFIX}.scroll_mode`,
|
|
18
|
-
open: `${KV_PREFIX}.open`,
|
|
19
|
-
ttlDays: `${KV_PREFIX}.ttl_days`,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import type { SessionRecord } from "./types"
|
|
2
|
+
|
|
3
|
+
export const KV_PREFIX = "subagent_magazine"
|
|
4
|
+
export const SESSION_DATA_KEY = `${KV_PREFIX}.session_data`
|
|
5
|
+
|
|
6
|
+
/** Minimal KV surface used by the magazine's persistence layer. */
|
|
7
|
+
export interface KVApi {
|
|
8
|
+
get(key: string, fallback?: unknown): unknown
|
|
9
|
+
set(key: string, value: unknown): void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Setting keys shared by V1/V2 (values are raw strings in KV). */
|
|
13
|
+
export const SETTING_KEYS = {
|
|
14
|
+
lang: `${KV_PREFIX}.lang`,
|
|
15
|
+
maxEntries: `${KV_PREFIX}.max_entries`,
|
|
16
|
+
order: `${KV_PREFIX}.order`,
|
|
17
|
+
scrollMode: `${KV_PREFIX}.scroll_mode`,
|
|
18
|
+
open: `${KV_PREFIX}.open`,
|
|
19
|
+
ttlDays: `${KV_PREFIX}.ttl_days`,
|
|
20
|
+
border: `${KV_PREFIX}.border`,
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
export function loadSessionData(kv: KVApi): Record<string, SessionRecord> {
|
|
24
|
+
try {
|
|
25
|
+
const raw = kv.get(SESSION_DATA_KEY, "{}")
|
|
26
|
+
return JSON.parse(String(raw))
|
|
27
|
+
} catch { return {} }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function saveSessionData(kv: KVApi, data: Record<string, SessionRecord>) {
|
|
31
|
+
try { kv.set(SESSION_DATA_KEY, JSON.stringify(data)) } catch {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function readTTLDays(kv: KVApi): number {
|
|
35
|
+
const ttlDaysRaw = parseInt(String(kv.get(SETTING_KEYS.ttlDays, "3")), 10)
|
|
36
|
+
return Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw
|
|
37
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import type { SubEntry, SubStatus } from "./types"
|
|
2
|
-
import type { TodoStats } from "./usage"
|
|
3
|
-
|
|
4
|
-
/** Final status decision when a child session settles (V1 `session.idle` semantics). */
|
|
5
|
-
export function settleOnIdle(entry: SubEntry): SubStatus {
|
|
6
|
-
if (entry.status === "cancel_requested" && entry.abortAccepted) return "cancelled"
|
|
7
|
-
return "done"
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Agent-name normalization used by the fallback matching chain. */
|
|
11
|
-
export function normalizeAgent(s: string): string {
|
|
12
|
-
return s.toLowerCase().replace(/[^a-z0-9-]/g, "")
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Snapshot of usage values captured when a child session settles. */
|
|
16
|
-
export interface SettleSnapshot {
|
|
17
|
-
tokens?: number
|
|
18
|
-
cost?: number
|
|
19
|
-
model?: string
|
|
20
|
-
todo?: TodoStats
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Apply a settled status + usage backfill onto an entry (no-op preserving already-settled). */
|
|
24
|
-
export function settleEntry(
|
|
25
|
-
entry: SubEntry,
|
|
26
|
-
targetStatus: SubStatus,
|
|
27
|
-
nowTs: number,
|
|
28
|
-
snap: SettleSnapshot,
|
|
29
|
-
errorMsg?: string,
|
|
30
|
-
): SubEntry {
|
|
31
|
-
const alreadySettled = entry.status !== "running" && entry.status !== "cancel_requested"
|
|
32
|
-
const finalStatus = targetStatus === "error" ? "error" : settleOnIdle(entry)
|
|
33
|
-
return {
|
|
34
|
-
...entry,
|
|
35
|
-
...(alreadySettled ? {} : { status: finalStatus, endedAt: nowTs }),
|
|
36
|
-
tokens: entry.tokens ?? snap.tokens,
|
|
37
|
-
cost: entry.cost ?? snap.cost,
|
|
38
|
-
model: entry.model ?? snap.model,
|
|
39
|
-
todoTotal: entry.todoTotal ?? snap.todo?.total,
|
|
40
|
-
todoDone: entry.todoDone ?? snap.todo?.done,
|
|
41
|
-
error: errorMsg || entry.error,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Time-based scan heuristic: how old must a running entry be before assuming completion. */
|
|
46
|
-
export const STALE_RUNNING_MS = 30 * 60 * 1000
|
|
1
|
+
import type { SubEntry, SubStatus } from "./types"
|
|
2
|
+
import type { TodoStats } from "./usage"
|
|
3
|
+
|
|
4
|
+
/** Final status decision when a child session settles (V1 `session.idle` semantics). */
|
|
5
|
+
export function settleOnIdle(entry: SubEntry): SubStatus {
|
|
6
|
+
if (entry.status === "cancel_requested" && entry.abortAccepted) return "cancelled"
|
|
7
|
+
return "done"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Agent-name normalization used by the fallback matching chain. */
|
|
11
|
+
export function normalizeAgent(s: string): string {
|
|
12
|
+
return s.toLowerCase().replace(/[^a-z0-9-]/g, "")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Snapshot of usage values captured when a child session settles. */
|
|
16
|
+
export interface SettleSnapshot {
|
|
17
|
+
tokens?: number
|
|
18
|
+
cost?: number
|
|
19
|
+
model?: string
|
|
20
|
+
todo?: TodoStats
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Apply a settled status + usage backfill onto an entry (no-op preserving already-settled). */
|
|
24
|
+
export function settleEntry(
|
|
25
|
+
entry: SubEntry,
|
|
26
|
+
targetStatus: SubStatus,
|
|
27
|
+
nowTs: number,
|
|
28
|
+
snap: SettleSnapshot,
|
|
29
|
+
errorMsg?: string,
|
|
30
|
+
): SubEntry {
|
|
31
|
+
const alreadySettled = entry.status !== "running" && entry.status !== "cancel_requested"
|
|
32
|
+
const finalStatus = targetStatus === "error" ? "error" : settleOnIdle(entry)
|
|
33
|
+
return {
|
|
34
|
+
...entry,
|
|
35
|
+
...(alreadySettled ? {} : { status: finalStatus, endedAt: nowTs }),
|
|
36
|
+
tokens: entry.tokens ?? snap.tokens,
|
|
37
|
+
cost: entry.cost ?? snap.cost,
|
|
38
|
+
model: entry.model ?? snap.model,
|
|
39
|
+
todoTotal: entry.todoTotal ?? snap.todo?.total,
|
|
40
|
+
todoDone: entry.todoDone ?? snap.todo?.done,
|
|
41
|
+
error: errorMsg || entry.error,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Time-based scan heuristic: how old must a running entry be before assuming completion. */
|
|
46
|
+
export const STALE_RUNNING_MS = 30 * 60 * 1000
|
package/src/core/types.ts
CHANGED
package/src/core/usage.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export interface TodoStats {
|
|
2
|
-
total: number
|
|
3
|
-
done: number
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Data-source abstraction for per-session usage reads.
|
|
8
|
-
* V1 and V2 provide their own implementations backed by their respective
|
|
9
|
-
* client APIs; the state machine consumes only this surface.
|
|
10
|
-
*/
|
|
11
|
-
export interface UsageReader {
|
|
12
|
-
readSessionTokens(sid: string): number | undefined
|
|
13
|
-
readSessionCost(sid: string): number | undefined
|
|
14
|
-
readSessionModel(sid: string): string | undefined
|
|
15
|
-
readSessionTodo(sid: string): TodoStats | undefined
|
|
16
|
-
}
|
|
1
|
+
export interface TodoStats {
|
|
2
|
+
total: number
|
|
3
|
+
done: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Data-source abstraction for per-session usage reads.
|
|
8
|
+
* V1 and V2 provide their own implementations backed by their respective
|
|
9
|
+
* client APIs; the state machine consumes only this surface.
|
|
10
|
+
*/
|
|
11
|
+
export interface UsageReader {
|
|
12
|
+
readSessionTokens(sid: string): number | undefined
|
|
13
|
+
readSessionCost(sid: string): number | undefined
|
|
14
|
+
readSessionModel(sid: string): string | undefined
|
|
15
|
+
readSessionTodo(sid: string): TodoStats | undefined
|
|
16
|
+
}
|
package/src/i18n.ts
CHANGED
|
@@ -56,6 +56,8 @@ const ZH_T = {
|
|
|
56
56
|
"cancel.status_error": "无法查询会话状态",
|
|
57
57
|
"cancel.sent": "已发送取消指令",
|
|
58
58
|
"cancel.failed": "取消失败",
|
|
59
|
+
"borderShown": "面板边框 已显示",
|
|
60
|
+
"borderHidden": "面板边框 已隐藏",
|
|
59
61
|
} as const
|
|
60
62
|
|
|
61
63
|
/** 结构约束:值放宽为 string,键集合来自中文表(新增语言缺 key 会编译报错)。 */
|
|
@@ -111,6 +113,8 @@ const EN_T: Translation = {
|
|
|
111
113
|
"cancel.status_error": "Cannot query session status",
|
|
112
114
|
"cancel.sent": "Cancel instruction sent",
|
|
113
115
|
"cancel.failed": "Cancellation failed",
|
|
116
|
+
"borderShown": "Panel border shown",
|
|
117
|
+
"borderHidden": "Panel border hidden",
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
const JA_T: Translation = {
|
|
@@ -163,6 +167,8 @@ const JA_T: Translation = {
|
|
|
163
167
|
"cancel.status_error": "セッション状態を照会できません",
|
|
164
168
|
"cancel.sent": "キャンセル指示を送信しました",
|
|
165
169
|
"cancel.failed": "キャンセルに失敗しました",
|
|
170
|
+
"borderShown": "パネル枠線 表示",
|
|
171
|
+
"borderHidden": "パネル枠線 非表示",
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
const KO_T: Translation = {
|
|
@@ -215,6 +221,8 @@ const KO_T: Translation = {
|
|
|
215
221
|
"cancel.status_error": "세션 상태를 조회할 수 없습니다",
|
|
216
222
|
"cancel.sent": "취소 지시를 보냈습니다",
|
|
217
223
|
"cancel.failed": "취소에 실패했습니다",
|
|
224
|
+
"borderShown": "패널 테두리 표시",
|
|
225
|
+
"borderHidden": "패널 테두리 숨김",
|
|
218
226
|
}
|
|
219
227
|
|
|
220
228
|
export const LANGS: Record<LangCode, Translation> = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T }
|