opencode-subagent-magazine 1.6.0-beta.4 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,39 @@
1
- import type { SubEntry } from "../core/types"
2
-
3
- export type SubEntryPatch = Omit<SubEntry, "startedAt" | "endedAt"> & {
4
- startedAt?: number
5
- }
6
-
7
- export function findSubEntryKey(
8
- entries: ReadonlyMap<string, SubEntry>,
9
- id: string,
10
- sessionId?: string,
11
- ) {
12
- if (sessionId) {
13
- for (const [key, entry] of entries) {
14
- if (entry.sessionId === sessionId) return key
15
- }
16
- }
17
- return entries.has(id) ? id : undefined
18
- }
19
-
20
- export function upsertSubEntry(
21
- entries: ReadonlyMap<string, SubEntry>,
22
- partial: SubEntryPatch,
23
- now = Date.now(),
24
- ) {
25
- const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id
26
- const existing = entries.get(key)
27
- const next = new Map(entries)
28
- if (key !== partial.id) next.delete(partial.id)
29
-
30
- const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled"
31
- next.set(key, {
32
- ...(existing ?? { startedAt: now }),
33
- ...partial,
34
- id: existing?.id ?? partial.id,
35
- startedAt: existing?.startedAt || partial.startedAt || now,
36
- endedAt: ended ? (existing?.endedAt || now) : undefined,
37
- })
38
- return next
39
- }
1
+ import type { SubEntry } from "../core/types"
2
+
3
+ export type SubEntryPatch = Omit<SubEntry, "startedAt" | "endedAt"> & {
4
+ startedAt?: number
5
+ }
6
+
7
+ export function findSubEntryKey(
8
+ entries: ReadonlyMap<string, SubEntry>,
9
+ id: string,
10
+ sessionId?: string,
11
+ ) {
12
+ if (sessionId) {
13
+ for (const [key, entry] of entries) {
14
+ if (entry.sessionId === sessionId) return key
15
+ }
16
+ }
17
+ return entries.has(id) ? id : undefined
18
+ }
19
+
20
+ export function upsertSubEntry(
21
+ entries: ReadonlyMap<string, SubEntry>,
22
+ partial: SubEntryPatch,
23
+ now = Date.now(),
24
+ ) {
25
+ const key = findSubEntryKey(entries, partial.id, partial.sessionId) ?? partial.id
26
+ const existing = entries.get(key)
27
+ const next = new Map(entries)
28
+ if (key !== partial.id) next.delete(partial.id)
29
+
30
+ const ended = partial.status === "done" || partial.status === "error" || partial.status === "cancelled"
31
+ next.set(key, {
32
+ ...(existing ?? { startedAt: now }),
33
+ ...partial,
34
+ id: existing?.id ?? partial.id,
35
+ startedAt: existing?.startedAt || partial.startedAt || now,
36
+ endedAt: ended ? (existing?.endedAt || now) : undefined,
37
+ })
38
+ return next
39
+ }
@@ -1,75 +1,75 @@
1
- import type { KVApi } from "../core/kv"
2
- import type { UsageReader, TodoStats } from "../core/usage"
3
- import type { Lang, SortOrder, ScrollMode } from "../core/types"
4
-
5
- /** Minimal session shape the panel reads from the host SDK. */
6
- export interface SessionLike {
7
- id?: string
8
- parentID?: string
9
- agent?: string
10
- cost?: number
11
- }
12
-
13
- export interface SessionStatusLike {
14
- type: string
15
- }
16
-
17
- /**
18
- * Standardized panel events. Adapter layers (V1/V2) translate host events
19
- * into these shapes so the panel component never touches host APIs directly.
20
- */
21
- export type PanelEventType = "part.updated" | "message.updated" | "session.idle" | "session.error"
22
- export type PanelEventScope = "session" | "global"
23
-
24
- export interface PanelEvent {
25
- type: PanelEventType
26
- /** Global V2 events need explicit session ownership checks. */
27
- scope?: PanelEventScope
28
- /** part payload for part.updated; session properties for idle/error. */
29
- payload?: Record<string, unknown>
30
- }
31
-
32
- export interface ToastOptions {
33
- variant?: "success" | "warning" | "error"
34
- title?: string
35
- duration?: number
36
- }
37
-
38
- /**
39
- * The full surface the SubAgentPanel component consumes.
40
- * V1 and V2 each implement this against their host SDK; the panel is agnostic.
41
- * Theme is slot-scoped in both hosts, so it is passed as a panel prop,
42
- * not part of the API.
43
- */
44
- export interface PanelApi {
45
- kv: KVApi
46
- usage: UsageReader
47
- session: {
48
- get(sid: string): SessionLike | undefined
49
- status(sid: string): SessionStatusLike | undefined
50
- /** Raw message list for a session (scan / error extraction). */
51
- messages(sid: string): unknown[] | undefined
52
- /** Raw parts of a message (scan). */
53
- part(messageID: string): unknown[] | undefined
54
- }
55
- event: {
56
- on(type: PanelEventType, cb: (e: PanelEvent) => void): () => void
57
- }
58
- client: {
59
- abort(input: { sessionID: string }): Promise<void>
60
- }
61
- route: {
62
- navigateSession(sessionID: string): void
63
- }
64
- ui: {
65
- toast(message: string, opts?: ToastOptions): void
66
- }
67
- settings: {
68
- lang: () => Lang
69
- maxEntries: () => number
70
- sortOrder: () => SortOrder
71
- scrollMode: () => ScrollMode
72
- }
73
- }
74
-
75
- export type { TodoStats }
1
+ import type { KVApi } from "../core/kv"
2
+ import type { UsageReader, TodoStats } from "../core/usage"
3
+ import type { Lang, SortOrder, ScrollMode } from "../core/types"
4
+
5
+ /** Minimal session shape the panel reads from the host SDK. */
6
+ export interface SessionLike {
7
+ id?: string
8
+ parentID?: string
9
+ agent?: string
10
+ cost?: number
11
+ }
12
+
13
+ export interface SessionStatusLike {
14
+ type: string
15
+ }
16
+
17
+ /**
18
+ * Standardized panel events. Adapter layers (V1/V2) translate host events
19
+ * into these shapes so the panel component never touches host APIs directly.
20
+ */
21
+ export type PanelEventType = "part.updated" | "message.updated" | "session.idle" | "session.error"
22
+ export type PanelEventScope = "session" | "global"
23
+
24
+ export interface PanelEvent {
25
+ type: PanelEventType
26
+ /** Global V2 events need explicit session ownership checks. */
27
+ scope?: PanelEventScope
28
+ /** part payload for part.updated; session properties for idle/error. */
29
+ payload?: Record<string, unknown>
30
+ }
31
+
32
+ export interface ToastOptions {
33
+ variant?: "success" | "warning" | "error"
34
+ title?: string
35
+ duration?: number
36
+ }
37
+
38
+ /**
39
+ * The full surface the SubAgentPanel component consumes.
40
+ * V1 and V2 each implement this against their host SDK; the panel is agnostic.
41
+ * Theme is slot-scoped in both hosts, so it is passed as a panel prop,
42
+ * not part of the API.
43
+ */
44
+ export interface PanelApi {
45
+ kv: KVApi
46
+ usage: UsageReader
47
+ session: {
48
+ get(sid: string): SessionLike | undefined
49
+ status(sid: string): SessionStatusLike | undefined
50
+ /** Raw message list for a session (scan / error extraction). */
51
+ messages(sid: string): unknown[] | undefined
52
+ /** Raw parts of a message (scan). */
53
+ part(messageID: string): unknown[] | undefined
54
+ }
55
+ event: {
56
+ on(type: PanelEventType, cb: (e: PanelEvent) => void): () => void
57
+ }
58
+ client: {
59
+ abort(input: { sessionID: string }): Promise<void>
60
+ }
61
+ route: {
62
+ navigateSession(sessionID: string): void
63
+ }
64
+ ui: {
65
+ toast(message: string, opts?: ToastOptions): void
66
+ }
67
+ settings: {
68
+ lang: () => Lang
69
+ maxEntries: () => number
70
+ sortOrder: () => SortOrder
71
+ scrollMode: () => ScrollMode
72
+ }
73
+ }
74
+
75
+ export type { TodoStats }
@@ -1,10 +1,10 @@
1
- import type { SessionLike } from "./panel-api"
2
-
3
- /** Whether an end event belongs to a child tracked by the current panel. */
4
- export function isDirectChildSession(
5
- currentSessionId: string,
6
- eventSessionId: string,
7
- eventSession: SessionLike | undefined,
8
- ): boolean {
9
- return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId
10
- }
1
+ import type { SessionLike } from "./panel-api"
2
+
3
+ /** Whether an end event belongs to a child tracked by the current panel. */
4
+ export function isDirectChildSession(
5
+ currentSessionId: string,
6
+ eventSessionId: string,
7
+ eventSession: SessionLike | undefined,
8
+ ): boolean {
9
+ return eventSessionId !== currentSessionId && eventSession?.parentID === currentSessionId
10
+ }
@@ -1,8 +1,8 @@
1
- import { createSignal } from "solid-js"
2
- import type { SubEntry } from "../core/types"
3
-
4
- /** 模块级缓存:各 session 的 entry 状态独立存储,不随当前视图切换而清除。 */
5
- export const globalEntryCache = new Map<string, Map<string, SubEntry>>()
6
-
7
- /** 模块级刷新信号:外部(如斜杠命令)触发清除后 +1,组件 scan 依赖它以重扫。 */
8
- export const [clearTick, setClearTick] = createSignal(0)
1
+ import { createSignal } from "solid-js"
2
+ import type { SubEntry } from "../core/types"
3
+
4
+ /** 模块级缓存:各 session 的 entry 状态独立存储,不随当前视图切换而清除。 */
5
+ export const globalEntryCache = new Map<string, Map<string, SubEntry>>()
6
+
7
+ /** 模块级刷新信号:外部(如斜杠命令)触发清除后 +1,组件 scan 依赖它以重扫。 */
8
+ export const [clearTick, setClearTick] = createSignal(0)
package/src/server.ts CHANGED
@@ -1,10 +1,19 @@
1
- import type { Plugin, PluginModule } from "@opencode-ai/plugin"
2
-
3
- const server: Plugin = async () => ({})
4
-
5
- const mod: PluginModule = {
6
- id: "opencode-subagent-magazine",
7
- server,
8
- }
9
-
10
- export default mod
1
+ import type { Plugin, PluginModule } from "@opencode-ai/plugin"
2
+
3
+ const server: Plugin = async () => ({})
4
+
5
+ /**
6
+ * V2 (opencode2) requires the default export to expose an `id` plus a
7
+ * `setup` (or `effect`) function. This plugin has no server-side behavior,
8
+ * so an empty setup keeps the module schema-valid and active on V2 while the
9
+ * `server` field stays for V1 detection.
10
+ */
11
+ const setup = async () => {}
12
+
13
+ const mod: PluginModule & { setup: () => Promise<void> } = {
14
+ id: "opencode-subagent-magazine",
15
+ server,
16
+ setup,
17
+ }
18
+
19
+ export default mod