pantheon-opencode 1.5.0-beta.1 → 1.5.0-beta.3
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 +44 -15
- package/README.pt-BR.md +39 -12
- package/package.json +7 -3
- package/plugin.json +1 -1
- package/pyproject.toml +57 -0
- package/scripts/code_mode_server.py +101 -36
- package/scripts/cost.mjs +30 -14
- package/scripts/install/opencode.mjs +6 -0
- package/scripts/install/plugin.mjs +3 -33
- package/scripts/manifest-inventory.mjs +410 -0
- package/scripts/mcp_persistence_server.py +518 -128
- package/scripts/package-evidence.mjs +242 -0
- package/scripts/probe-context-rehydrate.mjs +240 -0
- package/scripts/probe-pantheon-cost.mjs +304 -0
- package/scripts/release-beta-version.mjs +61 -60
- package/scripts/sync-tui.mjs +5 -27
- package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
- package/scripts/validation-policy.mjs +7 -7
- package/scripts/version-check.mjs +113 -108
- package/scripts/versioning.mjs +131 -88
- package/src/mcp/code_mode_server.py +101 -36
- package/src/mcp/mcp_persistence_server.py +518 -128
- package/src/pantheon/compaction-handoff.ts +304 -0
- package/src/pantheon/cost-command.ts +49 -32
- package/src/pantheon/delegate-manager.ts +285 -0
- package/src/pantheon/provider-capabilities.ts +221 -0
- package/src/pantheon/provider-stream-tracker.ts +182 -0
- package/src/pantheon/token-opt.ts +237 -11
- package/src/plugin-v2.ts +2 -14
- package/src/plugin.ts +3 -8
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/.pantheon/code-mode/.gitkeep +0 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
- package/src/pantheon/delegate-task-adapter.ts +0 -444
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* V1 Session Task Adapter (WS1, PR #94) — task()-shaped function over the V1
|
|
3
|
-
* SDK session surface (`session.create` / `promptAsync` / `messages`).
|
|
4
|
-
*
|
|
5
|
-
* The thin manager (delegate-manager.ts) orchestrates launch → monitor →
|
|
6
|
-
* reconcile → verify; THIS module is the only place that touches the host:
|
|
7
|
-
*
|
|
8
|
-
* - `createSessionTaskFn()` — prompt acceptance (bounded) + completion
|
|
9
|
-
* observed via the board (the idle hook / finalize path owns the terminal
|
|
10
|
-
* transition) + verified content pulled from the MD report written by
|
|
11
|
-
* `finalizeDelegation`. No MD report (or an empty one) means the result is
|
|
12
|
-
* NOT verified — the manager records `error`, never `completed`.
|
|
13
|
-
* - `createNativeDelegateTools()` — the three delegation tools in the same
|
|
14
|
-
* structural shape as the legacy toolset, wired to per-parent managers.
|
|
15
|
-
* The child session is created FIRST so `taskID == childSessionID`: the
|
|
16
|
-
* V1 idle hook and the manager share one board record (no double
|
|
17
|
-
* registration, no scheduler of its own).
|
|
18
|
-
*
|
|
19
|
-
* The legacy fire-and-forget path (delegation.ts) is untouched; the plugin
|
|
20
|
-
* selects legacy vs native via `PANTHEON_DELEGATE_MODE`.
|
|
21
|
-
*
|
|
22
|
-
* @module delegate-task-adapter
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { z } from 'zod'
|
|
26
|
-
|
|
27
|
-
import type { BackgroundJobBoard } from './background-job-board.ts'
|
|
28
|
-
import {
|
|
29
|
-
createDelegateManager,
|
|
30
|
-
type DelegateManager,
|
|
31
|
-
type DelegateManagerOptions,
|
|
32
|
-
isDelegationEnabled,
|
|
33
|
-
type NativeTaskFn,
|
|
34
|
-
} from './delegate-manager.ts'
|
|
35
|
-
import type { DelegationToolset } from './delegation.ts'
|
|
36
|
-
import {
|
|
37
|
-
DELEGATION_DEFAULTS,
|
|
38
|
-
type DelegationClient,
|
|
39
|
-
readDelegationReport,
|
|
40
|
-
} from './delegation-finalize.ts'
|
|
41
|
-
import type { StepCapTracker } from './step-cap.ts'
|
|
42
|
-
|
|
43
|
-
// ─── Mode gate + kill-switch ───────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
/** Delegation backend selected by `PANTHEON_DELEGATE_MODE`. */
|
|
46
|
-
export type DelegateMode = 'native' | 'legacy'
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Resolve the delegation backend: `PANTHEON_DELEGATE_MODE=native` selects the
|
|
50
|
-
* thin native manager; anything else (unset, `legacy`, garbage) stays on the
|
|
51
|
-
* V1 fire-and-forget path. Fail-safe default = legacy (V1 intacto).
|
|
52
|
-
*/
|
|
53
|
-
export function resolveDelegateMode(
|
|
54
|
-
env: Record<string, string | undefined> = process.env,
|
|
55
|
-
): DelegateMode {
|
|
56
|
-
return (env.PANTHEON_DELEGATE_MODE ?? '').trim().toLowerCase() === 'native' ? 'native' : 'legacy'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Minimal structural shape of a delegation tool (legacy or native). */
|
|
60
|
-
export interface KillSwitchableTool {
|
|
61
|
-
description: string
|
|
62
|
-
args: unknown
|
|
63
|
-
// biome-ignore lint/suspicious/noExplicitAny: tool args/ctx shapes differ per toolset
|
|
64
|
-
execute: (args: any, ctx: any) => Promise<string>
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Wrap a delegation toolset with the `PANTHEON_DELEGATION=off` kill-switch.
|
|
69
|
-
* Every entry exposing `execute()` is guarded (throws when disabled);
|
|
70
|
-
* non-tool entries (e.g. legacy `finalizeDelegation`) pass through
|
|
71
|
-
* untouched — the completion observer must keep working so children never
|
|
72
|
-
* orphan. Native tools already guard internally; wrapping them again is a
|
|
73
|
-
* harmless double-check. Returns a new object; the input is untouched.
|
|
74
|
-
*/
|
|
75
|
-
export function withDelegationKillSwitch<T extends object>(
|
|
76
|
-
toolset: T,
|
|
77
|
-
env: Record<string, string | undefined> = process.env,
|
|
78
|
-
): T {
|
|
79
|
-
const out: Record<string, unknown> = {}
|
|
80
|
-
for (const [key, value] of Object.entries(toolset)) {
|
|
81
|
-
const tool = value as Partial<KillSwitchableTool>
|
|
82
|
-
if (tool !== null && typeof tool === 'object' && typeof tool.execute === 'function') {
|
|
83
|
-
const execute = tool.execute.bind(tool)
|
|
84
|
-
out[key] = {
|
|
85
|
-
...(tool as Record<string, unknown>),
|
|
86
|
-
// biome-ignore lint/suspicious/noExplicitAny: passthrough to the wrapped tool
|
|
87
|
-
execute: async (args: any, ctx: any) => {
|
|
88
|
-
if (!isDelegationEnabled(env)) {
|
|
89
|
-
throw new Error('pantheon delegation disabled (PANTHEON_DELEGATION=off)')
|
|
90
|
-
}
|
|
91
|
-
return execute(args, ctx)
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
out[key] = value
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return out as T
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ─── Args schemas (same contract as the legacy toolset) ────────────────
|
|
102
|
-
|
|
103
|
-
const delegateArgs = {
|
|
104
|
-
prompt: z.string().min(1).describe('Task prompt delivered to the background agent.'),
|
|
105
|
-
agent: z.string().min(1).describe('Agent name, e.g. "apollo" or "hermes".'),
|
|
106
|
-
description: z.string().optional().describe('Human-readable description shown on the job board.'),
|
|
107
|
-
read_only: z.boolean().optional().describe('Advisory flag for Phase 4 read-only enforcement.'),
|
|
108
|
-
model: z
|
|
109
|
-
.string()
|
|
110
|
-
.optional()
|
|
111
|
-
.describe(
|
|
112
|
-
'Explicit model for the child session (provider/model, e.g. "opencode/deepseek-v4-flash-free").',
|
|
113
|
-
),
|
|
114
|
-
} satisfies z.ZodRawShape
|
|
115
|
-
|
|
116
|
-
const readArgs = {
|
|
117
|
-
id: z.string().min(1).describe('Job alias (e.g. "apo-1") or task ID to read.'),
|
|
118
|
-
} satisfies z.ZodRawShape
|
|
119
|
-
|
|
120
|
-
const listArgs = {} satisfies z.ZodRawShape
|
|
121
|
-
|
|
122
|
-
export interface ToolContextLike {
|
|
123
|
-
sessionID: string
|
|
124
|
-
agent?: string
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ─── Session task function ─────────────────────────────────────────────
|
|
128
|
-
|
|
129
|
-
export interface SessionTaskAdapterOptions {
|
|
130
|
-
client: DelegationClient
|
|
131
|
-
board: BackgroundJobBoard
|
|
132
|
-
outputDir?: string
|
|
133
|
-
/** Bound for the promptAsync acceptance request (default: 60_000). */
|
|
134
|
-
promptTimeoutMs?: number
|
|
135
|
-
/** Bound for observing the terminal transition (default: 900_000). */
|
|
136
|
-
settleTimeoutMs?: number
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Split `provider/model` into the `{ id, providerID }` ref session.create expects. */
|
|
140
|
-
export function parseModelRef(model: string): { id: string; providerID: string } | undefined {
|
|
141
|
-
const idx = model.indexOf('/')
|
|
142
|
-
if (idx <= 0 || idx === model.length - 1) return undefined
|
|
143
|
-
return { providerID: model.slice(0, idx), id: model.slice(idx + 1) }
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Text after `## Output` (cut before a trailing `**Error**:` line). */
|
|
147
|
-
export function extractOutputSection(md: string | undefined): string {
|
|
148
|
-
if (md === undefined) return ''
|
|
149
|
-
const marker = '## Output'
|
|
150
|
-
const at = md.indexOf(marker)
|
|
151
|
-
const body = at < 0 ? md : md.slice(at + marker.length)
|
|
152
|
-
const errAt = body.indexOf('\n**Error**:')
|
|
153
|
-
return (errAt < 0 ? body : body.slice(0, errAt)).trim()
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async function promptAccept(
|
|
157
|
-
client: DelegationClient,
|
|
158
|
-
taskID: string,
|
|
159
|
-
agent: string,
|
|
160
|
-
prompt: string,
|
|
161
|
-
model: string | undefined,
|
|
162
|
-
signal: AbortSignal | undefined,
|
|
163
|
-
timeoutMs: number,
|
|
164
|
-
): Promise<void> {
|
|
165
|
-
const modelRef = model !== undefined ? parseModelRef(model) : undefined
|
|
166
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
167
|
-
try {
|
|
168
|
-
await Promise.race([
|
|
169
|
-
client.session
|
|
170
|
-
.promptAsync({
|
|
171
|
-
path: { id: taskID },
|
|
172
|
-
body: {
|
|
173
|
-
agent,
|
|
174
|
-
...(modelRef !== undefined ? { model: modelRef } : {}),
|
|
175
|
-
parts: [{ type: 'text', text: prompt }],
|
|
176
|
-
},
|
|
177
|
-
...(signal !== undefined ? { signal } : {}),
|
|
178
|
-
})
|
|
179
|
-
.then(
|
|
180
|
-
() => undefined,
|
|
181
|
-
(error: unknown) => {
|
|
182
|
-
throw new Error(
|
|
183
|
-
`promptAsync rejected: ${error instanceof Error ? error.message : String(error)}`,
|
|
184
|
-
)
|
|
185
|
-
},
|
|
186
|
-
),
|
|
187
|
-
new Promise<never>((_, reject) => {
|
|
188
|
-
timer = setTimeout(
|
|
189
|
-
() => reject(new Error(`promptAsync timed out after ${timeoutMs}ms`)),
|
|
190
|
-
timeoutMs,
|
|
191
|
-
)
|
|
192
|
-
timer.unref?.()
|
|
193
|
-
}),
|
|
194
|
-
])
|
|
195
|
-
} finally {
|
|
196
|
-
if (timer !== undefined) clearTimeout(timer)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Build the task()-shaped function over a V1 session client.
|
|
202
|
-
* The child session MUST already exist with id == input.taskID (created by
|
|
203
|
-
* the tool wrapper before manager.launch so both share one board record).
|
|
204
|
-
*/
|
|
205
|
-
export function createSessionTaskFn(options: SessionTaskAdapterOptions): NativeTaskFn {
|
|
206
|
-
const {
|
|
207
|
-
client,
|
|
208
|
-
board,
|
|
209
|
-
outputDir = DELEGATION_DEFAULTS.outputDir,
|
|
210
|
-
promptTimeoutMs = 60_000,
|
|
211
|
-
settleTimeoutMs = DELEGATION_DEFAULTS.timeoutMs,
|
|
212
|
-
} = options
|
|
213
|
-
return async ({ agent, prompt, model, signal, taskID }) => {
|
|
214
|
-
await promptAccept(client, taskID, agent, prompt, model, signal, promptTimeoutMs)
|
|
215
|
-
const terminal = await board.waitForTerminal(taskID, settleTimeoutMs)
|
|
216
|
-
const md = await readDelegationReport(outputDir, terminal)
|
|
217
|
-
const content = extractOutputSection(md) || terminal.resultSummary || ''
|
|
218
|
-
return { content }
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// ─── Native toolset ────────────────────────────────────────────────────
|
|
223
|
-
|
|
224
|
-
export interface NativeDelegateWiring {
|
|
225
|
-
board: BackgroundJobBoard
|
|
226
|
-
client: DelegationClient
|
|
227
|
-
outputDir?: string
|
|
228
|
-
env?: Record<string, string | undefined>
|
|
229
|
-
isRootSession: (sessionID: string) => boolean
|
|
230
|
-
registerChildSession?: (sessionID: string, parentID: string) => void
|
|
231
|
-
registerReadOnlySession?: (
|
|
232
|
-
sessionID: string,
|
|
233
|
-
info: { agent: string; readOnlyFlag?: boolean },
|
|
234
|
-
) => void
|
|
235
|
-
isReadOnlyAgent?: (agent: string) => boolean
|
|
236
|
-
/** Per-agent model override in `provider/model` form (preset/routing). */
|
|
237
|
-
agentModel?: (agent: string) => string | undefined
|
|
238
|
-
managerOptions?: Partial<
|
|
239
|
-
Omit<DelegateManagerOptions, 'board' | 'task' | 'parentSessionID' | 'env'>
|
|
240
|
-
>
|
|
241
|
-
stepCap?: StepCapTracker
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export interface NativeDelegateToolset {
|
|
245
|
-
pantheon_delegate: {
|
|
246
|
-
description: string
|
|
247
|
-
args: typeof delegateArgs
|
|
248
|
-
execute(args: z.infer<z.ZodObject<typeof delegateArgs>>, ctx: ToolContextLike): Promise<string>
|
|
249
|
-
}
|
|
250
|
-
pantheon_delegation_read: {
|
|
251
|
-
description: string
|
|
252
|
-
args: typeof readArgs
|
|
253
|
-
execute(args: z.infer<z.ZodObject<typeof readArgs>>, ctx: ToolContextLike): Promise<string>
|
|
254
|
-
}
|
|
255
|
-
pantheon_delegation_list: {
|
|
256
|
-
description: string
|
|
257
|
-
args: typeof listArgs
|
|
258
|
-
execute(args: z.infer<z.ZodObject<typeof listArgs>>, ctx: ToolContextLike): Promise<string>
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function firstLine(text: string, max = 60): string {
|
|
263
|
-
const line =
|
|
264
|
-
(text ?? '')
|
|
265
|
-
.split('\n')
|
|
266
|
-
.map((l) => l.trim())
|
|
267
|
-
.find((l) => l !== '') ?? ''
|
|
268
|
-
return line.length > max ? `${line.slice(0, max - 1)}…` : line
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function createNativeDelegateTools(wiring: NativeDelegateWiring): NativeDelegateToolset {
|
|
272
|
-
const {
|
|
273
|
-
board,
|
|
274
|
-
client,
|
|
275
|
-
outputDir = DELEGATION_DEFAULTS.outputDir,
|
|
276
|
-
env = process.env,
|
|
277
|
-
isRootSession,
|
|
278
|
-
registerChildSession,
|
|
279
|
-
registerReadOnlySession,
|
|
280
|
-
isReadOnlyAgent,
|
|
281
|
-
agentModel,
|
|
282
|
-
managerOptions = {},
|
|
283
|
-
stepCap,
|
|
284
|
-
} = wiring
|
|
285
|
-
const task = createSessionTaskFn({
|
|
286
|
-
client,
|
|
287
|
-
board,
|
|
288
|
-
outputDir,
|
|
289
|
-
...(managerOptions.timeoutMs !== undefined
|
|
290
|
-
? { promptTimeoutMs: managerOptions.timeoutMs }
|
|
291
|
-
: {}),
|
|
292
|
-
})
|
|
293
|
-
// One thin manager per parent session; the board is shared.
|
|
294
|
-
const managers = new Map<string, DelegateManager>()
|
|
295
|
-
function managerFor(parentSessionID: string): DelegateManager {
|
|
296
|
-
let manager = managers.get(parentSessionID)
|
|
297
|
-
if (!manager) {
|
|
298
|
-
manager = createDelegateManager({
|
|
299
|
-
...managerOptions,
|
|
300
|
-
board,
|
|
301
|
-
task,
|
|
302
|
-
parentSessionID,
|
|
303
|
-
env,
|
|
304
|
-
...(stepCap !== undefined ? { stepCap } : {}),
|
|
305
|
-
})
|
|
306
|
-
managers.set(parentSessionID, manager)
|
|
307
|
-
}
|
|
308
|
-
return manager
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
pantheon_delegate: {
|
|
313
|
-
description:
|
|
314
|
-
'Dispatch a background agent as a child session (native task() semantics: the call returns only after the verified result). Returns one line `[pantheon:<alias>] <agent> — <summary> — <state>`.',
|
|
315
|
-
args: delegateArgs,
|
|
316
|
-
execute: async (args, ctx) => {
|
|
317
|
-
if (!isDelegationEnabled(env)) {
|
|
318
|
-
throw new Error('pantheon delegation disabled (PANTHEON_DELEGATION=off)')
|
|
319
|
-
}
|
|
320
|
-
if (!isRootSession(ctx.sessionID)) {
|
|
321
|
-
throw new Error(
|
|
322
|
-
`pantheon_delegate rejected: session ${ctx.sessionID} is a sub-session — only root sessions can delegate`,
|
|
323
|
-
)
|
|
324
|
-
}
|
|
325
|
-
const model = args.model ?? agentModel?.(args.agent)
|
|
326
|
-
let childID: string
|
|
327
|
-
try {
|
|
328
|
-
const modelRef = model !== undefined ? parseModelRef(model) : undefined
|
|
329
|
-
const created = await client.session.create({
|
|
330
|
-
body: {
|
|
331
|
-
parentID: ctx.sessionID,
|
|
332
|
-
title: args.description ?? firstLine(args.prompt),
|
|
333
|
-
...(modelRef !== undefined ? { model: modelRef } : {}),
|
|
334
|
-
},
|
|
335
|
-
})
|
|
336
|
-
childID = created.id
|
|
337
|
-
} catch (err) {
|
|
338
|
-
const reason = err instanceof Error ? err.message : String(err)
|
|
339
|
-
return `pantheon_delegate failed: session.create rejected: ${reason}`
|
|
340
|
-
}
|
|
341
|
-
registerChildSession?.(childID, ctx.sessionID)
|
|
342
|
-
if (args.read_only === true || isReadOnlyAgent?.(args.agent) === true) {
|
|
343
|
-
const info: { agent: string; readOnlyFlag?: boolean } = { agent: args.agent }
|
|
344
|
-
if (args.read_only !== undefined) info.readOnlyFlag = args.read_only
|
|
345
|
-
registerReadOnlySession?.(childID, info)
|
|
346
|
-
}
|
|
347
|
-
const receipt = await managerFor(ctx.sessionID).launch({
|
|
348
|
-
agent: args.agent,
|
|
349
|
-
prompt: args.prompt,
|
|
350
|
-
...(args.description !== undefined ? { description: args.description } : {}),
|
|
351
|
-
...(args.model !== undefined ? { model: args.model } : {}),
|
|
352
|
-
taskID: childID,
|
|
353
|
-
})
|
|
354
|
-
return receipt.line
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
pantheon_delegation_read: {
|
|
358
|
-
description:
|
|
359
|
-
'Block until a background delegation finishes, then return its verified report markdown and mark the job reconciled.',
|
|
360
|
-
args: readArgs,
|
|
361
|
-
execute: async (args, ctx) => {
|
|
362
|
-
if (!isDelegationEnabled(env)) {
|
|
363
|
-
throw new Error('pantheon delegation disabled (PANTHEON_DELEGATION=off)')
|
|
364
|
-
}
|
|
365
|
-
return managerFor(ctx.sessionID).read(args.id)
|
|
366
|
-
},
|
|
367
|
-
},
|
|
368
|
-
pantheon_delegation_list: {
|
|
369
|
-
description: 'List background delegations with [pantheon:<alias>] / [native] tags per line.',
|
|
370
|
-
args: listArgs,
|
|
371
|
-
execute: async (_args, ctx) => {
|
|
372
|
-
if (!isDelegationEnabled(env)) {
|
|
373
|
-
throw new Error('pantheon delegation disabled (PANTHEON_DELEGATION=off)')
|
|
374
|
-
}
|
|
375
|
-
const lines = await managerFor(ctx.sessionID).list()
|
|
376
|
-
return lines.length > 0 ? lines.join('\n') : 'No delegations.'
|
|
377
|
-
},
|
|
378
|
-
},
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ─── Plugin native branch (WS1, PR #94) ─────────────────────────────────
|
|
383
|
-
//
|
|
384
|
-
// Extracted verbatim from the `delegateMode === 'native'` branch in
|
|
385
|
-
// src/plugin.ts so the real mounting (model lowercasing, read-only set,
|
|
386
|
-
// conditional managerOptions, finalizeDelegation passthrough) is unit
|
|
387
|
-
// testable with fakes — importing plugin.ts itself would pull module-level
|
|
388
|
-
// singletons (board, timers, logger). The plugin calls this helper; the
|
|
389
|
-
// wiring test calls the same helper with the same shapes.
|
|
390
|
-
|
|
391
|
-
/** Raw materials the plugin owns for the native branch. */
|
|
392
|
-
export interface PluginNativeDelegationWiring {
|
|
393
|
-
board: BackgroundJobBoard
|
|
394
|
-
client: DelegationClient
|
|
395
|
-
isRootSession: (sessionID: string) => boolean
|
|
396
|
-
registerChildSession: (sessionID: string, parentID: string) => void
|
|
397
|
-
registerReadOnlySession: (
|
|
398
|
-
sessionID: string,
|
|
399
|
-
info: { agent: string; readOnlyFlag?: boolean },
|
|
400
|
-
) => void
|
|
401
|
-
readOnlyAgents: ReadonlySet<string>
|
|
402
|
-
agentModels: Record<string, string | undefined>
|
|
403
|
-
stepCap: StepCapTracker
|
|
404
|
-
wallClockTimeoutMs?: number
|
|
405
|
-
finalizeDelegation: DelegationToolset['finalizeDelegation']
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/** Native toolset + the legacy completion observer (never kill-switched). */
|
|
409
|
-
export type PluginNativeDelegation = NativeDelegateToolset & {
|
|
410
|
-
finalizeDelegation: DelegationToolset['finalizeDelegation']
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export function buildPluginNativeDelegation(
|
|
414
|
-
wiring: PluginNativeDelegationWiring,
|
|
415
|
-
): PluginNativeDelegation {
|
|
416
|
-
const {
|
|
417
|
-
board,
|
|
418
|
-
client,
|
|
419
|
-
isRootSession,
|
|
420
|
-
registerChildSession,
|
|
421
|
-
registerReadOnlySession,
|
|
422
|
-
readOnlyAgents,
|
|
423
|
-
agentModels,
|
|
424
|
-
stepCap,
|
|
425
|
-
wallClockTimeoutMs,
|
|
426
|
-
finalizeDelegation,
|
|
427
|
-
} = wiring
|
|
428
|
-
return {
|
|
429
|
-
...createNativeDelegateTools({
|
|
430
|
-
board,
|
|
431
|
-
client,
|
|
432
|
-
isRootSession,
|
|
433
|
-
registerChildSession,
|
|
434
|
-
registerReadOnlySession,
|
|
435
|
-
isReadOnlyAgent: (agent) => readOnlyAgents.has(agent.toLowerCase()),
|
|
436
|
-
agentModel: (agent) => agentModels[agent.toLowerCase()],
|
|
437
|
-
stepCap,
|
|
438
|
-
...(wallClockTimeoutMs !== undefined
|
|
439
|
-
? { managerOptions: { timeoutMs: wallClockTimeoutMs } }
|
|
440
|
-
: {}),
|
|
441
|
-
}),
|
|
442
|
-
finalizeDelegation,
|
|
443
|
-
}
|
|
444
|
-
}
|