Skip to content

Commit 64e60eb

Browse files
authored
Merge pull request #288 from LeXwDeX/fix/hook-field-drift
fix(hook): apply prompt-hook timeout, surface dropped HookCommand fields
2 parents b02c727 + fabee48 commit 64e60eb

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

packages/opencode/src/hook/settings.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,16 @@ export function detectUnsupportedFields(
968968
for (const m of matchers) {
969969
for (const h of m.hooks ?? []) {
970970
if (h.shell !== undefined) unsupported.push({ field: "shell", value: h.shell, eventName })
971+
// issue #286 — schema-accepted but executor-dropped fields. Surfaced
972+
// here instead of silently swallowed: allowedEnvVars/statusMessage have
973+
// zero consumers anywhere; per-command `once` is never read (only the
974+
// entry-level _sessionEntry?.once is consumed). `timeout` is NOT
975+
// flagged — every handler type applies it (incl. prompt).
976+
if (h.allowedEnvVars !== undefined)
977+
unsupported.push({ field: "allowedEnvVars", value: h.allowedEnvVars, eventName })
978+
if (h.statusMessage !== undefined)
979+
unsupported.push({ field: "statusMessage", value: h.statusMessage, eventName })
980+
if (h.once !== undefined) unsupported.push({ field: "once", value: h.once, eventName })
971981
// `if` is implemented (condition-filter); async/asyncRewake implemented.
972982
// All 5 known types now have handlers; type-level unsupported set is empty by design.
973983
}
@@ -1537,10 +1547,19 @@ const promptHandler: HookHandler = {
15371547
schema: HookJSONOutputZodSchema,
15381548
} satisfies Parameters<typeof generateObject>[0]
15391549

1550+
// issue #286 — the header doc promises `timeout` for every hook type,
1551+
// but promptHandler was the only executor never applying it. Honor it
1552+
// with the same idiom as command/mcp/http; on expiry the TimeoutException
1553+
// is captured by Effect.exit below and degrades to the non-blocking warn.
1554+
const timeoutMs = entry.timeout ? entry.timeout * 1000 : DEFAULT_TIMEOUT_MS
1555+
15401556
const llmExit = yield* Effect.tryPromise({
15411557
try: () => generateObject(params).then((r) => r.object),
15421558
catch: (e) => e,
1543-
}).pipe(Effect.exit)
1559+
}).pipe(
1560+
Effect.timeout(timeoutMs),
1561+
Effect.exit,
1562+
)
15441563

15451564
if (llmExit._tag === "Failure") {
15461565
log.warn("prompt hook failed (non-blocking)", { error: String(llmExit.cause) })

packages/opencode/test/hook/warn-unsupported.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,16 @@ describe("detectUnsupportedFields", () => {
3838
expect(detectUnsupportedFields(undefined)).toEqual([])
3939
expect(detectUnsupportedFields({})).toEqual([])
4040
})
41+
42+
// GOAL-FP/issue #286: HookCommand fields accepted by the schema but dropped
43+
// by every executor must be surfaced, not silently swallowed. `timeout` for
44+
// type "prompt" is implemented (excluded here); allowedEnvVars/statusMessage
45+
// have zero consumers anywhere, and per-command `once` is never read (only
46+
// the entry-level _sessionEntry?.once is consumed).
47+
test("allowedEnvVars / statusMessage / per-command once are flagged (dropped by executors)", () => {
48+
const unsupported = detectUnsupportedFields(
49+
hooks({ allowedEnvVars: ["FOO"], statusMessage: "hi", once: true }),
50+
)
51+
expect(unsupported.map((u) => u.field).sort()).toEqual(["allowedEnvVars", "once", "statusMessage"])
52+
})
4153
})

0 commit comments

Comments
 (0)