pantheon-opencode 1.5.0-beta.2 → 1.5.0-beta.20
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/.pantheon/code-mode/manifest.json +21 -0
- package/AGENTS.md +14 -63
- package/CHANGELOG.md +1260 -0
- package/README.md +162 -23
- package/README.pt-BR.md +104 -19
- package/bin/pantheon-init.mjs +178 -21
- package/commands/pantheon.md +1 -35
- package/package.json +23 -15
- package/plugin.json +1 -1
- package/pyproject.toml +62 -0
- package/scripts/code_mode_server.py +431 -55
- package/scripts/doctor.mjs +207 -9
- package/scripts/install/cli-ui.mjs +17 -11
- package/scripts/install/config-migration.mjs +4 -6
- package/scripts/install/opencode.mjs +137 -35
- package/scripts/install/plugin.mjs +12 -45
- package/scripts/install/shared.mjs +43 -2
- package/scripts/install/strings.mjs +129 -0
- package/scripts/install-mcp.mjs +1 -0
- package/scripts/manifest-inventory.mjs +410 -0
- package/scripts/mcp_persistence_server.py +536 -164
- package/scripts/package-evidence.mjs +242 -0
- package/scripts/probe-pantheon-cost.mjs +10 -1
- package/scripts/release-beta-version.mjs +56 -60
- package/scripts/sync-artifacts.mjs +169 -0
- package/scripts/sync-tui.mjs +129 -64
- package/scripts/test-opencode-v1-v2-sandbox.sh +79 -161
- package/scripts/validation-policy.mjs +7 -7
- package/scripts/version-check.mjs +113 -108
- package/scripts/versioning.mjs +230 -86
- package/src/agents/apollo.md +1 -0
- package/src/agents/gaia.md +1 -0
- package/src/agents/mnemosyne.md +1 -1
- package/src/agents/zeus.md +11 -54
- package/src/instructions/memory-protocol.instructions.md +6 -3
- package/src/instructions/zeus-anti-stall.instructions.md +2 -4
- package/src/instructions/zeus-council-synthesis.instructions.md +3 -28
- package/src/instructions/zeus-timeout-retry.instructions.md +3 -28
- package/src/mcp/code_mode_server.py +431 -55
- package/src/mcp/mcp_persistence_server.py +536 -164
- package/src/mcp/toon_codec.py +2 -4
- package/src/pantheon/background-job-board.ts +47 -5
- package/src/pantheon/{delegation-compaction.ts → compaction-context.ts} +10 -100
- package/src/pantheon/cost-command.ts +309 -44
- package/src/pantheon/delegation-enforce.ts +59 -14
- package/src/pantheon/file-persistence-salvage.ts +64 -0
- package/src/pantheon/file-persistence.ts +194 -45
- package/src/pantheon/goal-loop.ts +1 -1
- package/src/pantheon/goal-store.ts +1 -1
- package/src/pantheon/idle-continuation.ts +4 -6
- package/src/pantheon/model-command.ts +1 -2
- package/src/pantheon/native-task-status.ts +139 -0
- package/src/pantheon/presets.d.mts +0 -20
- package/src/pantheon/presets.mjs +0 -64
- package/src/pantheon/session-guard.ts +2 -2
- package/src/pantheon/session-id.ts +30 -0
- package/src/pantheon/shared-board.ts +37 -0
- package/src/pantheon/task-result-guard.ts +106 -0
- package/src/pantheon/todo-enforcer.ts +3 -3
- package/src/pantheon/tool-ceiling.ts +159 -0
- package/src/pantheon/tool-context.ts +23 -0
- package/src/pantheon/v2-bridge.ts +1 -4
- package/src/plugin-v2.ts +8 -21
- package/src/plugin.ts +83 -251
- package/src/plugins/pantheon-hooks.ts +51 -0
- package/src/plugins/tui/dist/server.d.ts +1 -1
- package/src/plugins/tui/dist/server.d.ts.map +1 -1
- package/src/plugins/tui/dist/server.js +1 -1
- package/src/plugins/tui/dist/server.js.map +1 -1
- package/src/plugins/tui/dist/tui.d.ts +261 -77
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +640 -234
- package/src/plugins/tui/dist/tui.js.map +1 -1
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +2 -2
- package/src/plugins/tui/src/index.tsx +872 -226
- package/src/routing.yml +0 -16
- package/src/skills/context-compression/SKILL.md +1 -1
- package/scripts/generate-preset-docs.mjs +0 -300
- package/scripts/generate-prompts.sh +0 -222
- package/scripts/hash_verify.py +0 -192
- package/scripts/install/component.mjs +0 -24
- package/scripts/install/migrate-config.mjs +0 -302
- package/scripts/test-context-compression.sh +0 -169
- package/src/pantheon/auto-wake.ts +0 -89
- package/src/pantheon/chat-reminders.ts +0 -9
- package/src/pantheon/comment-checker.ts +0 -48
- package/src/pantheon/compaction-assert.ts +0 -160
- package/src/pantheon/cost-tracker.ts +0 -108
- package/src/pantheon/delegate-manager.ts +0 -669
- package/src/pantheon/delegation-classifier.ts +0 -307
- package/src/pantheon/delegation-finalize.ts +0 -374
- package/src/pantheon/delegation-notify.ts +0 -212
- package/src/pantheon/delegation-toast.ts +0 -73
- package/src/pantheon/delegation.ts +0 -1120
- package/src/pantheon/dispatch-guard.ts +0 -109
- package/src/pantheon/permission-eval.ts +0 -284
- package/src/pantheon/preemptive-compact.ts +0 -76
- package/src/pantheon/retry-policy.ts +0 -212
- package/src/pantheon/step-cap.ts +0 -110
- package/src/pantheon/token-opt.ts +0 -781
- package/src/pantheon/toon-codec.ts +0 -481
- package/src/pantheon/v2-events.ts +0 -131
- package/src/pantheon/v2-hooks.ts +0 -287
- package/src/pantheon/v2-tool-definitions.ts +0 -331
- package/src/pantheon/zeus-delegate-with-retry.ts +0 -406
- package/src/plugins/tui/dist/tui.tsx +0 -2605
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delegation event wiring — observe background-job lifecycle events and
|
|
3
|
-
* route them to finalize. This module is the `event`-hook complement of the
|
|
4
|
-
* board: `session.idle`/`session.error` on a child session that is a board
|
|
5
|
-
* job → `finalizeDelegation` (Phase 2), which transitions the board and
|
|
6
|
-
* fires `onTerminal`.
|
|
7
|
-
*
|
|
8
|
-
* NOT a notification channel. The user policy is ZERO delegation
|
|
9
|
-
* notifications in the chat transcript — no `<task-notification>` block is
|
|
10
|
-
* ever injected into a `chat.message` output, and no queued delivery path
|
|
11
|
-
* exists. Completion visibility lives in the legitimate channels only:
|
|
12
|
-
* the board `[unread]` marker (`pantheon_delegation_list`),
|
|
13
|
-
* `pantheon_delegation_read`, TUI toasts (pantheon-hooks, PANTHEON_TOASTS
|
|
14
|
-
* gate), and compaction carry-forward. The plugin's `onTerminal` listener
|
|
15
|
-
* writes a file-only log line for the on-disk audit trail.
|
|
16
|
-
*
|
|
17
|
-
* `handleDelegationEvent` wires the `event` hook: `session.idle`/`session.error`
|
|
18
|
-
* on a child session that is a board job → `finalizeDelegation` (Phase 2),
|
|
19
|
-
* which transitions the board and fires `onTerminal`. Finalize never notifies
|
|
20
|
-
* directly, and `markReconciled` does not re-fire onTerminal (reconcile is an
|
|
21
|
-
* acknowledgment, not a completion), so every job gets exactly ONE terminal
|
|
22
|
-
* transition — never a reconciled echo. Unknown sessions are ignored.
|
|
23
|
-
*
|
|
24
|
-
* `finalizeIdleChildrenWithoutMd` proactively finalizes children confirmed
|
|
25
|
-
* idle by the V1 session-status API but with no MD report on disk — eliminates
|
|
26
|
-
* the phantom "running" window during `pullOutput` without treating a board
|
|
27
|
-
* record's `running` state as proof that the child is idle. Wired into a
|
|
28
|
-
* periodic scan via `startIdleChildScan` (default 30 s cadence).
|
|
29
|
-
*
|
|
30
|
-
* Pure TypeScript — zero runtime dependencies beyond the board types.
|
|
31
|
-
*
|
|
32
|
-
* Follow-up (Themis review, Phase 5): `extractErrorMessage` handles the two
|
|
33
|
-
* observed `session.error` error shapes (string, { message }). The opencode SDK
|
|
34
|
-
* MAY expose other shapes (e.g. Error-like with non-string `message`) — if a
|
|
35
|
-
* real session.error ever carries one, the extraction degrades to the generic
|
|
36
|
-
* "session error" label. No code change: there is no evidence any other shape
|
|
37
|
-
* occurs in practice; revisit if a session.error shows an unexpected payload.
|
|
38
|
-
*
|
|
39
|
-
* @module delegation-notify
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
import type { BackgroundJobRecord } from './background-job-board.ts'
|
|
43
|
-
import type { FinalizeInput } from './delegation-finalize.ts'
|
|
44
|
-
|
|
45
|
-
export type { FinalizeInput } from './delegation-finalize.ts'
|
|
46
|
-
|
|
47
|
-
/** Default cadence for the proactive idle-child scan (30 s). */
|
|
48
|
-
export const IDLE_SCAN_INTERVAL_MS = 30_000
|
|
49
|
-
|
|
50
|
-
// ─── Types ─────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
/** Structural view of the event payload the `event` hook receives. */
|
|
53
|
-
export interface DelegationEventLike {
|
|
54
|
-
type?: string
|
|
55
|
-
properties?: Record<string, unknown>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Dependencies threaded to handleDelegationEvent (board lookup + finalize). */
|
|
59
|
-
export interface DelegationEventDeps {
|
|
60
|
-
/** Look up a board job by session ID (child session IDs are task IDs). */
|
|
61
|
-
board: { get(taskID: string): BackgroundJobRecord | undefined }
|
|
62
|
-
/** Bound Phase 2 finalize — clears the timeout timer + writes the report. */
|
|
63
|
-
finalize: (childSessionID: string, opts: FinalizeInput) => Promise<unknown>
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─── Event → finalize wiring ───────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
function extractErrorMessage(error: unknown): string {
|
|
69
|
-
if (typeof error === 'string' && error.trim() !== '') return error
|
|
70
|
-
if (error && typeof error === 'object' && 'message' in error) {
|
|
71
|
-
const message = (error as { message: unknown }).message
|
|
72
|
-
if (typeof message === 'string' && message.trim() !== '') return message
|
|
73
|
-
}
|
|
74
|
-
return 'session error'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Handle a lifecycle event for background delegations:
|
|
79
|
-
* - `session.idle` on a board job → finalize as completed;
|
|
80
|
-
* - `session.error` on a board job → finalize as error (message extracted);
|
|
81
|
-
* - any other event, a missing session ID, or an unknown session → no-op.
|
|
82
|
-
*
|
|
83
|
-
* Finalize transitions the board, which fires `onTerminal` (the plugin's
|
|
84
|
-
* file-only audit log — no chat delivery; see module header). The board's
|
|
85
|
-
* same-terminal idempotency makes repeated idles harmless (finalize rewrites
|
|
86
|
-
* the report but never transitions a terminal job to a different state).
|
|
87
|
-
*
|
|
88
|
-
* @returns True when the event named a board job (handled).
|
|
89
|
-
*/
|
|
90
|
-
export async function handleDelegationEvent(
|
|
91
|
-
ev: DelegationEventLike,
|
|
92
|
-
deps: DelegationEventDeps,
|
|
93
|
-
): Promise<boolean> {
|
|
94
|
-
const type = ev?.type
|
|
95
|
-
if (type !== 'session.idle' && type !== 'session.error') return false
|
|
96
|
-
const sessionID = ev.properties?.sessionID
|
|
97
|
-
if (typeof sessionID !== 'string' || sessionID === '') return false
|
|
98
|
-
if (deps.board.get(sessionID) === undefined) return false
|
|
99
|
-
|
|
100
|
-
if (type === 'session.idle') {
|
|
101
|
-
await deps.finalize(sessionID, { state: 'completed' })
|
|
102
|
-
} else {
|
|
103
|
-
await deps.finalize(sessionID, {
|
|
104
|
-
state: 'error',
|
|
105
|
-
error: extractErrorMessage(ev.properties?.error),
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
return true
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ─── Proactive finalize (Fix 2) ────────────────────────────────────────
|
|
112
|
-
|
|
113
|
-
/** Dependencies for idle-child scanning. */
|
|
114
|
-
export interface IdleChildScanDeps {
|
|
115
|
-
/** Look up a board job by session ID (child session IDs are task IDs). */
|
|
116
|
-
board: {
|
|
117
|
-
get(taskID: string): BackgroundJobRecord | undefined
|
|
118
|
-
list(parentSessionID?: string): BackgroundJobRecord[]
|
|
119
|
-
}
|
|
120
|
-
/** Bound Phase 2 finalize — clears the timeout timer + writes the report. */
|
|
121
|
-
finalize: (childSessionID: string, opts: FinalizeInput) => Promise<unknown>
|
|
122
|
-
/**
|
|
123
|
-
* Confirm the child's native V1 session status. `undefined` means the
|
|
124
|
-
* status is unavailable and MUST NOT be treated as idle.
|
|
125
|
-
*/
|
|
126
|
-
isIdle?: (childSessionID: string) => Promise<boolean | undefined>
|
|
127
|
-
/**
|
|
128
|
-
* Check whether an MD report exists for the given board job.
|
|
129
|
-
* Receives the full job record so the caller can construct the report
|
|
130
|
-
* path from `job.parentSessionID` + `job.alias`.
|
|
131
|
-
* If the function throws, the scan skips that child (fail-open).
|
|
132
|
-
*/
|
|
133
|
-
hasReport: (job: BackgroundJobRecord) => boolean
|
|
134
|
-
/** Optional logger for diagnostic messages. */
|
|
135
|
-
logger?: { info?: (msg: string) => void; warn?: (msg: string) => void }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Scan running board jobs for children whose V1 session status is confirmed
|
|
140
|
-
* idle but have no MD report on disk, and proactively finalize them. This
|
|
141
|
-
* eliminates the phantom "running" window that occurs between `session.idle`
|
|
142
|
-
* firing and the `pullOutput`→`writeDelegationReport` cycle completing.
|
|
143
|
-
*
|
|
144
|
-
* Called from the periodic idle-child scan (`startIdleChildScan`, default 30 s).
|
|
145
|
-
* The scan is O(N) where N = number of running jobs (typically < 10).
|
|
146
|
-
*
|
|
147
|
-
* A missing status probe is an explicit unknown state, not an idle signal, so
|
|
148
|
-
* legacy embedders without the V1 API fail closed. Errors are caught
|
|
149
|
-
* per-child so one failure never blocks the rest.
|
|
150
|
-
*
|
|
151
|
-
* @returns Number of children finalized (for diagnostics).
|
|
152
|
-
*/
|
|
153
|
-
export async function finalizeIdleChildrenWithoutMd(deps: IdleChildScanDeps): Promise<number> {
|
|
154
|
-
const runningJobs = deps.board.list().filter((j: BackgroundJobRecord) => j.state === 'running')
|
|
155
|
-
|
|
156
|
-
let finalized = 0
|
|
157
|
-
for (const job of runningJobs) {
|
|
158
|
-
try {
|
|
159
|
-
if (deps.isIdle === undefined) {
|
|
160
|
-
deps.logger?.warn?.(
|
|
161
|
-
`[delegation-notify] idle status unknown for ${job.taskID}; proactive finalize skipped`,
|
|
162
|
-
)
|
|
163
|
-
continue
|
|
164
|
-
}
|
|
165
|
-
const idle = await deps.isIdle(job.taskID)
|
|
166
|
-
if (idle !== true) {
|
|
167
|
-
if (idle === undefined) {
|
|
168
|
-
deps.logger?.warn?.(
|
|
169
|
-
`[delegation-notify] idle status unknown for ${job.taskID}; proactive finalize skipped`,
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
continue
|
|
173
|
-
}
|
|
174
|
-
// If the child already has an MD report, finalize was already handled
|
|
175
|
-
// by handleDelegationEvent — skip.
|
|
176
|
-
if (deps.hasReport(job)) continue
|
|
177
|
-
// Child is idle on the board but has no report — proactively finalize.
|
|
178
|
-
await deps.finalize(job.taskID, { state: 'completed' })
|
|
179
|
-
finalized++
|
|
180
|
-
} catch (err) {
|
|
181
|
-
deps.logger?.warn?.(`[delegation-notify] proactive finalize failed for ${job.taskID}: ${err}`)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return finalized
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ─── Periodic scan launcher ─────────────────────────────────────────────
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Start a periodic scan that proactively finalizes idle board children
|
|
191
|
-
* without MD reports. The scan runs every `intervalMs` (default 30 s) and
|
|
192
|
-
* is **unref'd** so it never holds the process open on its own.
|
|
193
|
-
*
|
|
194
|
-
* The timer is idempotent — calling this function multiple times is safe
|
|
195
|
-
* (each call creates an independent timer, but the underlying finalize
|
|
196
|
-
* operation is itself idempotent due to the board's same-terminal guard).
|
|
197
|
-
*
|
|
198
|
-
* @returns A `NodeJS.Timeout` handle that can be cleared with
|
|
199
|
-
* `clearInterval(handle)` to stop the scan.
|
|
200
|
-
*/
|
|
201
|
-
export function startIdleChildScan(
|
|
202
|
-
deps: IdleChildScanDeps,
|
|
203
|
-
intervalMs: number = IDLE_SCAN_INTERVAL_MS,
|
|
204
|
-
): NodeJS.Timeout {
|
|
205
|
-
const timer = setInterval(() => {
|
|
206
|
-
void finalizeIdleChildrenWithoutMd(deps).catch((err) => {
|
|
207
|
-
deps.logger?.warn?.(`[delegation-notify] periodic idle scan failed: ${err}`)
|
|
208
|
-
})
|
|
209
|
-
}, intervalMs)
|
|
210
|
-
timer.unref()
|
|
211
|
-
return timer
|
|
212
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { BackgroundJobRecord } from './background-job-board.ts'
|
|
2
|
-
|
|
3
|
-
type ToastCategory = 'errors' | 'delegations' | 'council'
|
|
4
|
-
|
|
5
|
-
const toastMode = (process.env.PANTHEON_TOASTS ?? '').trim().toLowerCase()
|
|
6
|
-
const enabledToastCategories: ReadonlySet<ToastCategory> = (() => {
|
|
7
|
-
switch (toastMode) {
|
|
8
|
-
case 'off':
|
|
9
|
-
return new Set()
|
|
10
|
-
case 'errors':
|
|
11
|
-
return new Set(['errors'])
|
|
12
|
-
case 'council':
|
|
13
|
-
return new Set(['errors', 'council'])
|
|
14
|
-
case 'all':
|
|
15
|
-
return new Set(['errors', 'delegations', 'council'])
|
|
16
|
-
default:
|
|
17
|
-
return new Set(['errors', 'delegations', 'council'])
|
|
18
|
-
}
|
|
19
|
-
})()
|
|
20
|
-
const terminalToastsShown = new Set<string>()
|
|
21
|
-
const terminalStates = new Set(['completed', 'error', 'cancelled'])
|
|
22
|
-
|
|
23
|
-
/** Match the lifecycle toast gate used by pantheon-hooks. */
|
|
24
|
-
export function delegationToastEnabled(): boolean {
|
|
25
|
-
return enabledToastCategories.has('delegations')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type ToastClient = {
|
|
29
|
-
tui?: {
|
|
30
|
-
showToast?: (input: {
|
|
31
|
-
body: {
|
|
32
|
-
title?: string
|
|
33
|
-
message: string
|
|
34
|
-
variant: 'info' | 'success' | 'warning' | 'error'
|
|
35
|
-
duration: number
|
|
36
|
-
}
|
|
37
|
-
}) => Promise<unknown>
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Best-effort visual completion signal; never affects board finalization. */
|
|
42
|
-
export async function showDelegationTerminalToast(
|
|
43
|
-
client: ToastClient,
|
|
44
|
-
job: Pick<BackgroundJobRecord, 'taskID' | 'alias' | 'agent' | 'state' | 'timedOut'>,
|
|
45
|
-
): Promise<void> {
|
|
46
|
-
try {
|
|
47
|
-
if (!delegationToastEnabled()) return
|
|
48
|
-
const isTerminal = job.timedOut || terminalStates.has(job.state)
|
|
49
|
-
if (isTerminal) {
|
|
50
|
-
if (terminalToastsShown.has(job.taskID)) return
|
|
51
|
-
terminalToastsShown.add(job.taskID)
|
|
52
|
-
}
|
|
53
|
-
const showToast = client.tui?.showToast
|
|
54
|
-
if (typeof showToast !== 'function') return
|
|
55
|
-
const state = job.timedOut ? 'timeout' : job.state
|
|
56
|
-
const variant =
|
|
57
|
-
state === 'completed'
|
|
58
|
-
? 'success'
|
|
59
|
-
: state === 'error' || state === 'timeout'
|
|
60
|
-
? 'error'
|
|
61
|
-
: 'warning'
|
|
62
|
-
await showToast({
|
|
63
|
-
body: {
|
|
64
|
-
title: 'Delegação concluída',
|
|
65
|
-
message: `${job.alias} (${job.agent}) — ${state}`,
|
|
66
|
-
variant,
|
|
67
|
-
duration: 4000,
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
} catch {
|
|
71
|
-
// TUI is optional (and may disappear during shutdown); completion is durable.
|
|
72
|
-
}
|
|
73
|
-
}
|