@yanlinglabs/winter-agent-sdk 0.0.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.
@@ -0,0 +1,252 @@
1
+ import type { SpawnClaudeCodeProcess } from "./transport.js";
2
+ import type { PermissionMode, CanUseTool, HookEvent, HookCallbackMatcher } from "./permissions/types.js";
3
+ import type { SandboxSettingsConfig, McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, RuntimeAgentDefinition, SdkPluginConfig, SystemPromptOption, OutputFormat, SkillsOption, ProviderSelection, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig } from "./protocol/config.js";
4
+ import type { SettingSource } from "./settings/types.js";
5
+ import type { SessionStore } from "./store/session-store.js";
6
+ import { type BrandProfile } from "./brand.js";
7
+ export type { BrandProfile, BrandValidation } from "./brand.js";
8
+ export type { SdkPluginConfig, SystemPromptOption, OutputFormat, JsonSchemaOutputFormat, SkillsOption } from "./protocol/config.js";
9
+ export type { ProviderSelection, ProviderConnectionConfig, CredentialRef, ThinkingConfig, EffortLevel, AutoClassifierConfig, AdvisorConfig } from "./protocol/config.js";
10
+ export declare const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = "__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__";
11
+ export declare const DEFAULT_CONTEXT_WINDOW_TOKENS = 200000;
12
+ export declare const DEFAULT_COMPACTION_THRESHOLD = 0.92;
13
+ export declare const DEFAULT_PLANS_DIRECTORY: string;
14
+ export declare const DEFAULT_OUTPUT_STYLE = "default";
15
+ export declare const DEFAULT_PROVIDER_STALL_TIMEOUT_MS = 120000;
16
+ export declare const DEFAULT_KEYCHAIN_SERVICE: string;
17
+ export interface McpSdkServerConfigWithInstance extends McpSdkServerConfig {
18
+ instance: unknown;
19
+ }
20
+ export type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig | McpSSEServerConfig | McpSdkServerConfigWithInstance;
21
+ export type { McpServerToolPolicy, McpStdioServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig };
22
+ export interface WinterMcpServerInstance {
23
+ listTools(): Array<{
24
+ name: string;
25
+ description?: string;
26
+ inputSchema: Record<string, unknown>;
27
+ outputSchema?: Record<string, unknown>;
28
+ annotations?: {
29
+ readOnlyHint?: boolean;
30
+ destructiveHint?: boolean;
31
+ openWorldHint?: boolean;
32
+ title?: string;
33
+ idempotentHint?: boolean;
34
+ };
35
+ _meta?: Record<string, unknown>;
36
+ }>;
37
+ callTool(name: string, args: Record<string, unknown>): Promise<{
38
+ content: unknown[];
39
+ isError?: boolean;
40
+ }>;
41
+ }
42
+ export declare function isWinterMcpServerInstance(value: unknown): value is WinterMcpServerInstance;
43
+ export type AgentDefinition = Omit<RuntimeAgentDefinition, "permissionMode"> & {
44
+ permissionMode?: PermissionMode;
45
+ };
46
+ export interface Options {
47
+ model?: string;
48
+ permissionMode?: PermissionMode;
49
+ maxTurns?: number;
50
+ cwd?: string;
51
+ env?: Record<string, string>;
52
+ pathToClaudeCodeExecutable?: string;
53
+ spawnClaudeCodeProcess?: SpawnClaudeCodeProcess;
54
+ stderr?: (chunk: string) => void;
55
+ maxBufferSize?: number;
56
+ abortController?: AbortController;
57
+ sessionId?: string;
58
+ continue?: boolean;
59
+ resume?: string;
60
+ forkSession?: boolean;
61
+ resumeSessionAt?: string;
62
+ resumeDropsTurn?: boolean;
63
+ persistSession?: boolean;
64
+ allowedTools?: string[];
65
+ disallowedTools?: string[];
66
+ permissions?: {
67
+ allow?: string[];
68
+ ask?: string[];
69
+ deny?: string[];
70
+ disableBypassPermissionsMode?: boolean;
71
+ };
72
+ settingSources?: SettingSource[];
73
+ /**
74
+ * Phase 5 fix wave, C1: the MANAGED policy tiers, threaded to the runtime's own settings
75
+ * resolution. `managedSettings` is filtered restrictive-only there; `serverManagedSettings` is
76
+ * deliberately not (`sdk.d.ts:2838-2839`). Before this existed the pinned `managed` rule source had
77
+ * no producer in a live session at all.
78
+ */
79
+ managedSettings?: Record<string, unknown>;
80
+ serverManagedSettings?: Record<string, unknown>;
81
+ permissionPromptToolName?: string;
82
+ additionalDirectories?: string[];
83
+ sandbox?: SandboxSettingsConfig;
84
+ outputsDir?: string;
85
+ capabilities?: string[];
86
+ toolSearchEnabled?: boolean;
87
+ insideSubagent?: boolean;
88
+ familyMetadata?: {
89
+ taskNative?: boolean;
90
+ };
91
+ allowDangerouslySkipPermissions?: boolean;
92
+ canUseTool?: CanUseTool;
93
+ hooks?: Partial<Record<HookEvent, HookCallbackMatcher[]>>;
94
+ includeHookEvents?: boolean;
95
+ mcpServers?: Record<string, McpServerConfig>;
96
+ strictMcpConfig?: boolean;
97
+ toolAliases?: Record<string, string>;
98
+ agents?: Record<string, AgentDefinition>;
99
+ forwardSubagentText?: boolean;
100
+ onElicitation?: (request: {
101
+ serverName: string;
102
+ message: string;
103
+ mode?: "form" | "url";
104
+ url?: string;
105
+ elicitationId?: string;
106
+ requestedSchema?: Record<string, unknown>;
107
+ title?: string;
108
+ displayName?: string;
109
+ description?: string;
110
+ }, options: {
111
+ signal: AbortSignal;
112
+ requestId: string;
113
+ }) => Promise<{
114
+ action: "accept" | "decline" | "cancel";
115
+ content?: Record<string, unknown>;
116
+ } | null>;
117
+ systemPrompt?: SystemPromptOption;
118
+ plugins?: SdkPluginConfig[];
119
+ skills?: SkillsOption;
120
+ outputFormat?: OutputFormat;
121
+ enableFileCheckpointing?: boolean;
122
+ /**
123
+ * `sdk.d.ts:1672-1683`: an external session store the runtime MIRRORS transcript writes to. A
124
+ * DUAL-WRITE MIRROR, never a replacement -- local writes always still happen, which is exactly why
125
+ * the checkpointing rejection below is about backup blobs specifically rather than about external
126
+ * storage in general.
127
+ *
128
+ * A LIVE OBJECT, like `canUseTool`/`hooks`/`mcpServers[].instance`: it is never serialized into
129
+ * `--config-json`, so a spawned-process transport reaches it only through the wrapper. Winter does
130
+ * not yet mirror to it (Lane K / a later phase); it is declared here because two documented
131
+ * combination REJECTIONS depend on its presence and capture (2) pins both of them at construction.
132
+ */
133
+ sessionStore?: SessionStore;
134
+ contextWindowTokens?: number;
135
+ compactionThreshold?: number;
136
+ trustedWorkspace?: boolean;
137
+ plansDirectory?: string;
138
+ outputStyle?: string;
139
+ /**
140
+ * DISCLOSED WINTER option (R6-9). Which provider a BARE `model` id resolves against, plus that
141
+ * provider's connection metadata and credential reference.
142
+ *
143
+ * `model` itself stays the pinned bare string and `system/init.model` keeps reporting what the
144
+ * caller passed — the resolved identity rides Winter-only init extension fields instead. A
145
+ * qualified `"<providerId>/<model>"` key needs no `provider` at all; the pinned Anthropic aliases
146
+ * (`sonnet`/`opus`/`haiku`/`claude-*`) resolve to the `anthropic` provider when a credential ref
147
+ * for it is configured. No model and no provider is a typed resolution error, never a silent
148
+ * default (WS-13 §9: no routing, no substitution in the provider layer).
149
+ */
150
+ provider?: ProviderSelection;
151
+ /**
152
+ * `sdk.d.ts:1540` — a **single string carrying a COMMA-SEPARATED list**, tried in order. This is
153
+ * the pin's own convention, not an oversight: the settings twin `Settings.fallbackModel` (`5577`)
154
+ * is a `string[]`, and the two shapes coexist with a documented precedence. Typing this `string[]`
155
+ * would be a parity divergence (derived-shapes-p6.md item (g), finding 1).
156
+ *
157
+ * Pinned semantics, AS IMPLEMENTED (P6 fix wave, Ruling E-3): the trigger is an R6-6
158
+ * retryable-class provider failure -- 5xx/overloaded, 429 (non-billing), 408, network, timeout --
159
+ * AFTER the adapter's own retries are exhausted (NOT a refusal: the `model_refusal_fallback`
160
+ * frame's `trigger` is the literal `'refusal'`). The failed round is re-run on the next candidate,
161
+ * candidates are tried in order, each once per turn, and the primary is re-tried at the START OF
162
+ * EACH USER TURN, so a temporary outage never permanently demotes the session. The swap emits NO
163
+ * pinned frame (capture (G): the only observable is the outgoing request's `model`); Winter
164
+ * additionally emits its disclosed `system/model_switch{reason: "fallback"}` for the swap AND for
165
+ * the restoration, and records both in the dialect record's `providerHistory`. A candidate is
166
+ * honoured only inside the CURRENT model's continuation domain (R6-9) -- a cross-domain candidate
167
+ * is a typed error at init and is skipped at engagement time if a `set_model` has since moved the
168
+ * session. A `set_model` parked during a fallback turn supersedes the restoration.
169
+ */
170
+ fallbackModel?: string;
171
+ /** `sdk.d.ts:1736`, three arms (`adaptive` | `enabled` | `disabled`). Takes precedence over `maxThinkingTokens`, stated twice in the pin (`1732`, `8215`). */
172
+ thinking?: ThinkingConfig;
173
+ /**
174
+ * `sdk.d.ts:1749`. **No numeric form** — see `EffortLevel`'s own declaration comment (R6-E).
175
+ *
176
+ * Pinned adjacent rules a consumer must not fight: `'max'` is session-scoped and deliberately not
177
+ * persistable (`Settings.effortLevel` excludes it), and the ACTIVE level is the one left after a
178
+ * per-model silent downgrade — which is exactly what the catalog's `reasoning.efforts` evidence
179
+ * exists to compute honestly rather than by guess.
180
+ */
181
+ effort?: EffortLevel;
182
+ /**
183
+ * @deprecated Use `thinking` instead.
184
+ *
185
+ * `sdk.d.ts:1758`. Kept and typed rather than dropped, because dropping a pinned Options member is
186
+ * a drop-in parity break. Its semantics CHANGE BY MODEL and that is the trap: on a modern model it
187
+ * is reinterpreted as on/off — `0` disables, any other value means *adaptive* — so forwarding
188
+ * `maxThinkingTokens: 8000` is not "budget 8000" (R6-E maps it exactly that way).
189
+ */
190
+ maxThinkingTokens?: number;
191
+ /**
192
+ * `stream_event` frames (`SDKPartialAssistantMessage`) are emitted only under this gate (R6-5).
193
+ * Absent/false keeps the stream byte-identical to every session before partial streaming existed.
194
+ */
195
+ includePartialMessages?: boolean;
196
+ /**
197
+ * A cumulative USD ceiling for this query's provider spend, LIVE since the P6 fix wave (Ruling
198
+ * E-4, R6-H). Checked BEFORE every provider request: once the accrued `total_cost_usd` exceeds it,
199
+ * the next request does not go out and the turn ends on the pinned `error_max_budget_usd` result
200
+ * (`is_error: true`), which carries the cost that crossed it. The generation that crossed the
201
+ * ceiling still delivers its own frames -- the cut is a request never sent, not an answer lost.
202
+ *
203
+ * The descriptor's `pricing` evidence is the ONLY price source: a priced row makes every result
204
+ * frame carry `total_cost_usd` (accumulated over the whole run and repeated on each result) and a
205
+ * `modelUsage` row keyed by the model string, with the catalog key as `canonicalModel` and
206
+ * `costBasis: "list"`; an UNPRICED row emits NO cost field at all and leaves this ceiling inert
207
+ * (disclosed). Winter deliberately does NOT do what the pin does here -- capture (K) shows the
208
+ * pinned runtime reporting a non-zero cost for a model no price table contains -- and it does not
209
+ * emit the main-loop-only `usage` block, which the pin's own JSDoc deprioritises (disclosed).
210
+ */
211
+ maxBudgetUsd?: number;
212
+ /** DISCLOSED WINTER option (R6-6): a stream silent for this long aborts as a typed `ProviderStallError`. Absent means DEFAULT_PROVIDER_STALL_TIMEOUT_MS. */
213
+ providerStallTimeoutMs?: number;
214
+ /** DISCLOSED WINTER option (R6-10): the macOS Keychain service every `{ kind: "keychain" }` ref resolves under. Absent means DEFAULT_KEYCHAIN_SERVICE. */
215
+ keychainService?: string;
216
+ /** DISCLOSED WINTER option (R6-14): the permission classifier's own model/credential, resolved through the SAME selection path as the session model. With none configured the worker serves only a `classifierEligible` model, else Manual fallback — never a silent weakening. */
217
+ autoClassifier?: AutoClassifierConfig;
218
+ /**
219
+ * DISCLOSED WINTER option (P2 carry, wired in T10): the advisor/reviewer backend's model, same
220
+ * selection path. Its optional `authRef` (fix wave, Ruling E-1) is the advisor's OWN credential:
221
+ * a target on another provider than the session's never inherits the session's -- it uses the
222
+ * route's ref, else the target provider's own keychain record (`<providerId>:default`), else a
223
+ * typed `no-credential-for-provider` refusal at its first generation.
224
+ *
225
+ * The MODEL may also come from `settings.advisor.model` (D30, hot); `Options.advisor.model` wins.
226
+ */
227
+ advisor?: AdvisorConfig;
228
+ /**
229
+ * DISCLOSED WINTER option (P7a, D19): THE BRAND PROFILE — every Winter-owned name this session
230
+ * runs under, as a partial that folds onto Winter's own defaults (brand.ts's `WINTER_BRAND`).
231
+ *
232
+ * This is what makes the SDK genuinely reusable rather than merely open: a host consuming Winter
233
+ * alone (D19's tier 1) gets its OWN home dir, project dir, instructions file, env prefix, keychain
234
+ * service, MCP server name and codex originator, resolved once here and carried to the runtime on
235
+ * `RuntimeConfig.brand` so a spawned or compiled child derives the same names the wrapper did.
236
+ *
237
+ * VALIDATED AT `query()` (brand.ts's grammar rules), and a refusal is a typed `InvalidBrandError`
238
+ * thrown synchronously at construction, like the other option-combination rejections above --
239
+ * never a silently-substituted default, because a session that quietly ran under the wrong home
240
+ * dir or the wrong keychain service is the worst possible outcome of a typo here.
241
+ *
242
+ * `keychainService` above is the DEPRECATED standalone alias for `brand.keychainService`: when
243
+ * both are set the standalone one WINS (it predates the profile and existing hosts pass it), and
244
+ * `query()` warns when the two are set to DIFFERENT values so the losing one is never silent.
245
+ *
246
+ * Claude-mirroring literals are NOT in the profile and cannot be rebranded (WS-01 §5): the
247
+ * official runtime's `CLAUDE_CONFIG_DIR`/`CLAUDE_CODE_TMPDIR`/`preset: "claude_code"`/
248
+ * `.claude-plugin` are its names, not ours. Neither are this repository's own test-harness env
249
+ * names.
250
+ */
251
+ brand?: Partial<BrandProfile>;
252
+ }
@@ -0,0 +1,48 @@
1
+ import { type BrandProfile } from "../brand.js";
2
+ export declare function isUnset(value: string | undefined): boolean;
3
+ /**
4
+ * The two brand fields this module needs. A `Pick` rather than the whole profile so a caller
5
+ * holding a partially-threaded config (or a test) can call it without constructing one.
6
+ */
7
+ export type HomeBrand = Pick<BrandProfile, "envPrefix" | "homeDirName">;
8
+ /**
9
+ * `<PREFIX>HOME` || `~/<homeDirName>` — mirrors `CLAUDE_CONFIG_DIR`'s env-override semantics
10
+ * (WS-05 §4), with the brand's own token instead of Claude's.
11
+ *
12
+ * `env` is injectable so tests never read the real process environment; it defaults to
13
+ * `process.env` in production. `brand` defaults to `WINTER_BRAND`, so every existing caller keeps
14
+ * exactly today's behaviour (`WINTER_HOME` || `~/.winter`) until it threads a profile through.
15
+ *
16
+ * THE ENV NAME IS DERIVED, NEVER SPELLED, and it is read INSIDE this function — never at module
17
+ * load. The brand arrives with `--config-json`, so a module-level read of a literal `<PREFIX>HOME`
18
+ * would bake in the wrong prefix for a reuser and could never be corrected; the sweep gate
19
+ * (packages/runtime/src/brand-gate.test.ts, rules 9 and 10) is what keeps it that way.
20
+ *
21
+ * PRECEDENCE (WS-01 §2.2, and the same rule the current daemon has always had): an explicit
22
+ * `<PREFIX>HOME` wins over EVERYTHING, including the dev profile. `<PREFIX>PROFILE=dev` selects
23
+ * `~/<homeDirName>-dev` — the dev/dist split, so a development build can never share a home (or a
24
+ * transcript store, or a settings file) with the copy a user actually runs.
25
+ */
26
+ export declare function resolveWinterHome(env?: Record<string, string | undefined>, brand?: HomeBrand): string;
27
+ /**
28
+ * P7a fix wave (item 5, whole-branch review M-3): THE OTHER HALF OF THE DEV PROFILE.
29
+ *
30
+ * WS-01's Phase 6 amendment pairs `<PREFIX>PROFILE=dev` with BOTH `~/<homeDirName>-dev` and a
31
+ * `.dev`-suffixed Keychain service, and assigns the fold "to whichever phase introduces the
32
+ * profile" -- this one. Only the home half landed: an env-selected dev session got its own home and
33
+ * its own transcript store while reading and WRITING the dist Keychain service, which is the one
34
+ * piece of state a developer most needs separated from the copy they actually use.
35
+ *
36
+ * It lives here, immediately beside `resolveWinterHome`, because the two are one rule read from two
37
+ * fields -- putting them in different files is how they came to disagree in the first place.
38
+ *
39
+ * AN EXPLICIT VALUE IS NEVER REWRITTEN. `hostSetKeychainService` is true when the host passed
40
+ * `brand.keychainService` or the deprecated `keychainService` alias: they named a service, and a
41
+ * runtime that silently appended to it would be rewriting a host's own decision -- the same
42
+ * precedence `resolveWinterHome` gives an explicit `<PREFIX>HOME` over the profile.
43
+ *
44
+ * The suffix is applied only when the result still satisfies `BrandProfile`'s own service grammar
45
+ * (64 chars); a longer name is left alone rather than made invalid, since an invalid service reaches
46
+ * the Keychain as a lookup that can never match.
47
+ */
48
+ export declare function resolveKeychainServiceForProfile(brand: Pick<BrandProfile, "envPrefix" | "keychainService">, env: Record<string, string | undefined> | undefined, hostSetKeychainService: boolean): string;
@@ -0,0 +1,6 @@
1
+ export interface CompatibilityKeys {
2
+ transcriptProjectKey: string;
3
+ memoryProjectKey: string;
4
+ tempProjectKey: string;
5
+ }
6
+ export declare function compatibilityKeys(cwd: string): CompatibilityKeys;
@@ -0,0 +1 @@
1
+ export declare function transcriptProjectKey(absPath: string): string;
@@ -0,0 +1,272 @@
1
+ export type PermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan" | "dontAsk" | "auto";
2
+ export type PermissionBehavior = "allow" | "deny" | "ask";
3
+ export type PermissionRuleValue = {
4
+ toolName: string;
5
+ ruleContent?: string;
6
+ };
7
+ export type PermissionUpdate = {
8
+ type: "addRules";
9
+ rules: PermissionRuleValue[];
10
+ behavior: PermissionBehavior;
11
+ destination: PermissionUpdateDestination;
12
+ } | {
13
+ type: "replaceRules";
14
+ rules: PermissionRuleValue[];
15
+ behavior: PermissionBehavior;
16
+ destination: PermissionUpdateDestination;
17
+ } | {
18
+ type: "removeRules";
19
+ rules: PermissionRuleValue[];
20
+ behavior: PermissionBehavior;
21
+ destination: PermissionUpdateDestination;
22
+ } | {
23
+ type: "setMode";
24
+ mode: PermissionMode;
25
+ destination: PermissionUpdateDestination;
26
+ } | {
27
+ type: "addDirectories";
28
+ directories: string[];
29
+ destination: PermissionUpdateDestination;
30
+ } | {
31
+ type: "removeDirectories";
32
+ directories: string[];
33
+ destination: PermissionUpdateDestination;
34
+ };
35
+ export type PermissionUpdateDestination = "userSettings" | "projectSettings" | "localSettings" | "session" | "cliArg";
36
+ export type RuleSource = "managed" | "user" | "project" | "local" | "cliArg" | "session" | "sdk";
37
+ export type CanUseTool = (toolName: string, input: Record<string, unknown>, options: {
38
+ signal: AbortSignal;
39
+ suggestions?: PermissionUpdate[];
40
+ blockedPath?: string;
41
+ decisionReason?: string;
42
+ title?: string;
43
+ displayName?: string;
44
+ description?: string;
45
+ toolUseID: string;
46
+ agentID?: string;
47
+ requestId: string;
48
+ matchedAskRule?: {
49
+ source: string;
50
+ toolName: string;
51
+ ruleContent?: string;
52
+ };
53
+ }) => Promise<PermissionResult | null>;
54
+ export type PermissionDecisionClassification = "user_temporary" | "user_permanent" | "user_reject";
55
+ export type PermissionResult = {
56
+ behavior: "allow";
57
+ updatedInput?: Record<string, unknown>;
58
+ updatedPermissions?: PermissionUpdate[];
59
+ toolUseID?: string;
60
+ decisionClassification?: PermissionDecisionClassification;
61
+ } | {
62
+ behavior: "deny";
63
+ message: string;
64
+ interrupt?: boolean;
65
+ toolUseID?: string;
66
+ decisionClassification?: PermissionDecisionClassification;
67
+ };
68
+ export interface PermissionRequestPayload {
69
+ toolName: string;
70
+ input: Record<string, unknown>;
71
+ suggestions?: PermissionUpdate[];
72
+ blockedPath?: string;
73
+ decisionReason?: string;
74
+ title?: string;
75
+ displayName?: string;
76
+ description?: string;
77
+ toolUseID: string;
78
+ agentID?: string;
79
+ requestId: string;
80
+ matchedAskRule?: {
81
+ source: string;
82
+ toolName: string;
83
+ ruleContent?: string;
84
+ };
85
+ policyVersion: number;
86
+ }
87
+ export declare const HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "UserPromptSubmit", "Stop", "SubagentStart", "SubagentStop", "PreCompact", "PermissionRequest", "Notification", "PostToolBatch", "UserPromptExpansion", "MessageDisplay", "StopFailure", "PostCompact", "PermissionDenied", "SessionStart", "SessionEnd", "Setup", "TeammateIdle", "TaskCreated", "TaskCompleted", "Elicitation", "ElicitationResult", "ConfigChange", "InstructionsLoaded", "WorktreeCreate", "WorktreeRemove", "CwdChanged", "FileChanged", "DirectoryAdded"];
88
+ export type HookEvent = (typeof HOOK_EVENTS)[number];
89
+ export type HookEventName = HookEvent;
90
+ export type HookSource = "managed" | "user" | "project" | "local" | "sdk" | "plugin";
91
+ export interface BaseHookInput {
92
+ session_id: string;
93
+ transcript_path: string;
94
+ cwd: string;
95
+ prompt_id?: string;
96
+ permission_mode?: string;
97
+ agent_id?: string;
98
+ agent_type?: string;
99
+ effort?: {
100
+ level: string;
101
+ };
102
+ }
103
+ export type HookPermissionDecision = "allow" | "ask" | "deny" | "defer";
104
+ export interface PreToolUseHookInput extends BaseHookInput {
105
+ hook_event_name: "PreToolUse";
106
+ tool_name: string;
107
+ tool_input: unknown;
108
+ tool_use_id: string;
109
+ }
110
+ export interface PostToolUseHookInput extends BaseHookInput {
111
+ hook_event_name: "PostToolUse";
112
+ tool_name: string;
113
+ tool_input: unknown;
114
+ tool_response: unknown;
115
+ tool_use_id: string;
116
+ duration_ms?: number;
117
+ }
118
+ export interface PostToolUseFailureHookInput extends BaseHookInput {
119
+ hook_event_name: "PostToolUseFailure";
120
+ tool_name: string;
121
+ tool_input: unknown;
122
+ tool_use_id: string;
123
+ error: string;
124
+ is_interrupt?: boolean;
125
+ duration_ms?: number;
126
+ }
127
+ export interface UserPromptSubmitHookInput extends BaseHookInput {
128
+ hook_event_name: "UserPromptSubmit";
129
+ prompt: string;
130
+ source?: "user" | "sdk" | "system" | "loop_wakeup" | "schedule_wakeup" | "poll_event";
131
+ session_title?: string;
132
+ }
133
+ export interface StopHookInput extends BaseHookInput {
134
+ hook_event_name: "Stop";
135
+ stop_hook_active: boolean;
136
+ last_assistant_message?: unknown;
137
+ background_tasks?: unknown[];
138
+ session_crons?: unknown[];
139
+ }
140
+ export interface SessionStartHookInput extends BaseHookInput {
141
+ hook_event_name: "SessionStart";
142
+ source: "startup" | "resume" | "clear" | "compact" | "fork";
143
+ agent_type?: string;
144
+ model?: string;
145
+ session_title?: string;
146
+ }
147
+ export interface SessionEndHookInput extends BaseHookInput {
148
+ hook_event_name: "SessionEnd";
149
+ reason: string;
150
+ }
151
+ export interface NotificationHookInput extends BaseHookInput {
152
+ hook_event_name: "Notification";
153
+ message: string;
154
+ title?: string;
155
+ notification_type: string;
156
+ }
157
+ export interface PermissionRequestHookInput extends BaseHookInput {
158
+ hook_event_name: "PermissionRequest";
159
+ tool_name: string;
160
+ tool_input: unknown;
161
+ permission_suggestions?: PermissionUpdate[];
162
+ }
163
+ export interface PermissionDeniedHookInput extends BaseHookInput {
164
+ hook_event_name: "PermissionDenied";
165
+ tool_name: string;
166
+ tool_input: unknown;
167
+ tool_use_id: string;
168
+ reason: string;
169
+ }
170
+ export interface GenericHookInput extends BaseHookInput {
171
+ hook_event_name: string;
172
+ }
173
+ export type HookInput = PreToolUseHookInput | PostToolUseHookInput | PostToolUseFailureHookInput | UserPromptSubmitHookInput | StopHookInput | SessionStartHookInput | SessionEndHookInput | NotificationHookInput | PermissionRequestHookInput | PermissionDeniedHookInput | GenericHookInput;
174
+ export interface AsyncHookJSONOutput {
175
+ async: true;
176
+ asyncTimeout?: number;
177
+ }
178
+ export interface PreToolUseHookSpecificOutput {
179
+ hookEventName: "PreToolUse";
180
+ permissionDecision?: HookPermissionDecision;
181
+ permissionDecisionReason?: string;
182
+ updatedInput?: Record<string, unknown>;
183
+ additionalContext?: string;
184
+ }
185
+ export interface PostToolUseHookSpecificOutput {
186
+ hookEventName: "PostToolUse";
187
+ additionalContext?: string;
188
+ classifierContext?: string;
189
+ updatedToolOutput?: unknown;
190
+ updatedMCPToolOutput?: unknown;
191
+ }
192
+ export interface PostToolUseFailureHookSpecificOutput {
193
+ hookEventName: "PostToolUseFailure";
194
+ additionalContext?: string;
195
+ }
196
+ export interface UserPromptSubmitHookSpecificOutput {
197
+ hookEventName: "UserPromptSubmit";
198
+ additionalContext?: string;
199
+ sessionTitle?: string;
200
+ suppressOriginalPrompt?: boolean;
201
+ }
202
+ export interface StopHookSpecificOutput {
203
+ hookEventName: "Stop";
204
+ additionalContext?: string;
205
+ }
206
+ export interface SessionStartHookSpecificOutput {
207
+ hookEventName: "SessionStart";
208
+ additionalContext?: string;
209
+ initialUserMessage?: string;
210
+ sessionTitle?: string;
211
+ watchPaths?: string[];
212
+ reloadSkills?: boolean;
213
+ }
214
+ export interface NotificationHookSpecificOutput {
215
+ hookEventName: "Notification";
216
+ additionalContext?: string;
217
+ }
218
+ export interface PermissionRequestHookSpecificOutput {
219
+ hookEventName: "PermissionRequest";
220
+ decision: {
221
+ behavior: "allow";
222
+ updatedInput?: Record<string, unknown>;
223
+ updatedPermissions?: PermissionUpdate[];
224
+ } | {
225
+ behavior: "deny";
226
+ message?: string;
227
+ interrupt?: boolean;
228
+ };
229
+ }
230
+ export interface PermissionDeniedHookSpecificOutput {
231
+ hookEventName: "PermissionDenied";
232
+ retry?: boolean;
233
+ }
234
+ export interface GenericHookSpecificOutput {
235
+ hookEventName: string;
236
+ additionalContext?: string;
237
+ [key: string]: unknown;
238
+ }
239
+ export type HookSpecificOutput = PreToolUseHookSpecificOutput | PostToolUseHookSpecificOutput | PostToolUseFailureHookSpecificOutput | UserPromptSubmitHookSpecificOutput | StopHookSpecificOutput | SessionStartHookSpecificOutput | NotificationHookSpecificOutput | PermissionRequestHookSpecificOutput | PermissionDeniedHookSpecificOutput | GenericHookSpecificOutput;
240
+ export interface SyncHookJSONOutput {
241
+ continue?: boolean;
242
+ suppressOutput?: boolean;
243
+ stopReason?: string;
244
+ decision?: "approve" | "block";
245
+ systemMessage?: string;
246
+ terminalSequence?: string;
247
+ reason?: string;
248
+ hookSpecificOutput?: HookSpecificOutput;
249
+ }
250
+ export type HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput;
251
+ export type HookCallback = (input: HookInput, toolUseID: string | undefined, options: {
252
+ signal: AbortSignal;
253
+ }) => Promise<HookJSONOutput>;
254
+ export interface HookCallbackMatcher {
255
+ matcher?: string;
256
+ hooks: HookCallback[];
257
+ timeout?: number;
258
+ }
259
+ export interface HookInvocationPayload {
260
+ event: string;
261
+ matchedMatcher?: string;
262
+ sessionId: string;
263
+ agentID?: string;
264
+ toolUseID?: string;
265
+ toolName?: string;
266
+ input?: Record<string, unknown>;
267
+ payload?: unknown;
268
+ policyVersion: string;
269
+ requestId: string;
270
+ hookId: string;
271
+ hookName?: string;
272
+ }
@@ -0,0 +1,10 @@
1
+ import type { WinterFrame } from "./frames.js";
2
+ export declare class ProtocolError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare function encodeFrame(frame: WinterFrame): string;
6
+ export declare function decodeFrame(line: string): WinterFrame;
7
+ export declare function splitFrames(chunk: string, carry: string): {
8
+ frames: WinterFrame[];
9
+ carry: string;
10
+ };