pantheon-opencode 1.5.0-beta.9 → 1.5.0
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/AGENTS.md +14 -63
- package/CHANGELOG.md +304 -0
- package/README.md +14 -27
- package/README.pt-BR.md +5 -6
- package/commands/pantheon.md +1 -35
- package/package.json +1 -2
- package/plugin.json +1 -1
- package/pyproject.toml +2 -5
- package/scripts/doctor.mjs +32 -3
- package/scripts/install/plugin.mjs +9 -12
- package/scripts/mcp_persistence_server.py +1 -1
- package/scripts/sync-tui.mjs +8 -11
- package/src/agents/apollo.md +1 -0
- package/src/agents/gaia.md +1 -0
- package/src/agents/zeus.md +9 -52
- 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/mcp_persistence_server.py +1 -1
- package/src/mcp/toon_codec.py +2 -4
- package/src/pantheon/background-job-board.ts +4 -4
- package/src/pantheon/{delegation-compaction.ts → compaction-context.ts} +10 -100
- package/src/pantheon/cost-command.ts +1 -2
- package/src/pantheon/delegation-enforce.ts +59 -14
- 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 +2 -2
- 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/task-result-guard.ts +2 -3
- package/src/pantheon/todo-enforcer.ts +3 -3
- 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 +61 -245
- package/src/plugins/pantheon-hooks.ts +1 -1
- package/src/plugins/tui/dist/tui.d.ts +212 -81
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +475 -254
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +2 -2
- package/src/plugins/tui/src/index.tsx +667 -269
- 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 -85
- 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/compaction-handoff.ts +0 -304
- package/src/pantheon/cost-tracker.ts +0 -108
- package/src/pantheon/delegate-manager.ts +0 -766
- package/src/pantheon/delegation-classifier.ts +0 -307
- package/src/pantheon/delegation-finalize.ts +0 -406
- package/src/pantheon/delegation-notify.ts +0 -212
- package/src/pantheon/delegation-toast.ts +0 -73
- package/src/pantheon/delegation.ts +0 -1337
- package/src/pantheon/dispatch-guard.ts +0 -109
- package/src/pantheon/native-probe.ts +0 -234
- package/src/pantheon/permission-eval.ts +0 -284
- package/src/pantheon/preemptive-compact.ts +0 -76
- package/src/pantheon/provider-capabilities.ts +0 -221
- package/src/pantheon/provider-stream-tracker.ts +0 -182
- package/src/pantheon/retry-policy.ts +0 -212
- package/src/pantheon/step-cap.ts +0 -110
- package/src/pantheon/token-opt.ts +0 -689
- package/src/pantheon/toon-codec.ts +0 -481
- package/src/pantheon/v2-events.ts +0 -148
- 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 -2853
package/src/pantheon/v2-hooks.ts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* V2 Session Hooks — Pantheon session hooks for the V2 plugin API.
|
|
3
|
-
*
|
|
4
|
-
* Provides two hook types:
|
|
5
|
-
* - `"prompt"` hook: Vision message interception (paste-and-ask). Intercepts
|
|
6
|
-
* user messages with pasted images and replaces them with text instructions
|
|
7
|
-
* or native vision descriptions.
|
|
8
|
-
* - `"context"` hook: Inject routing policy, active goals, and pending todos
|
|
9
|
-
* into the system context before each LLM call.
|
|
10
|
-
*
|
|
11
|
-
* These handlers are PURE — they depend only on structural interfaces.
|
|
12
|
-
*
|
|
13
|
-
* @module pantheon/v2-hooks
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type { BackgroundJobBoard } from './background-job-board.ts'
|
|
17
|
-
import { buildCompactionContext } from './delegation-compaction.ts'
|
|
18
|
-
import type { GoalStore } from './goal-loop.ts'
|
|
19
|
-
import { createPantheonLogger } from './logger.ts'
|
|
20
|
-
import type { TodoEnforcer } from './todo-enforcer.ts'
|
|
21
|
-
|
|
22
|
-
const log = createPantheonLogger({ module: 'pantheon-v2-hooks' })
|
|
23
|
-
|
|
24
|
-
// ─── Types ───────────────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Structural context event passed to the "context" hook.
|
|
28
|
-
* V2 API: `ctx.session.hook("context", (event) => { ... })`
|
|
29
|
-
*/
|
|
30
|
-
export interface V2ContextHookEvent {
|
|
31
|
-
sessionID: string
|
|
32
|
-
// Unknown: host beta may send string, string[], SystemPart objects
|
|
33
|
-
// ({type,text}), mixed arrays, or non-array. Narrow internally (fail-open).
|
|
34
|
-
system: unknown
|
|
35
|
-
generation: {
|
|
36
|
-
temperature?: number
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Structural prompt event passed to the "prompt" hook.
|
|
42
|
-
* V2 API: `ctx.session.hook("prompt", (event) => { ... })`
|
|
43
|
-
*/
|
|
44
|
-
export interface V2PromptHookEvent {
|
|
45
|
-
sessionID: string
|
|
46
|
-
message: unknown
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Dependencies for session hooks.
|
|
51
|
-
*/
|
|
52
|
-
export interface V2HookDeps {
|
|
53
|
-
board: BackgroundJobBoard
|
|
54
|
-
goalStore: GoalStore
|
|
55
|
-
todoEnforcer: TodoEnforcer
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ─── Context Hook ────────────────────────────────────────────────────────
|
|
59
|
-
|
|
60
|
-
const POLICY_MARKER = '<!-- pantheon-v2-policy -->'
|
|
61
|
-
const ROUTING_POLICY =
|
|
62
|
-
`${POLICY_MARKER}\nFollow Pantheon routing policy: delegate implementation work ` +
|
|
63
|
-
`to the named specialist and do not claim work was performed without verification.`
|
|
64
|
-
|
|
65
|
-
/** Default max compaction items (matching routing.yml background_delegation.max_compaction_items). */
|
|
66
|
-
const DEFAULT_MAX_COMPACTION_ITEMS = 10
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Extract comparable text from a system entry: plain string or
|
|
70
|
-
* SystemPart-like object ({type,text}). Returns null for junk (null/number).
|
|
71
|
-
* Runtime sniffing: pinned SDK (@opencode-ai/plugin 1.18.18) only knows
|
|
72
|
-
* string/string[] — host beta sends objects. Never throws (fail-open).
|
|
73
|
-
*/
|
|
74
|
-
function systemEntryText(entry: unknown): string | null {
|
|
75
|
-
try {
|
|
76
|
-
if (typeof entry === 'string') return entry
|
|
77
|
-
if (
|
|
78
|
-
typeof entry === 'object' &&
|
|
79
|
-
entry !== null &&
|
|
80
|
-
typeof (entry as { text?: unknown }).text === 'string'
|
|
81
|
-
) {
|
|
82
|
-
return (entry as { text: string }).text
|
|
83
|
-
}
|
|
84
|
-
return null
|
|
85
|
-
} catch {
|
|
86
|
-
return null
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function hasPolicyMarker(arr: unknown[]): boolean {
|
|
91
|
-
try {
|
|
92
|
-
return arr.some((s) => {
|
|
93
|
-
const t = systemEntryText(s)
|
|
94
|
-
return t?.includes(POLICY_MARKER) ?? false
|
|
95
|
-
})
|
|
96
|
-
} catch {
|
|
97
|
-
return false
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function usesObjectShape(arr: unknown[]): boolean {
|
|
102
|
-
try {
|
|
103
|
-
return arr.some(
|
|
104
|
-
(s) =>
|
|
105
|
-
typeof s === 'object' && s !== null && typeof (s as { text?: unknown }).text === 'string',
|
|
106
|
-
)
|
|
107
|
-
} catch {
|
|
108
|
-
return false
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function toSystemEntry(text: string, useObject: boolean): string | { type: 'text'; text: string } {
|
|
113
|
-
return useObject ? { type: 'text', text } : text
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Context hook handler — injects Pantheon policy, routing, and compaction
|
|
118
|
-
* state into the system context before each LLM call.
|
|
119
|
-
*
|
|
120
|
-
* Registered via: `ctx.session.hook("context", handler)`
|
|
121
|
-
*/
|
|
122
|
-
export function createV2ContextHookHandler(deps: V2HookDeps) {
|
|
123
|
-
return async (event: V2ContextHookEvent): Promise<void> => {
|
|
124
|
-
try {
|
|
125
|
-
// Beta 19192 hardening (defense-in-depth) + SystemPart sniffing: the
|
|
126
|
-
// host may send string[], SystemPart objects ({type,text}), mixed
|
|
127
|
-
// arrays, a single string, or a non-array system. Preserve the string
|
|
128
|
-
// path behavior; inject object shape when the array contains objects
|
|
129
|
-
// (else host schema validation fails on system[N]). Never throw.
|
|
130
|
-
const rawSystem: unknown = (event as { system?: unknown } | null | undefined)?.system
|
|
131
|
-
let system: unknown[]
|
|
132
|
-
if (typeof rawSystem === 'string') {
|
|
133
|
-
system = rawSystem.includes(POLICY_MARKER) ? [rawSystem] : [rawSystem, ROUTING_POLICY]
|
|
134
|
-
;(event as { system?: unknown }).system = system
|
|
135
|
-
} else if (Array.isArray(rawSystem)) {
|
|
136
|
-
// 1. Inject routing policy if not already present (string OU .text)
|
|
137
|
-
if (!hasPolicyMarker(rawSystem)) {
|
|
138
|
-
rawSystem.push(toSystemEntry(ROUTING_POLICY, usesObjectShape(rawSystem)))
|
|
139
|
-
}
|
|
140
|
-
system = rawSystem
|
|
141
|
-
} else {
|
|
142
|
-
// Non-string, non-array system: ignore silently (fail-open).
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 2. Inject compaction context (active goals, pending todos, delegations)
|
|
147
|
-
// Convert blocks to the same shape as the host array (object vs string).
|
|
148
|
-
const blocks = await buildCompactionContext(deps.board, {
|
|
149
|
-
sessionID: event.sessionID,
|
|
150
|
-
maxItems: DEFAULT_MAX_COMPACTION_ITEMS,
|
|
151
|
-
goals: {
|
|
152
|
-
enabled: true,
|
|
153
|
-
list: (sessionID: string) => deps.goalStore.list(sessionID),
|
|
154
|
-
},
|
|
155
|
-
todos: {
|
|
156
|
-
enabled: true,
|
|
157
|
-
list: (sessionID: string) => deps.todoEnforcer.listPendingTodos(sessionID),
|
|
158
|
-
},
|
|
159
|
-
})
|
|
160
|
-
if (blocks.length > 0) {
|
|
161
|
-
const useObject = usesObjectShape(system)
|
|
162
|
-
for (const b of blocks) {
|
|
163
|
-
system.push(useObject ? { type: 'text' as const, text: b } : b)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// 3. Apply conservative temperature for routing decisions
|
|
168
|
-
const generation = (event as { generation?: { temperature?: number } }).generation
|
|
169
|
-
if (generation != null && generation.temperature === undefined) {
|
|
170
|
-
generation.temperature = 0.2
|
|
171
|
-
}
|
|
172
|
-
} catch (err) {
|
|
173
|
-
log.warn('[Pantheon V2] Context hook failed:', err)
|
|
174
|
-
// Fail-open: never break the session
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ─── Prompt Hook ─────────────────────────────────────────────────────────
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Prompt hook handler — intercepts user messages for vision processing.
|
|
183
|
-
*
|
|
184
|
-
* Registered via: `ctx.session.hook("prompt", handler)`
|
|
185
|
-
*
|
|
186
|
-
* Note: The full vision handler is complex (file I/O, provider API calls).
|
|
187
|
-
* This is a minimal adapter that delegates to the V1 vision handler
|
|
188
|
-
* if available. Without vision infrastructure, it's a no-op.
|
|
189
|
-
*/
|
|
190
|
-
export function createV2PromptHookHandler(_deps: V2HookDeps) {
|
|
191
|
-
return async (event: V2PromptHookEvent): Promise<void> => {
|
|
192
|
-
// Vision interception is a complex subsystem (see src/pantheon/vision.ts).
|
|
193
|
-
// The V2 prompt hook delegates to the V1 vision handler via the plugin's
|
|
194
|
-
// integration point. Without V1 client access, this is a graceful no-op.
|
|
195
|
-
//
|
|
196
|
-
// TODO: Wire full vision handler when V2 provides SDK client access.
|
|
197
|
-
void event
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ─── Tool Hooks ──────────────────────────────────────────────────────────
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Tool execute.before hook — enforces read-only restrictions, command
|
|
205
|
-
* normalization, and todo preservation.
|
|
206
|
-
*
|
|
207
|
-
* Registered via: `ctx.tool.hook("execute.before", handler)`
|
|
208
|
-
*/
|
|
209
|
-
export interface V2ToolBeforeEvent {
|
|
210
|
-
tool: string
|
|
211
|
-
sessionID: string
|
|
212
|
-
callID: string
|
|
213
|
-
args?: unknown
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function createV2ToolBeforeHookHandler(
|
|
217
|
-
enforcementGuard: (
|
|
218
|
-
input: { tool: string; sessionID: string; callID: string },
|
|
219
|
-
output?: { args?: unknown },
|
|
220
|
-
) => void,
|
|
221
|
-
commandNormalizer: (
|
|
222
|
-
input: { tool: string; sessionID: string; callID: string },
|
|
223
|
-
output?: { args?: unknown },
|
|
224
|
-
) => Promise<void>,
|
|
225
|
-
) {
|
|
226
|
-
return async (event: V2ToolBeforeEvent): Promise<void> => {
|
|
227
|
-
try {
|
|
228
|
-
enforcementGuard(
|
|
229
|
-
{ tool: event.tool, sessionID: event.sessionID, callID: event.callID },
|
|
230
|
-
{ args: event.args },
|
|
231
|
-
)
|
|
232
|
-
await commandNormalizer(
|
|
233
|
-
{ tool: event.tool, sessionID: event.sessionID, callID: event.callID },
|
|
234
|
-
{ args: event.args },
|
|
235
|
-
)
|
|
236
|
-
} catch (err) {
|
|
237
|
-
log.warn('[Pantheon V2] Tool before-hook failed:', err)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Tool execute.after hook — augments read output with hashline tags
|
|
244
|
-
* and applies context sandbox truncation.
|
|
245
|
-
*
|
|
246
|
-
* Registered via: `ctx.tool.hook("execute.after", handler)`
|
|
247
|
-
*/
|
|
248
|
-
export interface V2ToolAfterEvent {
|
|
249
|
-
tool: string
|
|
250
|
-
sessionID: string
|
|
251
|
-
callID: string
|
|
252
|
-
args?: unknown
|
|
253
|
-
// The host may send a non-string output (shape drift on beta). Keep this
|
|
254
|
-
// honest as unknown and narrow with typeof at the use site — never assume.
|
|
255
|
-
output: unknown
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export function createV2ToolAfterHookHandler(
|
|
259
|
-
readEnhancer: (
|
|
260
|
-
input: { tool: string; sessionID: string; callID: string; args?: unknown },
|
|
261
|
-
output: { output: string },
|
|
262
|
-
) => Promise<void>,
|
|
263
|
-
sandboxHandler: (
|
|
264
|
-
input: { tool: string; sessionID: string; callID: string; args?: unknown },
|
|
265
|
-
output: { output: string },
|
|
266
|
-
) => Promise<void>,
|
|
267
|
-
) {
|
|
268
|
-
return async (event: V2ToolAfterEvent): Promise<void> => {
|
|
269
|
-
try {
|
|
270
|
-
const input = {
|
|
271
|
-
tool: event.tool,
|
|
272
|
-
sessionID: event.sessionID,
|
|
273
|
-
callID: event.callID,
|
|
274
|
-
args: event.args,
|
|
275
|
-
}
|
|
276
|
-
// Narrow the honest unknown: only strings flow through the enhancers.
|
|
277
|
-
// Non-string output is left untouched (fail-open, preserve host shape).
|
|
278
|
-
if (typeof event.output !== 'string') return
|
|
279
|
-
const output = { output: event.output }
|
|
280
|
-
await sandboxHandler(input, output)
|
|
281
|
-
await readEnhancer(input, output)
|
|
282
|
-
event.output = output.output
|
|
283
|
-
} catch (err) {
|
|
284
|
-
log.warn('[Pantheon V2] Tool after-hook failed:', err)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* V2 Tool Definitions — the 9 Pantheon orchestration tools adapted for the
|
|
3
|
-
* V2 plugin API. Each tool wraps existing V1 infrastructure (delegation,
|
|
4
|
-
* goal-loop, hashline, cost-command, model-command) into a unified
|
|
5
|
-
* `{ name, description, input, execute }` shape compatible with V2's
|
|
6
|
-
* `ctx.tool.transform()`.
|
|
7
|
-
*
|
|
8
|
-
* These definitions are PURE — they don't import any V1 plugin types.
|
|
9
|
-
* The execute functions use a structural `ToolContextLike` surface that
|
|
10
|
-
* both V1 and V2 can satisfy.
|
|
11
|
-
*
|
|
12
|
-
* @module pantheon/v2-tool-definitions
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import type { z } from 'zod'
|
|
16
|
-
import type { CostCommand } from './cost-command.ts'
|
|
17
|
-
import type { DelegationToolset } from './delegation.ts'
|
|
18
|
-
import type { GoalToolset } from './goal-loop.ts'
|
|
19
|
-
import type { ModelToolset } from './model-command.ts'
|
|
20
|
-
|
|
21
|
-
// ─── Structural Context ──────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Minimal structural context passed to V2 tool execute functions.
|
|
25
|
-
* Matches V1's ToolContextLike but avoids importing V1 types.
|
|
26
|
-
*/
|
|
27
|
-
export interface V2ToolContext {
|
|
28
|
-
sessionID: string
|
|
29
|
-
directory?: string
|
|
30
|
-
worktree?: string
|
|
31
|
-
agent?: string
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* A V2-compatible tool definition: name + description + JSON-Schema input
|
|
36
|
-
* + async execute function.
|
|
37
|
-
*/
|
|
38
|
-
export interface V2ToolDefinition {
|
|
39
|
-
name: string
|
|
40
|
-
description: string
|
|
41
|
-
input: {
|
|
42
|
-
type: 'object'
|
|
43
|
-
properties: Record<string, unknown>
|
|
44
|
-
required?: string[]
|
|
45
|
-
}
|
|
46
|
-
execute: (input: Record<string, unknown>, context: V2ToolContext) => Promise<string>
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ─── Zod → JSON-Schema Helpers ──────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Convert a zod ZodRawShape to a minimal JSON-Schema properties object.
|
|
53
|
-
* Uses safe introspection: checks for `.describe()` and basic shape keys.
|
|
54
|
-
* Falls back to `{ type: "string" }` for unrecognizable schemas.
|
|
55
|
-
*/
|
|
56
|
-
function zodShapeToJsonSchema(
|
|
57
|
-
shape: z.ZodRawShape,
|
|
58
|
-
required: string[] = [],
|
|
59
|
-
): { properties: Record<string, unknown>; required?: string[] } {
|
|
60
|
-
const properties: Record<string, unknown> = {}
|
|
61
|
-
for (const [key, schema] of Object.entries(shape)) {
|
|
62
|
-
const prop: Record<string, unknown> = {}
|
|
63
|
-
|
|
64
|
-
// Get description via .describe() if available
|
|
65
|
-
try {
|
|
66
|
-
const schemaAsObj = schema as unknown as Record<string, unknown>
|
|
67
|
-
const describeFn = schemaAsObj.describe
|
|
68
|
-
if (typeof describeFn === 'function') {
|
|
69
|
-
const desc = (describeFn as () => string).call(schemaAsObj)
|
|
70
|
-
if (typeof desc === 'string' && desc !== '') {
|
|
71
|
-
prop.description = desc
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
// Some zod types may throw on describe()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Detect type from constructor name or _def.type (works across zod versions)
|
|
79
|
-
const typeName = schema.constructor?.name ?? ''
|
|
80
|
-
const defObj = (schema as unknown as { _def?: Record<string, unknown> })?._def
|
|
81
|
-
const defType = (defObj?.typeName as string) ?? ''
|
|
82
|
-
|
|
83
|
-
if (typeName.includes('ZodString') || defType === 'ZodString') {
|
|
84
|
-
prop.type = 'string'
|
|
85
|
-
} else if (typeName.includes('ZodNumber') || defType === 'ZodNumber') {
|
|
86
|
-
prop.type = 'number'
|
|
87
|
-
} else if (typeName.includes('ZodBoolean') || defType === 'ZodBoolean') {
|
|
88
|
-
prop.type = 'boolean'
|
|
89
|
-
} else if (typeName.includes('ZodEnum') || defType === 'ZodEnum') {
|
|
90
|
-
prop.type = 'string'
|
|
91
|
-
// Safely extract enum values
|
|
92
|
-
try {
|
|
93
|
-
const schemaObj = schema as unknown as Record<string, unknown>
|
|
94
|
-
const defObj = schemaObj._def as Record<string, unknown> | undefined
|
|
95
|
-
const values = (schemaObj.options ?? defObj?.values) as string[] | undefined
|
|
96
|
-
if (Array.isArray(values)) prop.enum = values
|
|
97
|
-
} catch {
|
|
98
|
-
/* ignore */
|
|
99
|
-
}
|
|
100
|
-
} else if (typeName.includes('ZodArray') || defType === 'ZodArray') {
|
|
101
|
-
prop.type = 'array'
|
|
102
|
-
} else if (typeName.includes('ZodOptional') || defType === 'ZodOptional') {
|
|
103
|
-
// Optional wrappers — treat as string by default
|
|
104
|
-
prop.type = 'string'
|
|
105
|
-
} else {
|
|
106
|
-
prop.type = 'string'
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
properties[key] = prop
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const result: { properties: Record<string, unknown>; required?: string[] } = { properties }
|
|
113
|
-
if (required.length > 0) {
|
|
114
|
-
result.required = required
|
|
115
|
-
}
|
|
116
|
-
return result
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ─── V2 Tool Factory ────────────────────────────────────────────────────
|
|
120
|
-
|
|
121
|
-
export interface V2ToolFactoryDeps {
|
|
122
|
-
delegation: DelegationToolset
|
|
123
|
-
goalTools: GoalToolset
|
|
124
|
-
costCommand: CostCommand
|
|
125
|
-
modelCommand: ModelToolset
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Create all 9 V2 tool definitions from the V1 infrastructure.
|
|
130
|
-
* These can be registered via `ctx.tool.transform()` or used directly.
|
|
131
|
-
*/
|
|
132
|
-
export function createV2ToolDefinitions(deps: V2ToolFactoryDeps): V2ToolDefinition[] {
|
|
133
|
-
const { delegation, goalTools, costCommand, modelCommand } = deps
|
|
134
|
-
|
|
135
|
-
return [
|
|
136
|
-
// 1. pantheon_delegate
|
|
137
|
-
{
|
|
138
|
-
name: 'pantheon_delegate',
|
|
139
|
-
description: delegation.pantheon_delegate.description,
|
|
140
|
-
input: {
|
|
141
|
-
type: 'object',
|
|
142
|
-
...zodShapeToJsonSchema(delegation.pantheon_delegate.args as z.ZodRawShape, [
|
|
143
|
-
'prompt',
|
|
144
|
-
'agent',
|
|
145
|
-
]),
|
|
146
|
-
},
|
|
147
|
-
execute: async (input, ctx) => {
|
|
148
|
-
return delegation.pantheon_delegate.execute(
|
|
149
|
-
input as Parameters<typeof delegation.pantheon_delegate.execute>[0],
|
|
150
|
-
ctx,
|
|
151
|
-
)
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
// 2. pantheon_delegation_read
|
|
156
|
-
{
|
|
157
|
-
name: 'pantheon_delegation_read',
|
|
158
|
-
description: delegation.pantheon_delegation_read.description,
|
|
159
|
-
input: {
|
|
160
|
-
type: 'object',
|
|
161
|
-
...zodShapeToJsonSchema(delegation.pantheon_delegation_read.args as z.ZodRawShape, ['id']),
|
|
162
|
-
},
|
|
163
|
-
execute: async (input, ctx) => {
|
|
164
|
-
return delegation.pantheon_delegation_read.execute(
|
|
165
|
-
input as Parameters<typeof delegation.pantheon_delegation_read.execute>[0],
|
|
166
|
-
ctx,
|
|
167
|
-
)
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
// 3. pantheon_delegation_list
|
|
172
|
-
{
|
|
173
|
-
name: 'pantheon_delegation_list',
|
|
174
|
-
description: delegation.pantheon_delegation_list.description,
|
|
175
|
-
input: {
|
|
176
|
-
type: 'object',
|
|
177
|
-
properties: {},
|
|
178
|
-
},
|
|
179
|
-
execute: async (input, ctx) => {
|
|
180
|
-
return delegation.pantheon_delegation_list.execute(
|
|
181
|
-
input as Parameters<typeof delegation.pantheon_delegation_list.execute>[0],
|
|
182
|
-
ctx,
|
|
183
|
-
)
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
// 4. hashline_edit
|
|
188
|
-
{
|
|
189
|
-
name: 'hashline_edit',
|
|
190
|
-
description:
|
|
191
|
-
'Edit a file anchored by hashline refs (LINE#TAG) instead of raw line numbers. ' +
|
|
192
|
-
'Ops: replace (ref..endRef or single ref → lines), append/prepend (anchor ref → content), ' +
|
|
193
|
-
'delete (ref..endRef). ALL refs must be validated against the ORIGINAL file before anything ' +
|
|
194
|
-
'is written; on mismatch the tool returns an error with a re-tagged excerpt and a ' +
|
|
195
|
-
'Did-you-mean suggestion, and nothing is modified.',
|
|
196
|
-
input: {
|
|
197
|
-
type: 'object',
|
|
198
|
-
properties: {
|
|
199
|
-
file: { type: 'string', description: 'Absolute path to the file to edit.' },
|
|
200
|
-
edits: {
|
|
201
|
-
type: 'array',
|
|
202
|
-
description: 'List of edit operations to apply.',
|
|
203
|
-
items: {
|
|
204
|
-
type: 'object',
|
|
205
|
-
properties: {
|
|
206
|
-
op: {
|
|
207
|
-
type: 'string',
|
|
208
|
-
enum: ['replace', 'append', 'prepend', 'delete'],
|
|
209
|
-
description: 'The edit operation.',
|
|
210
|
-
},
|
|
211
|
-
ref: {
|
|
212
|
-
type: 'string',
|
|
213
|
-
description: 'Anchor ref "LINE#TAG" against the ORIGINAL snapshot.',
|
|
214
|
-
},
|
|
215
|
-
endRef: {
|
|
216
|
-
type: 'string',
|
|
217
|
-
description: 'Optional range end for replace/delete ("LINE#TAG").',
|
|
218
|
-
},
|
|
219
|
-
lines: {
|
|
220
|
-
type: 'array',
|
|
221
|
-
items: { type: 'string' },
|
|
222
|
-
description: 'Replacement lines (replace).',
|
|
223
|
-
},
|
|
224
|
-
content: {
|
|
225
|
-
type: 'string',
|
|
226
|
-
description: 'Text to insert (append/prepend; split on newlines).',
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
required: ['op', 'ref'],
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
required: ['file', 'edits'],
|
|
234
|
-
},
|
|
235
|
-
execute: async (input, ctx) => {
|
|
236
|
-
// hashline_edit uses a different execute signature — it takes (args, toolContext)
|
|
237
|
-
// where toolContext includes sessionID, directory, worktree
|
|
238
|
-
const { createHashlineEditTool } = await import('./hashline/tool.ts')
|
|
239
|
-
const tool = createHashlineEditTool()
|
|
240
|
-
const result = await tool.execute(
|
|
241
|
-
input as Parameters<typeof tool.execute>[0],
|
|
242
|
-
ctx as Parameters<typeof tool.execute>[1],
|
|
243
|
-
)
|
|
244
|
-
if (typeof result === 'string') return result
|
|
245
|
-
return result.output
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
// 5. pantheon_goal_create
|
|
250
|
-
{
|
|
251
|
-
name: 'pantheon_goal_create',
|
|
252
|
-
description: goalTools.pantheon_goal_create.description,
|
|
253
|
-
input: {
|
|
254
|
-
type: 'object',
|
|
255
|
-
...zodShapeToJsonSchema(goalTools.pantheon_goal_create.args as z.ZodRawShape, [
|
|
256
|
-
'objective',
|
|
257
|
-
]),
|
|
258
|
-
},
|
|
259
|
-
execute: async (input, ctx) => {
|
|
260
|
-
return goalTools.pantheon_goal_create.execute(
|
|
261
|
-
input as Parameters<typeof goalTools.pantheon_goal_create.execute>[0],
|
|
262
|
-
ctx,
|
|
263
|
-
)
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
// 6. pantheon_goal_get
|
|
268
|
-
{
|
|
269
|
-
name: 'pantheon_goal_get',
|
|
270
|
-
description: goalTools.pantheon_goal_get.description,
|
|
271
|
-
input: {
|
|
272
|
-
type: 'object',
|
|
273
|
-
properties: {},
|
|
274
|
-
},
|
|
275
|
-
execute: async (input, ctx) => {
|
|
276
|
-
return goalTools.pantheon_goal_get.execute(
|
|
277
|
-
input as Parameters<typeof goalTools.pantheon_goal_get.execute>[0],
|
|
278
|
-
ctx,
|
|
279
|
-
)
|
|
280
|
-
},
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
// 7. pantheon_goal_update
|
|
284
|
-
{
|
|
285
|
-
name: 'pantheon_goal_update',
|
|
286
|
-
description: goalTools.pantheon_goal_update.description,
|
|
287
|
-
input: {
|
|
288
|
-
type: 'object',
|
|
289
|
-
...zodShapeToJsonSchema(goalTools.pantheon_goal_update.args as z.ZodRawShape),
|
|
290
|
-
},
|
|
291
|
-
execute: async (input, ctx) => {
|
|
292
|
-
return goalTools.pantheon_goal_update.execute(
|
|
293
|
-
input as Parameters<typeof goalTools.pantheon_goal_update.execute>[0],
|
|
294
|
-
ctx,
|
|
295
|
-
)
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
|
|
299
|
-
// 8. pantheon_cost
|
|
300
|
-
{
|
|
301
|
-
name: 'pantheon_cost',
|
|
302
|
-
description: costCommand.pantheon_cost.description,
|
|
303
|
-
input: {
|
|
304
|
-
type: 'object',
|
|
305
|
-
...zodShapeToJsonSchema(costCommand.pantheon_cost.args as z.ZodRawShape),
|
|
306
|
-
},
|
|
307
|
-
execute: async (input, ctx) => {
|
|
308
|
-
return costCommand.pantheon_cost.execute(
|
|
309
|
-
input as Parameters<typeof costCommand.pantheon_cost.execute>[0],
|
|
310
|
-
ctx,
|
|
311
|
-
)
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
// 9. pantheon_model
|
|
316
|
-
{
|
|
317
|
-
name: 'pantheon_model',
|
|
318
|
-
description: modelCommand.pantheon_model.description,
|
|
319
|
-
input: {
|
|
320
|
-
type: 'object',
|
|
321
|
-
...zodShapeToJsonSchema(modelCommand.pantheon_model.args as z.ZodRawShape),
|
|
322
|
-
},
|
|
323
|
-
execute: async (input, ctx) => {
|
|
324
|
-
return modelCommand.pantheon_model.execute(
|
|
325
|
-
input as Parameters<typeof modelCommand.pantheon_model.execute>[0],
|
|
326
|
-
ctx,
|
|
327
|
-
)
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
]
|
|
331
|
-
}
|