/** * DshHookProvider — IHookProvider adapter mapping rolebox hook kinds onto * dsh extension points. * * Rolebox hook kinds (the lifecycle handlers produced by HookService for * opencode — see `src/core/services/hook-service.ts:buildHandlers`) map onto * the verified dsh extension points (`docs/dsh-plugin-contract.md` §3.5 * tools events, §4.1 session events) as follows: * * | rolebox hook kind | dsh extension point | status | * |-------------------|----------------------------------------------|--------| * | tool-before | `ctx.on("tools/pre-execute")` | mapped | * | tool-after | `ctx.on("tools/post-execute")` + `tools/result` | mapped | * | chat-message | `ctx.on("session/event")` (user/assistant appends) | mapped | * | system-transform | — none — | no-op | * | context | — none — | no-op | * | compaction | — none — | no-op | * * ## Documented no-ops (rolebox hook kinds with NO dsh equivalent) * * - **system-transform**: dsh composes the model-facing system prompt from * the mounted `systemPrompt` service (dsh-tools injects it, §3.1); there is * no per-turn prompt transform hook to attach to. The handler in * `getHandlers()` stays a no-op. Session-level injection now flows through * {@link DshSystemPromptAdapter} (`system-prompt.ts` — the `rolebox:role` * section + `rolebox:context` entry in the dsh `systemPrompt` registry, * resolved per-session via `context.agent.id`). Spawn-time context still * reaches subagents via the registrar's {@link DshSpawnContextProvider} seam. * - **context**: rolebox's `context` "hook" is a helper bundle * (`src/hooks/context.ts` — `collectAllFunctions`, `appendCorrection`, * `fetchLastAssistantText`) with no dsh event seam; its handler is a no-op. * - **compaction**: dsh compacts sessions through data-level * `surfaceOp: 'replace'` appends on the session log (§4.1); there is no * compaction lifecycle event. The handler is a no-op. * * Tool registration is NOT part of this provider: dsh tools are registered * through `ctx.tools.register(defineTool(...))`, owned by the parallel * `tool-factory.ts` adapter. `getHandlers().tool` is therefore an empty * record, kept for IHookProvider port conformance. * * All dsh types are structural (duck-typed). This module does NOT import * `@deepseek-ai/*` (or `@opencode-ai/*`). * * @module */ import type { IHookProvider } from "../../ports/hook-provider.ts"; import type { DshCordisContext } from "./event-bridge.ts"; /** * Normalized payload passed to a rolebox hook callback. Carries the dsh * event name it was produced from (`event`), the owning rolebox hook kind * (`hookKind`), and every enumerable field of the raw dsh event payload. */ export type DshHookPayload = Record & { /** The dsh event name that produced this payload. */ event: string; /** The rolebox hook kind this callback implements. */ hookKind: DshHookKind; }; /** The rolebox hook kinds this provider understands. */ export type DshHookKind = "system-transform" | "chat-message" | "tool-before" | "tool-after" | "context" | "compaction"; /** Callback signature for a rolebox hook kind. */ export type DshHookCallback = (payload: DshHookPayload) => void | Promise; /** Mapped rolebox hook callbacks — only kinds with a dsh equivalent are wired. */ export interface DshHookProviderOptions { /** `tool-before` → `tools/pre-execute`. */ toolBefore?: DshHookCallback; /** `tool-after` → `tools/post-execute` (mutate gate) and `tools/result` (frozen outcome). */ toolAfter?: DshHookCallback; /** `chat-message` → `session/event` (user/message + assistant/message appends). */ chatMessage?: DshHookCallback; } /** * Handler map returned by `getHandlers()`, keyed by rolebox hook kind. * Mapped kinds expose the listener registered on the dsh ctx; unmapped kinds * (`system-transform`, `context`, `compaction`) expose documented no-ops. */ export type DshHookHandlers = { "system-transform": DshHookCallback; "chat-message": DshHookCallback; "tool-before": DshHookCallback; "tool-after": DshHookCallback; "context": DshHookCallback; "compaction": DshHookCallback; /** Platform-native tool definitions — empty here; DshToolFactory owns registration. */ tool: Record; /** Unsubscribe every listener registered on the dsh ctx. */ dispose: () => void; }; /** * IHookProvider implementation that wires rolebox hook kinds onto dsh * extension points on a cordis context. * * On construction it registers `ctx.on(...)` listeners for the mapped dsh * events. `getHandlers()` exposes one handler per rolebox hook kind (the * registered listeners for mapped kinds, documented no-ops for unmapped * kinds), plus `tool` and `dispose`. */ export declare class DshHookProvider implements IHookProvider { private readonly ctx; private readonly options; private readonly _log; private readonly handlers; /** Cordis disposers returned by `ctx.on` — released by `dispose()`. */ private readonly disposers; /** * @param ctx - Structural cordis context (`ctx.on` / `ctx.emit`). * @param options - Mapped rolebox hook callbacks (tool-before, tool-after, * chat-message). Kinds with no dsh equivalent * (system-transform, context, compaction) are not accepted * and remain documented no-ops. */ constructor(ctx: DshCordisContext, options?: DshHookProviderOptions); /** * Return the assembled hook handlers, keyed by rolebox hook kind. */ getHandlers(): Record; /** * Unsubscribe every dsh event listener registered on the cordis ctx. * Idempotent — safe to call multiple times. */ dispose(): void; /** * Build the per-kind handler map. Mapped kinds invoke the user callback * with a normalized payload; unmapped kinds are documented no-ops. */ private buildHandlers; /** * Register the dsh event listeners for the mapped hook kinds. */ private wire; /** * Build a normalized payload record from raw dsh listener args. * * Event-aware. `session/event` delivers `(session, event)` in rc.6 * (`dsh-session/lib/types/index.d.ts:66`), so the session id is read from * arg0 and the sub-type/`seq`/`time`/`data` from arg1. Tool events keep the * tolerant `(payload)` / `(name, payload)` handling, and the outcome * argument of `tools/post-execute` / `tools/result` is surfaced as * `payload.result`. Common field aliases are normalized (`name` → `tool`, * `callId` → `callID`, `sessionId` → `sessionID`). */ private toPayload; /** * Normalize the rc.6 `session/event(session, event)` args. The session id * comes from arg0 (`Session.id`); the sub-type (`type`), `seq`, `time`, and * `data` come from arg1 (`SessionEvent`). A `Session` carries no `type`, so * merging arg0 as the event would leave `sessionEventType` undefined and * silently kill every `chat-message` hook. */ private mergeSessionEvent; /** * Tolerant tool-event arg handling: `(payload)` where payload is an object, * and `(name, payload)` where the tool name leads. */ private mergeToolArgs; /** * `tools/post-execute(exec, result, next)` and `tools/result(exec, result)` * carry the outcome as the SECOND argument; surface it under * `payload.result` so `tool-after` hooks can observe it. `next` is already * stripped by the listener for waterfall events. */ private mergeToolResult; } //# sourceMappingURL=hook-provider.d.ts.map