pantheon-opencode 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/README.md +54 -4
- package/opencode.json +16 -0
- package/package.json +1 -1
- package/scripts/doctor.mjs +80 -31
- package/scripts/hooks/log-session-start.sh +19 -1
- package/scripts/hooks/on-subagent-delegation-start.sh +116 -6
- package/scripts/hooks/on-subagent-delegation-stop.sh +391 -9
- package/scripts/hooks/scan-secrets.sh +61 -19
- package/scripts/hooks/validate-talos-scope.sh +7 -0
- package/scripts/validation-policy.mjs +18 -0
- package/src/agents/zeus.md +33 -5
- package/src/plugins/hook-runner.ts +44 -4
- package/src/plugins/pantheon-hooks.ts +1391 -41
- package/src/plugins/tui/dist/tui.js +1 -1
- package/src/plugins/tui/dist/tui.tsx +1 -1
- package/src/plugins/tui/package-lock.json +34 -5
- package/src/plugins/tui/package.json +1 -1
- package/src/plugins/tui/src/index.tsx +1 -1
|
@@ -98,21 +98,37 @@ export function runHook(
|
|
|
98
98
|
const scriptPath = path.join(opts.cwd ?? resolveHooksDir(), script)
|
|
99
99
|
const timeout = opts.timeout ?? HOOK_TIMEOUT_MS
|
|
100
100
|
|
|
101
|
+
let serializedPayload: string
|
|
102
|
+
try {
|
|
103
|
+
serializedPayload = JSON.stringify(payload)
|
|
104
|
+
} catch (err) {
|
|
105
|
+
// Fail closed: a scanner hook must not run without the payload it is
|
|
106
|
+
// supposed to inspect, and serialization errors must never reject.
|
|
107
|
+
return Promise.resolve({
|
|
108
|
+
code: 1,
|
|
109
|
+
stdout: '',
|
|
110
|
+
stderr: `payload serialization failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
111
|
+
signal: null,
|
|
112
|
+
timedOut: false,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
return new Promise<HookResult>((resolve) => {
|
|
102
117
|
const stdoutBuf: { value: string } = { value: '' }
|
|
103
118
|
const stderrBuf: { value: string } = { value: '' }
|
|
104
119
|
let settled = false
|
|
120
|
+
let timedOut = false
|
|
121
|
+
let timeoutHandle: NodeJS.Timeout | undefined
|
|
105
122
|
|
|
106
123
|
const settle = (result: HookResult) => {
|
|
107
124
|
if (!settled) {
|
|
108
125
|
settled = true
|
|
126
|
+
if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)
|
|
109
127
|
resolve(result)
|
|
110
128
|
}
|
|
111
129
|
}
|
|
112
130
|
|
|
113
131
|
const spawnOpts = {
|
|
114
|
-
timeout,
|
|
115
|
-
killSignal: 'SIGKILL' as const,
|
|
116
132
|
env: hookEnv(payload, opts.env),
|
|
117
133
|
// stdio:'pipe' is equivalent to ['pipe','pipe','pipe'] — it guarantees
|
|
118
134
|
// the child's stdin is OUR pipe, NEVER the opencode TUI's terminal
|
|
@@ -120,6 +136,10 @@ export function runHook(
|
|
|
120
136
|
// the opencode core hook timeout).
|
|
121
137
|
stdio: 'pipe' as const,
|
|
122
138
|
windowsHide: true,
|
|
139
|
+
// A process group lets the timeout kill shell descendants too. Without
|
|
140
|
+
// this, a shell hook can leave `sleep` holding stdout/stderr open after
|
|
141
|
+
// the shell itself receives SIGKILL, so the Promise would not settle.
|
|
142
|
+
...(process.platform !== 'win32' ? { detached: true } : {}),
|
|
123
143
|
...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
|
|
124
144
|
}
|
|
125
145
|
|
|
@@ -172,15 +192,35 @@ export function runHook(
|
|
|
172
192
|
stdout: stdoutBuf.value,
|
|
173
193
|
stderr: stderrBuf.value,
|
|
174
194
|
signal,
|
|
175
|
-
timedOut
|
|
195
|
+
timedOut,
|
|
176
196
|
})
|
|
177
197
|
})
|
|
178
198
|
|
|
199
|
+
timeoutHandle = setTimeout(() => {
|
|
200
|
+
timedOut = true
|
|
201
|
+
try {
|
|
202
|
+
// Negative PIDs target the process group created by `detached`.
|
|
203
|
+
if (process.platform !== 'win32' && child.pid !== undefined) {
|
|
204
|
+
process.kill(-child.pid, 'SIGKILL')
|
|
205
|
+
} else {
|
|
206
|
+
child.kill('SIGKILL')
|
|
207
|
+
}
|
|
208
|
+
} catch {
|
|
209
|
+
// The child may have exited between the timer and kill call. The
|
|
210
|
+
// close/error handlers still produce the structured result.
|
|
211
|
+
try {
|
|
212
|
+
child.kill('SIGKILL')
|
|
213
|
+
} catch {
|
|
214
|
+
// Hooks must never leak a kill failure to the plugin host.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}, timeout)
|
|
218
|
+
|
|
179
219
|
// Deliver the Claude Code protocol JSON on stdin. stdin.end() runs
|
|
180
220
|
// UNCONDITIONALLY — even for an empty payload {} or a script that never
|
|
181
221
|
// reads stdin — so the child can never block waiting for input and hit
|
|
182
222
|
// the runtime hook timeout (reported as "Hook X timed out after 30000ms").
|
|
183
|
-
child.stdin.write(
|
|
223
|
+
child.stdin.write(serializedPayload)
|
|
184
224
|
child.stdin.end()
|
|
185
225
|
})
|
|
186
226
|
}
|