pantheon-opencode 1.4.3 → 1.5.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/.pantheon/code-mode/README.md +93 -0
- package/.pantheon/code-mode/checkpoint-session.py +7 -0
- package/.pantheon/code-mode/checkpoint-session.sh +11 -0
- package/.pantheon/code-mode/checkpoint_session.py +481 -0
- package/.pantheon/code-mode/compress-inline.py +450 -0
- package/.pantheon/code-mode/eval-llm-judge.py +255 -0
- package/.pantheon/code-mode/eval-monte-carlo.py +368 -0
- package/.pantheon/code-mode/eval-run.py +197 -0
- package/.pantheon/code-mode/eval-static.py +321 -0
- package/.pantheon/code-mode/example-sync.sh +5 -0
- package/.pantheon/code-mode/session-end-save.py +186 -0
- package/.pantheon/code-mode/session-end-save.sh +18 -0
- package/.pantheon/code-mode/test-allowed.sh +5 -0
- package/.pantheon/code-mode/test-auto-continue-checkpoint.sh +179 -0
- package/.pantheon/code-mode/test-env.sh +7 -0
- package/.pantheon/code-mode/test-prlimit.sh +9 -0
- package/.pantheon/code-mode/test-timeout.sh +6 -0
- package/README.md +161 -1101
- package/README.pt-BR.md +222 -0
- package/bin/pantheon-init.mjs +30 -11
- package/opencode.json +1 -0
- package/package.json +14 -3
- package/plugin.json +15 -0
- package/scripts/_pantheon_paths.py +48 -7
- package/scripts/ci-validate-yaml.py +7 -2
- package/scripts/code_mode_server.py +33 -2
- package/scripts/cost.mjs +30 -14
- package/scripts/doctor.mjs +135 -127
- package/scripts/generate-preset-docs.mjs +127 -66
- package/scripts/generate-prompts.sh +2 -2
- package/scripts/install/cli-ui.mjs +8 -1
- package/scripts/install/config-migration.mjs +401 -0
- package/scripts/install/health-check.mjs +3 -3
- package/scripts/install/model-picker.mjs +72 -23
- package/scripts/install/opencode-version.mjs +47 -0
- package/scripts/install/opencode.mjs +261 -88
- package/scripts/install/plugin.mjs +112 -42
- package/scripts/install/shared.mjs +13 -158
- package/scripts/install-mcp.mjs +44 -232
- package/scripts/mcp_persistence_server.py +382 -16
- package/scripts/mcp_resources_server.py +144 -40
- package/scripts/memory_mcp_server.py +2 -2
- package/scripts/probe-context-rehydrate.mjs +240 -0
- package/scripts/probe-pantheon-cost.mjs +304 -0
- package/scripts/recover-zenodo-deposition.mjs +146 -0
- package/scripts/sync-tui.mjs +32 -7
- package/scripts/test-opencode-v1-v2-sandbox.sh +944 -0
- package/scripts/uninstall.mjs +298 -954
- package/scripts/validate-zenodo-release.mjs +154 -0
- package/scripts/zenodo-artifact.mjs +21 -0
- package/scripts/zenodo-release-state.mjs +53 -0
- package/src/agents/zeus.md +25 -14
- package/src/mcp/_pantheon_paths.py +48 -7
- package/src/mcp/code_mode_server.py +33 -2
- package/src/mcp/eval_store.py +1 -1
- package/src/mcp/mcp_persistence_server.py +382 -16
- package/src/mcp/mcp_resources_server.py +144 -40
- package/src/mcp/pantheon_vision_server.py +1 -1
- package/src/mcp/toon_codec.py +491 -0
- package/src/pantheon/background-job-board.ts +67 -1
- package/src/pantheon/cost-command.ts +60 -39
- package/src/pantheon/delegate-manager.ts +669 -0
- package/src/pantheon/delegation-classifier.ts +307 -0
- package/src/pantheon/delegation-enforce.ts +13 -12
- package/src/pantheon/delegation-finalize.ts +374 -0
- package/src/pantheon/delegation-notify.ts +212 -0
- package/src/pantheon/delegation-toast.ts +73 -0
- package/src/pantheon/delegation.ts +1120 -0
- package/src/pantheon/goal-loop.ts +2 -2
- package/src/pantheon/goal-store.ts +1 -1
- package/src/pantheon/hashline/tool.ts +2 -2
- package/src/pantheon/model-command.ts +2 -1
- package/src/pantheon/permission-eval.ts +284 -0
- package/src/pantheon/step-cap.ts +110 -0
- package/src/pantheon/token-opt.ts +781 -0
- package/src/pantheon/toon-codec.ts +481 -0
- package/src/pantheon/tui-version-check.ts +1 -3
- package/src/pantheon/v2-bridge.ts +129 -0
- package/src/pantheon/v2-events.ts +131 -0
- package/src/pantheon/v2-hooks.ts +287 -0
- package/src/pantheon/v2-tool-definitions.ts +331 -0
- package/src/pantheon/zeus-delegate-with-retry.ts +406 -0
- package/src/plugin-v2/index.ts +21 -0
- package/src/plugin-v2.ts +824 -0
- package/src/plugin.ts +236 -42
- 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 +10 -7
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +11 -8
- package/src/plugins/tui/dist/tui.js.map +1 -1
- package/src/plugins/tui/dist/tui.tsx +22 -16
- package/src/plugins/tui/package-lock.json +51 -51
- package/src/plugins/tui/package.json +7 -7
- package/src/plugins/tui/src/index.tsx +28 -24
- package/src/skills/README.md +0 -2
- package/src/pantheon/tool-context.ts +0 -41
package/src/plugin.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
1
3
|
import type { Plugin, PluginInput } from '@opencode-ai/plugin'
|
|
2
4
|
import type { PluginConfig } from 'opencode'
|
|
3
5
|
import { BackgroundJobBoard } from './pantheon/background-job-board.ts'
|
|
@@ -10,6 +12,15 @@ import {
|
|
|
10
12
|
DEFAULT_LIMITS as SANDBOX_DEFAULT_LIMITS,
|
|
11
13
|
} from './pantheon/context-sandbox.ts'
|
|
12
14
|
import { createCostCommand } from './pantheon/cost-command.ts'
|
|
15
|
+
import { buildPluginNativeDelegation, resolveDelegateMode } from './pantheon/delegate-manager.ts'
|
|
16
|
+
import {
|
|
17
|
+
collectRootSessionIDs,
|
|
18
|
+
createDelegationTools,
|
|
19
|
+
type DelegationClient,
|
|
20
|
+
resolveDelegationBudgets,
|
|
21
|
+
resolveDelegationTimeoutMs,
|
|
22
|
+
startIdleChildScan,
|
|
23
|
+
} from './pantheon/delegation.ts'
|
|
13
24
|
import { buildCompactionContext } from './pantheon/delegation-compaction.ts'
|
|
14
25
|
import {
|
|
15
26
|
createEnforcementGuard,
|
|
@@ -17,6 +28,8 @@ import {
|
|
|
17
28
|
SessionHierarchyRegistry,
|
|
18
29
|
zeusReadGuard,
|
|
19
30
|
} from './pantheon/delegation-enforce.ts'
|
|
31
|
+
import { handleDelegationEvent } from './pantheon/delegation-notify.ts'
|
|
32
|
+
import { showDelegationTerminalToast, type ToastClient } from './pantheon/delegation-toast.ts'
|
|
20
33
|
import { FilePersistenceAdapter } from './pantheon/file-persistence.ts'
|
|
21
34
|
import { GOAL_LOOP_DEFAULTS, GoalLoop, GoalStore } from './pantheon/goal-loop.ts'
|
|
22
35
|
import { createReadEnhancer } from './pantheon/hashline/read-enhancer.ts'
|
|
@@ -25,8 +38,14 @@ import { createIdleDispatcher } from './pantheon/idle-continuation.ts'
|
|
|
25
38
|
import { createPantheonLogger } from './pantheon/logger.ts'
|
|
26
39
|
import { createModelCommand } from './pantheon/model-command.ts'
|
|
27
40
|
import { pantheonPluginOnce } from './pantheon/plugin-once.ts'
|
|
28
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
applyActivePresetToConfig,
|
|
43
|
+
loadRoutingAgentModels,
|
|
44
|
+
loadRoutingMaxSteps,
|
|
45
|
+
loadRoutingPermissionTask,
|
|
46
|
+
} from './pantheon/presets.mjs'
|
|
29
47
|
import { safeSessionPath } from './pantheon/session-guard.ts'
|
|
48
|
+
import { StepCapTracker } from './pantheon/step-cap.ts'
|
|
30
49
|
import {
|
|
31
50
|
TODO_ENFORCER_DEFAULTS,
|
|
32
51
|
TodoEnforcer,
|
|
@@ -45,12 +64,24 @@ import { activePresetCandidates, createVisionHandler } from './pantheon/vision.t
|
|
|
45
64
|
// .pantheon/logs/hooks.log; the console echo is opt-in via PANTHEON_HOOKS_LOG=1.
|
|
46
65
|
const log = createPantheonLogger({ module: 'pantheon-plugin' })
|
|
47
66
|
|
|
67
|
+
// R4: per-agent step caps from routing.yml `agents.<name>.max_steps`
|
|
68
|
+
// (fail-open → {} = no agent capped). R1 retry_policy/cooldown are NOT
|
|
69
|
+
// wired here — the plugin has no retry path (opencode cannot intercept task
|
|
70
|
+
// completion); zeus waves load them via presets.mjs loaders and pass them
|
|
71
|
+
// to zeusDelegateWithRetry explicitly.
|
|
72
|
+
const stepCapTracker = new StepCapTracker(loadRoutingMaxSteps({ logger: log }))
|
|
73
|
+
|
|
74
|
+
// O5: permission.task glob rules from routing.yml `permission.task`
|
|
75
|
+
// (fail-open → undefined = existing runtime matrix only).
|
|
76
|
+
const routingPermissionTask = loadRoutingPermissionTask({ logger: log })
|
|
77
|
+
|
|
48
78
|
const board = new BackgroundJobBoard({
|
|
49
79
|
maxConcurrentPerAgent: 3,
|
|
50
80
|
signalDir: '.pantheon/deepwork/board-signals',
|
|
51
81
|
})
|
|
52
82
|
const persistence = new FilePersistenceAdapter('.pantheon/board/state.json')
|
|
53
83
|
board.setPersistence(persistence)
|
|
84
|
+
let terminalToastClient: ToastClient = {}
|
|
54
85
|
board
|
|
55
86
|
.recoverRunningJobs()
|
|
56
87
|
.catch((err) => log.error('[Pantheon Plugin] Failed to recover running jobs:', err))
|
|
@@ -60,13 +91,17 @@ board
|
|
|
60
91
|
// file-only log line (console echo opt-in via PANTHEON_HOOKS_LOG). There is
|
|
61
92
|
// deliberately NO chat delivery — the user policy is zero delegation
|
|
62
93
|
// notifications in the transcript. Completion visibility lives in the board
|
|
63
|
-
// `[unread]` marker
|
|
94
|
+
// `[unread]` marker (pantheon_delegation_list), pantheon_delegation_read,
|
|
95
|
+
// TUI toasts (pantheon-hooks, PANTHEON_TOASTS gate) and compaction
|
|
96
|
+
// carry-forward.
|
|
64
97
|
board.onTerminal((taskID: string) => {
|
|
65
98
|
const job = board.get(taskID)
|
|
66
99
|
if (!job) return
|
|
67
100
|
log.info(
|
|
68
101
|
`[Pantheon Plugin] Board terminal: [${job.alias}] ${job.description} → ${job.state}${job.resultSummary ? ` — ${job.resultSummary}` : ''}`,
|
|
69
102
|
)
|
|
103
|
+
// Deliberately direct-to-TUI: onTerminal must not inject chat/transcript text.
|
|
104
|
+
void showDelegationTerminalToast(terminalToastClient, job)
|
|
70
105
|
})
|
|
71
106
|
|
|
72
107
|
// Periodically prune terminal/reconciled jobs (24h TTL, every 30 min).
|
|
@@ -84,15 +119,23 @@ setInterval(
|
|
|
84
119
|
|
|
85
120
|
// ─── Phase 4: Read-Only Enforcement + Compaction Context ───────────────
|
|
86
121
|
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
122
|
+
// WS1 (PR #94): root-session predicate shared by the Phase 4 enforcement
|
|
123
|
+
// guard and the native delegate toolset — both must agree on what a root is.
|
|
124
|
+
// (Hoisted function: `rootSessions`/`sessionHierarchy` below are resolved at
|
|
125
|
+
// call time, after module init.)
|
|
126
|
+
function isRootSession(sessionID: string): boolean {
|
|
127
|
+
return rootSessions.has(sessionID) || sessionHierarchy.isRoot(sessionID)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The read-only registry is populated by createDelegationTools (delegation.ts)
|
|
131
|
+
// when a delegate is read-only (explicit flag or agent ∈ readOnlyAgents).
|
|
132
|
+
// The guard denies edit/write/bash/task inside those sessions by throwing
|
|
133
|
+
// from tool.execute.before; unknown sessions are allowed (safe default).
|
|
91
134
|
const enforcementGuard = createEnforcementGuard({
|
|
92
135
|
getReadOnlySessions: () => readOnlyRegistry.sessionIDs(),
|
|
93
136
|
options: {
|
|
94
137
|
getSessionAgent: (sessionID) => sessionAgents.get(sessionID),
|
|
95
|
-
isRootSession
|
|
138
|
+
isRootSession,
|
|
96
139
|
isChildSession: (sessionID) => sessionHierarchy.isChild(sessionID),
|
|
97
140
|
logger: log,
|
|
98
141
|
},
|
|
@@ -108,7 +151,7 @@ const commandNormalizer = createCommandNormalizer({ logger: log })
|
|
|
108
151
|
// Mirrors routing.yml background_delegation.max_compaction_items.
|
|
109
152
|
const COMPACTION_MAX_ITEMS = 10
|
|
110
153
|
|
|
111
|
-
// Root-session registry for the
|
|
154
|
+
// Root-session registry for the delegation depth guard: sessions created
|
|
112
155
|
// without a parentID are roots. Populated from TWO sources:
|
|
113
156
|
// 1. the `session.created` event hook (live roots — fires for sessions
|
|
114
157
|
// created while the plugin runs);
|
|
@@ -116,9 +159,9 @@ const COMPACTION_MAX_ITEMS = 10
|
|
|
116
159
|
// hook (compaction-134) — opencode does NOT replay session.created
|
|
117
160
|
// events for sessions that exist before plugin load, so without the
|
|
118
161
|
// seed a resumed root would never enter this set after a restart.
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
162
|
+
// delegation.ts additionally falls back to knownChildren ("any session WE
|
|
163
|
+
// created is a sub-session; unknown sessions are roots") so the depth guard
|
|
164
|
+
// works even when this set is incomplete.
|
|
122
165
|
const rootSessions = new Set<string>()
|
|
123
166
|
const sessionHierarchy = new SessionHierarchyRegistry()
|
|
124
167
|
const sessionAgents = new Map<string, string>()
|
|
@@ -127,14 +170,14 @@ const sessionAgents = new Map<string, string>()
|
|
|
127
170
|
* Seed resumed root sessions without making OpenCode startup depend on the
|
|
128
171
|
* session API responding. Some OpenCode environments do not have the API
|
|
129
172
|
* ready while plugin config hooks run; awaiting session.list() there leaves
|
|
130
|
-
* the TUI stuck before it can open. The
|
|
173
|
+
* the TUI stuck before it can open. The delegation layer already treats an
|
|
131
174
|
* unknown session as a root, so this enrichment is deliberately best-effort.
|
|
132
175
|
*/
|
|
133
176
|
function seedRootSessionsInBackground(input: PluginInput): void {
|
|
134
177
|
// Fail-open: environments without the session API (client.session absent or
|
|
135
178
|
// not yet wired while config hooks run) must NOT break startup. `session`
|
|
136
179
|
// is captured and the method invoked on it so `this` stays bound; the
|
|
137
|
-
//
|
|
180
|
+
// delegation layer already treats unknown sessions as roots, so skipping
|
|
138
181
|
// the seed is always safe.
|
|
139
182
|
const session = input.client.session
|
|
140
183
|
if (typeof session?.list !== 'function') return
|
|
@@ -144,15 +187,10 @@ function seedRootSessionsInBackground(input: PluginInput): void {
|
|
|
144
187
|
.then((result) => {
|
|
145
188
|
if (!result.error && Array.isArray(result.data)) {
|
|
146
189
|
sessionHierarchy.completeSeed(result.data)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
seeded += 1
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (seeded > 0) {
|
|
155
|
-
log.info(`[Pantheon Plugin] Seeded ${seeded} root session(s) from session.list`)
|
|
190
|
+
const seeded = collectRootSessionIDs(result.data)
|
|
191
|
+
if (seeded.size > 0) {
|
|
192
|
+
for (const id of seeded) rootSessions.add(id)
|
|
193
|
+
log.info(`[Pantheon Plugin] Seeded ${seeded.size} root session(s) from session.list`)
|
|
156
194
|
}
|
|
157
195
|
} else {
|
|
158
196
|
sessionHierarchy.failSeed()
|
|
@@ -170,6 +208,62 @@ function sdkErrorMessage(error: { data?: { message?: string } } | null | undefin
|
|
|
170
208
|
return typeof message === 'string' && message.trim() !== '' ? message : 'request failed'
|
|
171
209
|
}
|
|
172
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Adapt the opencode SDK client to the structural DelegationClient the
|
|
213
|
+
* delegation toolset expects: the SDK wraps every call in a
|
|
214
|
+
* `{data, error, request, response}` result while delegation.ts uses direct
|
|
215
|
+
* returns. Errors surface as throws so the toolset's own error handling
|
|
216
|
+
* (tool errors returned as TEXT) keeps working.
|
|
217
|
+
*/
|
|
218
|
+
function adaptDelegationClient(client: PluginInput['client']): DelegationClient {
|
|
219
|
+
return {
|
|
220
|
+
session: {
|
|
221
|
+
create: async (input) => {
|
|
222
|
+
const body: {
|
|
223
|
+
parentID?: string
|
|
224
|
+
title?: string
|
|
225
|
+
model?: { id: string; providerID: string }
|
|
226
|
+
} = { parentID: input.body.parentID }
|
|
227
|
+
if (input.body.title !== undefined) body.title = input.body.title
|
|
228
|
+
// Forward a model only when the active routing profile supplied one.
|
|
229
|
+
if (input.body.model !== undefined) body.model = input.body.model
|
|
230
|
+
const result = await client.session.create({ body })
|
|
231
|
+
if (result.error) throw new Error(sdkErrorMessage(result.error))
|
|
232
|
+
return { id: result.data.id }
|
|
233
|
+
},
|
|
234
|
+
promptAsync: async (input) => {
|
|
235
|
+
const path = safeSessionPath(input.path.id)
|
|
236
|
+
if (!path) {
|
|
237
|
+
log.warn(`[Pantheon Plugin] promptAsync skipped: invalid sessionID "${input.path.id}"`)
|
|
238
|
+
throw new Error(`invalid sessionID: ${input.path.id}`)
|
|
239
|
+
}
|
|
240
|
+
const result = await client.session.promptAsync({
|
|
241
|
+
path: path.path,
|
|
242
|
+
body: {
|
|
243
|
+
agent: input.body.agent,
|
|
244
|
+
...(input.body.model !== undefined
|
|
245
|
+
? { model: { providerID: input.body.model.providerID, modelID: input.body.model.id } }
|
|
246
|
+
: {}),
|
|
247
|
+
parts: input.body.parts,
|
|
248
|
+
},
|
|
249
|
+
})
|
|
250
|
+
if (result.error) throw new Error(sdkErrorMessage(result.error))
|
|
251
|
+
return result.data
|
|
252
|
+
},
|
|
253
|
+
messages: async (input) => {
|
|
254
|
+
const path = safeSessionPath(input.path.id)
|
|
255
|
+
if (!path) {
|
|
256
|
+
log.warn(`[Pantheon Plugin] messages skipped: invalid sessionID "${input.path.id}"`)
|
|
257
|
+
return []
|
|
258
|
+
}
|
|
259
|
+
const result = await client.session.messages({ path: path.path })
|
|
260
|
+
if (result.error) throw new Error(sdkErrorMessage(result.error))
|
|
261
|
+
return result.data
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
173
267
|
/**
|
|
174
268
|
* Adapt the opencode SDK client to the structural TodoEnforcerClient the
|
|
175
269
|
* todo enforcer expects: unwraps the SDK `{data, error, request, response}`
|
|
@@ -250,6 +344,7 @@ function adaptTodoEnforcerClient(client: PluginInput['client']): TodoEnforcerCli
|
|
|
250
344
|
* Helpers live in src/pantheon/vision.ts and are imported from there directly.
|
|
251
345
|
*/
|
|
252
346
|
const plugin: Plugin = async (input: PluginInput) => {
|
|
347
|
+
terminalToastClient = input.client as unknown as ToastClient
|
|
253
348
|
// release-134: runtime guard against DOUBLE registration (duplicate toasts +
|
|
254
349
|
// duplicate task_ids in one process — 2026-08-12 live logs). opencode
|
|
255
350
|
// merges the global config (npm-package plugin paths) with the project
|
|
@@ -264,12 +359,100 @@ const plugin: Plugin = async (input: PluginInput) => {
|
|
|
264
359
|
// is older than the installed package. Runs once, fail-open, no auto-sync.
|
|
265
360
|
checkTuiVersionStaleness((msg) => log.warn(msg))
|
|
266
361
|
const vision = createVisionHandler(input)
|
|
362
|
+
// Phase 2/3: background delegation toolset + the bound finalize lifecycle
|
|
363
|
+
// hook. Completion is observed through the event hook below
|
|
364
|
+
// (session.idle / session.error on a child) → finalizeDelegation.
|
|
365
|
+
// Fase B3: runtime controls for the two read-only exceptions and the real
|
|
366
|
+
// per-child wall-clock timer. No task_budget field is injected into the
|
|
367
|
+
// plugin/frontmatter; these are enforced by delegation.ts itself.
|
|
368
|
+
const delegationBudgets = resolveDelegationBudgets()
|
|
369
|
+
const wallClockTimeoutMs = resolveDelegationTimeoutMs()
|
|
370
|
+
|
|
371
|
+
// WS1 (PR #94): delegation backend gate. Default = legacy (the V1
|
|
372
|
+
// fire-and-forget toolset, intact); PANTHEON_DELEGATE_MODE=native selects
|
|
373
|
+
// the thin manager over native task() semantics. Kill-switch
|
|
374
|
+
// PANTHEON_DELEGATION=off covers the native manager; finalizeDelegation stays on
|
|
375
|
+
// the legacy observer: it works on the shared board in both modes and must
|
|
376
|
+
// never be kill-switched, so children never orphan. The idle hook below is
|
|
377
|
+
// untouched.
|
|
378
|
+
const delegationClient = adaptDelegationClient(input.client)
|
|
379
|
+
// Only an explicitly active routing profile may provide child models.
|
|
380
|
+
// An empty map deliberately lets OpenCode inherit the parent model.
|
|
381
|
+
const routingAgentModels = loadRoutingAgentModels({
|
|
382
|
+
candidates: activePresetCandidates(),
|
|
383
|
+
logger: log,
|
|
384
|
+
})
|
|
385
|
+
const readOnlyAgents = new Set(['apollo', 'gaia'])
|
|
386
|
+
const legacyDelegation = createDelegationTools({
|
|
387
|
+
board,
|
|
388
|
+
client: delegationClient,
|
|
389
|
+
options: {
|
|
390
|
+
rootSessions,
|
|
391
|
+
isChildSession: (sessionID) => sessionHierarchy.isChild(sessionID),
|
|
392
|
+
registerChildSession: (sessionID, parentID) => {
|
|
393
|
+
sessionHierarchy.register({ id: sessionID, parentID })
|
|
394
|
+
},
|
|
395
|
+
enforceRuntimeMatrix: true,
|
|
396
|
+
readOnlyAgents,
|
|
397
|
+
agentModels: routingAgentModels,
|
|
398
|
+
delegationBudgets,
|
|
399
|
+
stepCapTracker,
|
|
400
|
+
...(routingPermissionTask != null ? { permissionTask: routingPermissionTask } : {}),
|
|
401
|
+
...(wallClockTimeoutMs !== undefined ? { wallClockTimeoutMs } : {}),
|
|
402
|
+
},
|
|
403
|
+
})
|
|
404
|
+
const delegateMode = resolveDelegateMode()
|
|
405
|
+
if (delegateMode === 'native') {
|
|
406
|
+
log.info('[Pantheon Plugin] Delegation mode: native (PANTHEON_DELEGATE_MODE=native)')
|
|
407
|
+
}
|
|
408
|
+
const delegation =
|
|
409
|
+
delegateMode === 'native'
|
|
410
|
+
? buildPluginNativeDelegation({
|
|
411
|
+
board,
|
|
412
|
+
client: delegationClient,
|
|
413
|
+
isRootSession,
|
|
414
|
+
registerChildSession: (sessionID, parentID) => {
|
|
415
|
+
sessionHierarchy.register({ id: sessionID, parentID })
|
|
416
|
+
},
|
|
417
|
+
registerReadOnlySession: (sessionID, info) => {
|
|
418
|
+
readOnlyRegistry.register(sessionID, info)
|
|
419
|
+
},
|
|
420
|
+
readOnlyAgents,
|
|
421
|
+
agentModels: routingAgentModels,
|
|
422
|
+
stepCap: stepCapTracker,
|
|
423
|
+
...(wallClockTimeoutMs !== undefined ? { wallClockTimeoutMs } : {}),
|
|
424
|
+
finalizeDelegation: legacyDelegation.finalizeDelegation,
|
|
425
|
+
})
|
|
426
|
+
: legacyDelegation
|
|
427
|
+
|
|
428
|
+
// Proactive idle-child scan (Fix 2): finalize children that are idle on the
|
|
429
|
+
// board but have no MD report on disk — eliminates the phantom "running"
|
|
430
|
+
// window. Runs every 30s (configurable via PANTHEON_IDLE_SCAN_INTERVAL_MS),
|
|
431
|
+
// unref'd so it never holds the process open. Fail-open by design.
|
|
432
|
+
const idleScanIntervalMs = Number(process.env.PANTHEON_IDLE_SCAN_INTERVAL_MS) || undefined
|
|
433
|
+
startIdleChildScan(
|
|
434
|
+
{
|
|
435
|
+
board,
|
|
436
|
+
finalize: (childSessionID, opts) => delegation.finalizeDelegation(childSessionID, opts),
|
|
437
|
+
hasReport: (job) => {
|
|
438
|
+
try {
|
|
439
|
+
const filePath = join('.pantheon/delegations', job.parentSessionID, `${job.alias}.md`)
|
|
440
|
+
return existsSync(filePath)
|
|
441
|
+
} catch {
|
|
442
|
+
return false
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
logger: log,
|
|
446
|
+
},
|
|
447
|
+
idleScanIntervalMs,
|
|
448
|
+
)
|
|
449
|
+
|
|
267
450
|
// Wave 1 (PR #46): TODO continuation enforcer for root/non-board sessions.
|
|
268
451
|
// Mirrors routing.yml `todo_enforcer` (plugin scope has no routing.yml
|
|
269
|
-
// access — COMPACTION_MAX_ITEMS pattern). The event hook routes
|
|
270
|
-
// session.idle events here
|
|
271
|
-
// (routing.yml is a doc
|
|
272
|
-
// PANTHEON_TOASTS pattern).
|
|
452
|
+
// access — COMPACTION_MAX_ITEMS pattern). The event hook routes non-board
|
|
453
|
+
// session.idle events here; board-child idles go to finalizeDelegation.
|
|
454
|
+
// Runtime kill-switch: PANTHEON_TODO_ENFORCER=off (routing.yml is a doc
|
|
455
|
+
// mirror only — the env var is the real switch, PANTHEON_TOASTS pattern).
|
|
273
456
|
const todoEnforcer = new TodoEnforcer({
|
|
274
457
|
client: adaptTodoEnforcerClient(input.client),
|
|
275
458
|
board,
|
|
@@ -326,7 +509,7 @@ const plugin: Plugin = async (input: PluginInput) => {
|
|
|
326
509
|
await readEnhancer(input, output)
|
|
327
510
|
}
|
|
328
511
|
|
|
329
|
-
// Wave 4 (PR #46): /cost — cost + token visibility
|
|
512
|
+
// Wave 4 (PR #46): /cost — delegation cost + token visibility. Reads
|
|
330
513
|
// opencode.db read-only (node:sqlite, falls back to scripts/cost.mjs).
|
|
331
514
|
// Fully wired (unlike dispatch-guard, which is manual-orchestration-only
|
|
332
515
|
// because opencode 1.18.x cannot intercept task completion via hooks).
|
|
@@ -338,11 +521,11 @@ const plugin: Plugin = async (input: PluginInput) => {
|
|
|
338
521
|
// compaction-134 seed: opencode does NOT replay `session.created`
|
|
339
522
|
// events for sessions that exist before plugin load, so after a
|
|
340
523
|
// restart the resumed ROOT session never enters rootSessions and the
|
|
341
|
-
// depth guard would
|
|
342
|
-
// session.list() in the background — every session
|
|
343
|
-
// is a root. Fail-open: a failed/throttled list
|
|
344
|
-
//
|
|
345
|
-
// and config setup must never wait for the session API.
|
|
524
|
+
// depth guard would reject its pantheon_delegate calls. Seed the
|
|
525
|
+
// registry from session.list() in the background — every session
|
|
526
|
+
// WITHOUT a parentID is a root. Fail-open: a failed/throttled list
|
|
527
|
+
// leaves the set untouched (delegation.ts treats unknown sessions as
|
|
528
|
+
// roots), and config setup must never wait for the session API.
|
|
346
529
|
// Context sandbox config — opencode.json → context_sandbox: {enabled, limits}
|
|
347
530
|
try {
|
|
348
531
|
const raw = (config as unknown as Record<string, unknown>).context_sandbox
|
|
@@ -387,16 +570,18 @@ const plugin: Plugin = async (input: PluginInput) => {
|
|
|
387
570
|
log.warn('[plugin] preset application failed (see logs for details)')
|
|
388
571
|
}
|
|
389
572
|
},
|
|
390
|
-
// Phase 2:
|
|
391
|
-
// shape
|
|
392
|
-
// deterministic /pantheon-model, /cost and full-auto goal tools.
|
|
573
|
+
// Phase 2: background delegation tools (structural — matches the `tool`
|
|
574
|
+
// hook field shape; read_only sessions feed the Phase 4 registry).
|
|
393
575
|
tool: {
|
|
576
|
+
pantheon_delegate: delegation.pantheon_delegate,
|
|
577
|
+
pantheon_delegation_read: delegation.pantheon_delegation_read,
|
|
578
|
+
pantheon_delegation_list: delegation.pantheon_delegation_list,
|
|
394
579
|
hashline_edit: hashlineEdit,
|
|
395
580
|
// Wave 3 (PR #46): full-auto goal tools (single active goal/session).
|
|
396
581
|
pantheon_goal_create: goalTools.pantheon_goal_create,
|
|
397
582
|
pantheon_goal_update: goalTools.pantheon_goal_update,
|
|
398
583
|
pantheon_goal_get: goalTools.pantheon_goal_get,
|
|
399
|
-
// Wave 4 (PR #46): /cost — cost report
|
|
584
|
+
// Wave 4 (PR #46): /cost — delegation cost report from opencode.db.
|
|
400
585
|
pantheon_cost: costCommand.pantheon_cost,
|
|
401
586
|
// Deterministic /pantheon-model management of top-level model fields.
|
|
402
587
|
pantheon_model: modelCommand.pantheon_model,
|
|
@@ -430,13 +615,22 @@ const plugin: Plugin = async (input: PluginInput) => {
|
|
|
430
615
|
if (ev.type === 'session.compacted') {
|
|
431
616
|
await todoPreserver.onCompacted(ev.properties.sessionID)
|
|
432
617
|
}
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
// + swallowed), so the session is safe.
|
|
618
|
+
// Phase 3: observe completion on child sessions → finalizeDelegation.
|
|
619
|
+
// The board transition fires onTerminal → the file-only audit log (no
|
|
620
|
+
// chat delivery — see the onTerminal listener above). Unknown sessions
|
|
621
|
+
// are a no-op.
|
|
438
622
|
try {
|
|
439
|
-
|
|
623
|
+
const delegated = await handleDelegationEvent(ev, {
|
|
624
|
+
board,
|
|
625
|
+
finalize: (childSessionID, opts) => delegation.finalizeDelegation(childSessionID, opts),
|
|
626
|
+
})
|
|
627
|
+
// Wave 1/3: non-board idle → idle dispatcher. Board children are
|
|
628
|
+
// handled by handleDelegationEvent above; everything else (roots,
|
|
629
|
+
// non-board sessions) is routed by priority: an active goal owns the
|
|
630
|
+
// idle (goal loop), otherwise the todo enforcer re-injects. Neither
|
|
631
|
+
// onIdle ever throws (internal failures are logged + swallowed), so
|
|
632
|
+
// the session is safe.
|
|
633
|
+
if (!delegated && ev.type === 'session.idle') {
|
|
440
634
|
await idleDispatcher.onIdle(ev.properties.sessionID)
|
|
441
635
|
}
|
|
442
636
|
} catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"mappings":";;;;;;;;;;;;;iBAYwB"}
|
|
1
|
+
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"mappings":";;;;;;;;;;;;;iBAYwB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["/**\n * No-op server stub for the opencode plugin loader.\n *\n * The opencode plugin host requires every plugin (TUI included) to expose a\n * `server()` entry via `exports[\"./server\"]` (or package.json `main`). When\n * that entry is missing the host fails to attach the plugin's server half and\n * the TUI runtime never wires the reactive channel — the initial JSX mounts\n * but effects/refresh/events stay dead (the \"mudo panel\" symptom).\n *\n * This stub satisfies the loader contract; pantheon-tui is TUI-only and has\n * no background server work of its own.\n */\nexport default function server() {\n return {}\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAwB,SAAS;CAC/B,OAAO,CAAC;AACV"}
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["/**\n * No-op server stub for the opencode plugin loader.\n *\n * The opencode plugin host requires every plugin (TUI included) to expose a\n * `server()` entry via `exports[\"./server\"]` (or package.json `main`). When\n * that entry is missing the host fails to attach the plugin's server half and\n * the TUI runtime never wires the reactive channel — the initial JSX mounts\n * but effects/refresh/events stay dead (the \"mudo panel\" symptom).\n *\n * This stub satisfies the loader contract; pantheon-tui is TUI-only and has\n * no background server work of its own.\n */\nexport default function server() {\n return {}\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAwB,SAAS;CAC/B,OAAO,CAAC;AACV"}
|
|
@@ -22,7 +22,7 @@ type DelegationEntry = {
|
|
|
22
22
|
read?: boolean;
|
|
23
23
|
/** Internal provenance used to keep a finalized md report authoritative.
|
|
24
24
|
* 'children-only' = a native task() child session with NO board report
|
|
25
|
-
* (rendered with the distinct `[
|
|
25
|
+
* (rendered with the distinct `[native]` tag); 'md' = board report wins. */
|
|
26
26
|
source?: 'child' | 'live' | 'md' | 'children-only';
|
|
27
27
|
};
|
|
28
28
|
/** Parse one delegation report md header into a structured entry.
|
|
@@ -59,7 +59,7 @@ declare function readAllDelegationEntries(root: string): Promise<DelegationEntry
|
|
|
59
59
|
declare function resolveDelegationsDir(state: {
|
|
60
60
|
directory?: string;
|
|
61
61
|
worktree?: string;
|
|
62
|
-
} | undefined, cwd?:
|
|
62
|
+
} | undefined, cwd?: any): string;
|
|
63
63
|
/** Project root derived from the delegations dir: `<root>/.pantheon/delegations`
|
|
64
64
|
* → `<root>`. Used to point the panel logger at the REAL hooks.log — passing
|
|
65
65
|
* the delegations dir (or its dirname) directly made createTuiLogger append
|
|
@@ -279,10 +279,12 @@ type ChildDelegationLike = {
|
|
|
279
279
|
* (fail-open: a freshly-seen child is assumed active; the 1s poll + md
|
|
280
280
|
* correct it as soon as terminal data exists). */
|
|
281
281
|
declare function childStatusToState(status: string | undefined): 'running' | 'completed';
|
|
282
|
-
/** Row tag for a delegation entry: `[
|
|
283
|
-
* (source 'children-only' — no board report), `[
|
|
284
|
-
* ([apo-1])
|
|
285
|
-
*
|
|
282
|
+
/** Row tag for a delegation entry: `[native]` for native task() children
|
|
283
|
+
* (source 'children-only' — no board report), `[pantheon:<alias>]` for board
|
|
284
|
+
* rows ([pantheon:apo-1]) — the same tags the manager prints in
|
|
285
|
+
* pantheon_delegation_list, so panel rows and CLI list lines match. The
|
|
286
|
+
* panel renders the tag with a distinct style so native task() children are
|
|
287
|
+
* visually separable from pantheon_delegate jobs. */
|
|
286
288
|
declare function delegationTag(entry: DelegationEntry): string;
|
|
287
289
|
/** Turn child sessions (PRIMARY) enriched with md reports into the display
|
|
288
290
|
* list. One entry per child id (duplicates across re-fetches collapse).
|
|
@@ -293,7 +295,7 @@ declare function delegationTag(entry: DelegationEntry): string;
|
|
|
293
295
|
* time.created. A report-less child is a NATIVE task() child (every
|
|
294
296
|
* child of the current session — pantheon_delegate OR the native `task()`
|
|
295
297
|
* tool — carries parentID = caller), so it gets source 'children-only'
|
|
296
|
-
* and the `[
|
|
298
|
+
* and the `[native]` tag instead of a board alias.
|
|
297
299
|
* Terminal md state wins over the derived state; a running md defers to
|
|
298
300
|
* the child's live status. Sorted running-first (compareDelegationEntries).
|
|
299
301
|
* Pure — no I/O. */
|
|
@@ -319,6 +321,7 @@ type LiveDelegationStore = {
|
|
|
319
321
|
};
|
|
320
322
|
declare const plugin: TuiPluginModule & {
|
|
321
323
|
id: string;
|
|
324
|
+
setup: () => Promise<void>;
|
|
322
325
|
};
|
|
323
326
|
//#endregion
|
|
324
327
|
export { ChildDelegationLike, DelegationActivity, DelegationEntry, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, TuiSessionSources, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KAo7BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EASA;;EAEA;EACA;EACA;;EAEA;;;;EAIA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;iBAwC5C,yBAAyB,eAAe,QAAQ;;;;;;;;iBAWtD,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAYc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAWhD,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cASU;;;;cAKA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;iBAcX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAQI,mBAAmB,OAAO,kBAAkB;iBAc5C,wBAAwB,OAAO;;iBAoB/B,uBAAuB;;;;;;;iBAWvB,4BACd,mBAAmB,mBACnB,eAAe,wBACd;;KAmFS;EACV;EACA;EACA;EACA;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;MAAS;MAAgB;;;;;KAKjB;;EAEV;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;;;KAIU;EACV;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBAac,wBACd,MAAM,oBACN,eACC;;;iBA+Ea,yBACd,KAAK,YAAY,sBACjB,MAAM,oBACN;;;iBA+Fc,sBACd,KAAK,YAAY,sBACjB;;;;;;iBAiBc,2BACd;EAAqB;EAAa;iBAClC,YAAY,uDACX;;;;;iBA2Ba,sBACd,KAAK,YAAY,sBACjB,gBAAgB,sBAChB;;;iBAWc,kBAAkB,MAAM,sBAAsB;;;;;;iBAqB9C,uBACd,eAAe,uBACf,aAAa,oBACZ;;;;;;;;;iBA8Da,iBAAiB,cAAc;;;;KAOnC;;EAEV;EACA;;IAEE;MAAU;;;IAEV;MAAU;QAAY;QAAe,SAAS;;;;;;;;;;iBASlC,wBAAwB,SAAS;;;;;;;;iBAmBjC,gBAAgB;EAAgB;IAAQ;;;;;;;iBASxC,kBAAkB;EAAkC;IAAQ;;;;KAKhE;;EAEV;;EAEA;;;EAGA;;;EAGA;EACA;IACE;IACA;;;;;;;iBAQY,mBAAmB
|
|
1
|
+
{"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KAo7BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EASA;;EAEA;EACA;EACA;;EAEA;;;;EAIA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;iBAwC5C,yBAAyB,eAAe,QAAQ;;;;;;;;iBAWtD,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAYc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAWhD,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cASU;;;;cAKA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;iBAcX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAQI,mBAAmB,OAAO,kBAAkB;iBAc5C,wBAAwB,OAAO;;iBAoB/B,uBAAuB;;;;;;;iBAWvB,4BACd,mBAAmB,mBACnB,eAAe,wBACd;;KAmFS;EACV;EACA;EACA;EACA;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;MAAS;MAAgB;;;;;KAKjB;;EAEV;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;;;KAIU;EACV;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBAac,wBACd,MAAM,oBACN,eACC;;;iBA+Ea,yBACd,KAAK,YAAY,sBACjB,MAAM,oBACN;;;iBA+Fc,sBACd,KAAK,YAAY,sBACjB;;;;;;iBAiBc,2BACd;EAAqB;EAAa;iBAClC,YAAY,uDACX;;;;;iBA2Ba,sBACd,KAAK,YAAY,sBACjB,gBAAgB,sBAChB;;;iBAWc,kBAAkB,MAAM,sBAAsB;;;;;;iBAqB9C,uBACd,eAAe,uBACf,aAAa,oBACZ;;;;;;;;;iBA8Da,iBAAiB,cAAc;;;;KAOnC;;EAEV;EACA;;IAEE;MAAU;;;IAEV;MAAU;QAAY;QAAe,SAAS;;;;;;;;;;iBASlC,wBAAwB,SAAS;;;;;;;;iBAmBjC,gBAAgB;EAAgB;IAAQ;;;;;;;iBASxC,kBAAkB;EAAkC;IAAQ;;;;KAKhE;;EAEV;;EAEA;;;EAGA;;;EAGA;EACA;IACE;IACA;;;;;;;iBAQY,mBAAmB;;;;;;;iBAWnB,cAAc,OAAO;;;;;;;;;;;;;;iBAiBrB,4BACd,mBAAmB,mCACnB,aAAa,mBACb,eACC;;;;;;iBA0Ca,4BACd;EAAS,YAAY,cAAc,SAAS;eAC5C;;;;;KAiLU;EACV,KAAK,YAAY;;EAEjB;;EAEA;;cAucI,QAAQ;EAAoB;EAAY,aAAa"}
|
|
@@ -164,7 +164,7 @@ async function detectVersion(api) {
|
|
|
164
164
|
if (ver) return ver;
|
|
165
165
|
}
|
|
166
166
|
} catch {}
|
|
167
|
-
return "1.
|
|
167
|
+
return "1.5.0";
|
|
168
168
|
}
|
|
169
169
|
/**
|
|
170
170
|
* usage-bar — AI subscription usage gauge for the opencode TUI.
|
|
@@ -1311,12 +1311,14 @@ function childStatusToState(status) {
|
|
|
1311
1311
|
if (status === "idle") return "completed";
|
|
1312
1312
|
return "running";
|
|
1313
1313
|
}
|
|
1314
|
-
/** Row tag for a delegation entry: `[
|
|
1315
|
-
* (source 'children-only' — no board report), `[
|
|
1316
|
-
* ([apo-1])
|
|
1317
|
-
*
|
|
1314
|
+
/** Row tag for a delegation entry: `[native]` for native task() children
|
|
1315
|
+
* (source 'children-only' — no board report), `[pantheon:<alias>]` for board
|
|
1316
|
+
* rows ([pantheon:apo-1]) — the same tags the manager prints in
|
|
1317
|
+
* pantheon_delegation_list, so panel rows and CLI list lines match. The
|
|
1318
|
+
* panel renders the tag with a distinct style so native task() children are
|
|
1319
|
+
* visually separable from pantheon_delegate jobs. */
|
|
1318
1320
|
function delegationTag(entry) {
|
|
1319
|
-
return entry.source === "children-only" ? "[
|
|
1321
|
+
return entry.source === "children-only" ? "[native]" : `[pantheon:${entry.alias}]`;
|
|
1320
1322
|
}
|
|
1321
1323
|
/** Turn child sessions (PRIMARY) enriched with md reports into the display
|
|
1322
1324
|
* list. One entry per child id (duplicates across re-fetches collapse).
|
|
@@ -1327,7 +1329,7 @@ function delegationTag(entry) {
|
|
|
1327
1329
|
* time.created. A report-less child is a NATIVE task() child (every
|
|
1328
1330
|
* child of the current session — pantheon_delegate OR the native `task()`
|
|
1329
1331
|
* tool — carries parentID = caller), so it gets source 'children-only'
|
|
1330
|
-
* and the `[
|
|
1332
|
+
* and the `[native]` tag instead of a board alias.
|
|
1331
1333
|
* Terminal md state wins over the derived state; a running md defers to
|
|
1332
1334
|
* the child's live status. Sorted running-first (compareDelegationEntries).
|
|
1333
1335
|
* Pure — no I/O. */
|
|
@@ -1871,7 +1873,8 @@ const tui = (api, _options, _meta) => {
|
|
|
1871
1873
|
};
|
|
1872
1874
|
const plugin = {
|
|
1873
1875
|
id: "pantheon.tui",
|
|
1874
|
-
tui
|
|
1876
|
+
tui,
|
|
1877
|
+
setup: async () => {}
|
|
1875
1878
|
};
|
|
1876
1879
|
//#endregion
|
|
1877
1880
|
export { IDLE_SILENCE_MS, STALE_RUNNING_THRESHOLD_MS, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, fmtElapsed, isValidSessionId, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, toDelegationEntry, tuiLogPath, visibleDelegationList };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","names":[],"sources":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"tui.js","names":[],"sources":[],"mappings":""}
|