@combycode/llm-sdk 1.0.0-rc.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.
Files changed (228) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +103 -0
  4. package/dist/agent/approval-types.d.ts +49 -0
  5. package/dist/agent/context-registry/layers.d.ts +39 -0
  6. package/dist/agent/context-registry/registry-internal.d.ts +14 -0
  7. package/dist/agent/context-registry/registry.d.ts +104 -0
  8. package/dist/agent/context-registry/types.d.ts +110 -0
  9. package/dist/agent/guardrail-types.d.ts +58 -0
  10. package/dist/agent/history-types.d.ts +40 -0
  11. package/dist/agent/history.d.ts +111 -0
  12. package/dist/agent/loop-config.d.ts +67 -0
  13. package/dist/agent/loop-internals.d.ts +46 -0
  14. package/dist/agent/loop-step-state.d.ts +20 -0
  15. package/dist/agent/loop.d.ts +133 -0
  16. package/dist/agent/tool-key.d.ts +3 -0
  17. package/dist/agent/types.d.ts +138 -0
  18. package/dist/bus/agent-bus.d.ts +99 -0
  19. package/dist/bus/async-context.browser.d.ts +13 -0
  20. package/dist/bus/async-context.d.ts +11 -0
  21. package/dist/bus/async-context.types.d.ts +14 -0
  22. package/dist/bus/hook-bus.d.ts +43 -0
  23. package/dist/bus/hook-map.d.ts +592 -0
  24. package/dist/helpers/agent.d.ts +32 -0
  25. package/dist/helpers/batch.d.ts +74 -0
  26. package/dist/helpers/calibration-store.d.ts +27 -0
  27. package/dist/helpers/calibration-types.d.ts +64 -0
  28. package/dist/helpers/chain.d.ts +30 -0
  29. package/dist/helpers/client-pool.d.ts +15 -0
  30. package/dist/helpers/client-resolver.d.ts +62 -0
  31. package/dist/helpers/collection.d.ts +19 -0
  32. package/dist/helpers/consolidate.d.ts +66 -0
  33. package/dist/helpers/content.d.ts +25 -0
  34. package/dist/helpers/conversation-export.d.ts +14 -0
  35. package/dist/helpers/conversation-zip.d.ts +28 -0
  36. package/dist/helpers/count-tokens.d.ts +26 -0
  37. package/dist/helpers/define-tool.d.ts +66 -0
  38. package/dist/helpers/delegate.d.ts +6 -0
  39. package/dist/helpers/embed.d.ts +24 -0
  40. package/dist/helpers/engine.d.ts +121 -0
  41. package/dist/helpers/estimate-types.d.ts +67 -0
  42. package/dist/helpers/estimate.d.ts +45 -0
  43. package/dist/helpers/estimator.d.ts +48 -0
  44. package/dist/helpers/handoff-types.d.ts +18 -0
  45. package/dist/helpers/handoff.d.ts +14 -0
  46. package/dist/helpers/llm.d.ts +24 -0
  47. package/dist/helpers/mcp.d.ts +82 -0
  48. package/dist/helpers/media.d.ts +58 -0
  49. package/dist/helpers/models.d.ts +36 -0
  50. package/dist/helpers/moderate-types.d.ts +73 -0
  51. package/dist/helpers/moderate.d.ts +13 -0
  52. package/dist/helpers/moderation-guardrail.d.ts +29 -0
  53. package/dist/helpers/observer.d.ts +36 -0
  54. package/dist/helpers/one-shot.d.ts +86 -0
  55. package/dist/helpers/parallel.d.ts +16 -0
  56. package/dist/helpers/realtime.d.ts +25 -0
  57. package/dist/helpers/route.d.ts +29 -0
  58. package/dist/helpers/select-model.d.ts +41 -0
  59. package/dist/helpers/server.d.ts +32 -0
  60. package/dist/helpers/transcribe.d.ts +36 -0
  61. package/dist/index.browser.js +38047 -0
  62. package/dist/index.d.ts +275 -0
  63. package/dist/index.js +37974 -0
  64. package/dist/llm/audio/voices.d.ts +7 -0
  65. package/dist/llm/client-config.d.ts +37 -0
  66. package/dist/llm/client-internal.d.ts +28 -0
  67. package/dist/llm/client.d.ts +68 -0
  68. package/dist/llm/providers/_shared/constants.d.ts +5 -0
  69. package/dist/llm/providers/_shared/response-utils.d.ts +8 -0
  70. package/dist/llm/providers/anthropic/batch.d.ts +19 -0
  71. package/dist/llm/providers/anthropic/constants.d.ts +13 -0
  72. package/dist/llm/providers/anthropic/files.d.ts +23 -0
  73. package/dist/llm/providers/anthropic/messages.d.ts +30 -0
  74. package/dist/llm/providers/google/batch.d.ts +20 -0
  75. package/dist/llm/providers/google/constants.d.ts +6 -0
  76. package/dist/llm/providers/google/embeddings.d.ts +15 -0
  77. package/dist/llm/providers/google/files.d.ts +22 -0
  78. package/dist/llm/providers/google/generate.d.ts +27 -0
  79. package/dist/llm/providers/google/interactions.d.ts +30 -0
  80. package/dist/llm/providers/google/media.d.ts +27 -0
  81. package/dist/llm/providers/google/realtime.d.ts +27 -0
  82. package/dist/llm/providers/openai/batch.d.ts +19 -0
  83. package/dist/llm/providers/openai/completions.d.ts +25 -0
  84. package/dist/llm/providers/openai/embeddings.d.ts +16 -0
  85. package/dist/llm/providers/openai/files.d.ts +23 -0
  86. package/dist/llm/providers/openai/media.d.ts +28 -0
  87. package/dist/llm/providers/openai/moderations.d.ts +19 -0
  88. package/dist/llm/providers/openai/realtime.d.ts +24 -0
  89. package/dist/llm/providers/openai/responses.d.ts +29 -0
  90. package/dist/llm/providers/openai/tiers.d.ts +13 -0
  91. package/dist/llm/providers/openai/transcription.d.ts +20 -0
  92. package/dist/llm/providers/openrouter/completions.d.ts +15 -0
  93. package/dist/llm/providers/openrouter/embeddings.d.ts +7 -0
  94. package/dist/llm/providers/openrouter/media.d.ts +25 -0
  95. package/dist/llm/providers/openrouter/responses.d.ts +17 -0
  96. package/dist/llm/providers/xai/batch.d.ts +19 -0
  97. package/dist/llm/providers/xai/completions.d.ts +24 -0
  98. package/dist/llm/providers/xai/files.d.ts +23 -0
  99. package/dist/llm/providers/xai/media.d.ts +27 -0
  100. package/dist/llm/providers/xai/responses.d.ts +20 -0
  101. package/dist/llm/realtime/session.d.ts +34 -0
  102. package/dist/llm/realtime/types.d.ts +66 -0
  103. package/dist/llm/server-state.d.ts +32 -0
  104. package/dist/llm/types/audio.d.ts +18 -0
  105. package/dist/llm/types/messages.d.ts +125 -0
  106. package/dist/llm/types/options.d.ts +51 -0
  107. package/dist/llm/types/provider.d.ts +39 -0
  108. package/dist/llm/types/request.d.ts +51 -0
  109. package/dist/llm/types/response.d.ts +36 -0
  110. package/dist/llm/types/schema-utils.d.ts +8 -0
  111. package/dist/llm/types/stream.d.ts +42 -0
  112. package/dist/llm/types/tiers.d.ts +11 -0
  113. package/dist/llm/types/tools.d.ts +20 -0
  114. package/dist/network/engine.d.ts +87 -0
  115. package/dist/network/errors.d.ts +14 -0
  116. package/dist/network/queue-state-config.d.ts +41 -0
  117. package/dist/network/queue-state.d.ts +45 -0
  118. package/dist/network/rate-limiter.d.ts +49 -0
  119. package/dist/network/realtime-connection.d.ts +40 -0
  120. package/dist/network/request-queue.d.ts +40 -0
  121. package/dist/network/semaphore.d.ts +12 -0
  122. package/dist/network/sse.d.ts +3 -0
  123. package/dist/network/types.d.ts +127 -0
  124. package/dist/plugins/batch/batcher.d.ts +45 -0
  125. package/dist/plugins/batch/strategy.d.ts +20 -0
  126. package/dist/plugins/batch/types.d.ts +54 -0
  127. package/dist/plugins/cache/cache.d.ts +50 -0
  128. package/dist/plugins/cache/file-store.d.ts +15 -0
  129. package/dist/plugins/cache/memory-store.d.ts +10 -0
  130. package/dist/plugins/cache/types.d.ts +28 -0
  131. package/dist/plugins/configuration/configuration.d.ts +71 -0
  132. package/dist/plugins/context-guard/facts.d.ts +14 -0
  133. package/dist/plugins/context-guard/guard.d.ts +27 -0
  134. package/dist/plugins/context-guard/strategies/layered.d.ts +24 -0
  135. package/dist/plugins/context-guard/strategies/truncate.d.ts +19 -0
  136. package/dist/plugins/context-guard/tools.d.ts +50 -0
  137. package/dist/plugins/context-guard/types.d.ts +110 -0
  138. package/dist/plugins/context-measurer/calibration/store.d.ts +19 -0
  139. package/dist/plugins/context-measurer/counter/count-api.d.ts +41 -0
  140. package/dist/plugins/context-measurer/counter/heuristic.d.ts +25 -0
  141. package/dist/plugins/context-measurer/counter/hybrid.d.ts +33 -0
  142. package/dist/plugins/context-measurer/counter/tiktoken.d.ts +13 -0
  143. package/dist/plugins/context-measurer/measurer.d.ts +49 -0
  144. package/dist/plugins/context-measurer/types.d.ts +40 -0
  145. package/dist/plugins/cost-collector/collector.d.ts +42 -0
  146. package/dist/plugins/cost-collector/cost-collector-internal.d.ts +58 -0
  147. package/dist/plugins/cost-collector/cost-collector-types.d.ts +42 -0
  148. package/dist/plugins/embeddings/types.d.ts +23 -0
  149. package/dist/plugins/files/attachment.d.ts +79 -0
  150. package/dist/plugins/files/provider-adapter.d.ts +37 -0
  151. package/dist/plugins/files/registry.d.ts +48 -0
  152. package/dist/plugins/files/strategy.d.ts +38 -0
  153. package/dist/plugins/internal-tools/backends/local.d.ts +12 -0
  154. package/dist/plugins/internal-tools/builtin/builtin.d.ts +21 -0
  155. package/dist/plugins/internal-tools/builtin/clarify.d.ts +12 -0
  156. package/dist/plugins/internal-tools/builtin/classify.d.ts +12 -0
  157. package/dist/plugins/internal-tools/builtin/score.d.ts +13 -0
  158. package/dist/plugins/internal-tools/builtin/structure.d.ts +11 -0
  159. package/dist/plugins/internal-tools/builtin/summarize.d.ts +19 -0
  160. package/dist/plugins/internal-tools/id.d.ts +12 -0
  161. package/dist/plugins/internal-tools/registry.d.ts +24 -0
  162. package/dist/plugins/internal-tools/runner/define.d.ts +7 -0
  163. package/dist/plugins/internal-tools/runner/json-enforcement.d.ts +3 -0
  164. package/dist/plugins/internal-tools/runner/runner.d.ts +47 -0
  165. package/dist/plugins/internal-tools/runner/template.d.ts +10 -0
  166. package/dist/plugins/internal-tools/runner/types.d.ts +57 -0
  167. package/dist/plugins/internal-tools/runner/variants.d.ts +34 -0
  168. package/dist/plugins/internal-tools/types.d.ts +80 -0
  169. package/dist/plugins/logger/console-sink.d.ts +26 -0
  170. package/dist/plugins/logger/logger.d.ts +38 -0
  171. package/dist/plugins/logger/types.d.ts +25 -0
  172. package/dist/plugins/mcp/base-transport.d.ts +58 -0
  173. package/dist/plugins/mcp/client.d.ts +92 -0
  174. package/dist/plugins/mcp/jsonrpc.d.ts +20 -0
  175. package/dist/plugins/mcp/oauth.d.ts +122 -0
  176. package/dist/plugins/mcp/sampling.d.ts +17 -0
  177. package/dist/plugins/mcp/tools.d.ts +20 -0
  178. package/dist/plugins/mcp/transport-http.d.ts +50 -0
  179. package/dist/plugins/mcp/transport-stdio.d.ts +23 -0
  180. package/dist/plugins/mcp/transport-ws.d.ts +32 -0
  181. package/dist/plugins/mcp/transport.d.ts +29 -0
  182. package/dist/plugins/mcp/types.d.ts +202 -0
  183. package/dist/plugins/mcp/url-guard.d.ts +70 -0
  184. package/dist/plugins/mcp/win-spawn.d.ts +20 -0
  185. package/dist/plugins/media/file-store.d.ts +24 -0
  186. package/dist/plugins/media/memory-store.d.ts +17 -0
  187. package/dist/plugins/media/output.d.ts +44 -0
  188. package/dist/plugins/media/source-image.d.ts +36 -0
  189. package/dist/plugins/media/types.d.ts +146 -0
  190. package/dist/plugins/model-catalog/catalog.d.ts +152 -0
  191. package/dist/plugins/permissions/glob.d.ts +6 -0
  192. package/dist/plugins/permissions/matchers.d.ts +7 -0
  193. package/dist/plugins/permissions/policy.d.ts +10 -0
  194. package/dist/plugins/permissions/types.d.ts +22 -0
  195. package/dist/plugins/persistence/file.d.ts +25 -0
  196. package/dist/plugins/persistence/memory.d.ts +23 -0
  197. package/dist/plugins/persistence/types.d.ts +21 -0
  198. package/dist/plugins/retrieval/chunker.d.ts +28 -0
  199. package/dist/plugins/retrieval/hosted-google.d.ts +60 -0
  200. package/dist/plugins/retrieval/hosted-openai.d.ts +45 -0
  201. package/dist/plugins/retrieval/hosted-xai.d.ts +57 -0
  202. package/dist/plugins/retrieval/index.d.ts +70 -0
  203. package/dist/plugins/retrieval/local.d.ts +50 -0
  204. package/dist/plugins/retrieval/types.d.ts +161 -0
  205. package/dist/plugins/retrieval/vector-store.d.ts +70 -0
  206. package/dist/plugins/scheduler/scheduler.d.ts +31 -0
  207. package/dist/plugins/telemetry/telemetry.d.ts +118 -0
  208. package/dist/plugins/tool-catalog/catalog.d.ts +31 -0
  209. package/dist/plugins/tool-catalog/errors.d.ts +22 -0
  210. package/dist/plugins/tool-catalog/types.d.ts +44 -0
  211. package/dist/runtime/runtime.d.ts +20 -0
  212. package/dist/server/auth.d.ts +25 -0
  213. package/dist/server/dispatch.d.ts +35 -0
  214. package/dist/server/loaders.d.ts +26 -0
  215. package/dist/server/oai-adapter.d.ts +30 -0
  216. package/dist/server/oai-types.d.ts +114 -0
  217. package/dist/server/response-store.d.ts +59 -0
  218. package/dist/server/router.d.ts +59 -0
  219. package/dist/server/server.d.ts +61 -0
  220. package/dist/types/request-context.d.ts +46 -0
  221. package/dist/util/async.d.ts +3 -0
  222. package/dist/util/base64.d.ts +8 -0
  223. package/dist/util/duration.d.ts +12 -0
  224. package/dist/util/http.d.ts +9 -0
  225. package/dist/util/image-mime.d.ts +8 -0
  226. package/dist/util/json-schema.d.ts +7 -0
  227. package/dist/util/wav.d.ts +20 -0
  228. package/package.json +95 -0
@@ -0,0 +1,111 @@
1
+ /** ConversationHistory — traversable, exportable, importable.
2
+ * This is the canonical source of truth for any conversation. */
3
+ import type { Message } from '../llm/types/messages';
4
+ import { type Usage } from '../llm/types/response';
5
+ import { ContextRegistry } from './context-registry/registry';
6
+ import type { ConversationHistoryConfig, HistoryEntry, HistorySnapshot } from './history-types';
7
+ export declare class ConversationHistory {
8
+ private entries;
9
+ private _id;
10
+ private _metadata;
11
+ private _createdAt;
12
+ private _updatedAt;
13
+ /** Layered context storage. Contributors (ContextGuard, memory manager,
14
+ * RAG, user) write named layers here. `history.system` getter/setter is a
15
+ * thin shim over `registry.set(LEGACY_SYSTEM_LAYER, ...)` for backward compat. */
16
+ readonly registry: ContextRegistry;
17
+ /** Provider-reported exact input-token count from the last completion (includes system + all prior messages up to that point). */
18
+ private _lastActualTotal;
19
+ /** Index of the last entry that was included in _lastActualTotal. Entries after this index are "pending" and estimated. */
20
+ private _lastActualEntryIndex;
21
+ private _counter;
22
+ private _provider;
23
+ private _model;
24
+ constructor(idOrConfig?: string | ConversationHistoryConfig);
25
+ /** Get the last provider-reported exact input token count. */
26
+ get lastActualTotal(): number;
27
+ /**
28
+ * Record the actual input token count reported by a provider for a request.
29
+ *
30
+ * Call this BEFORE appending the response. The input tokens describe the state
31
+ * of history that was sent — which is what's currently in history before the
32
+ * response is added.
33
+ *
34
+ * Note: normally this is automatic. When you call `append()` with an assistant
35
+ * message carrying `usage.inputTokens`, this is invoked internally.
36
+ */
37
+ recordActualUsage(inputTokens: number): void;
38
+ get id(): string;
39
+ get length(): number;
40
+ /** Legacy system-prompt accessor — reads all 'system'-tagged layers in the
41
+ * registry. For the common case of a single system prompt, this returns
42
+ * the same string that was set via the setter. When other contributors
43
+ * (ContextGuard, memory manager) have added more 'system'-tagged layers,
44
+ * the returned string is their composed output. */
45
+ get system(): string | undefined;
46
+ /** Legacy setter — writes to the `_legacy_system` layer tagged 'system'.
47
+ * Priority 200 (in the "dynamic contributor" zone). Historically this field
48
+ * was used for content that changes during conversation (ContextGuard facts),
49
+ * so it renders AFTER stable prefix layers (agent role @10, context @100)
50
+ * for prompt-cache friendliness. Pass undefined to remove the layer. */
51
+ set system(value: string | undefined);
52
+ get metadata(): Record<string, unknown>;
53
+ setMetadata(key: string, value: unknown): void;
54
+ /** Add a message to history */
55
+ append(message: Message, meta?: {
56
+ model?: string;
57
+ usage?: Usage;
58
+ latencyMs?: number;
59
+ }): HistoryEntry;
60
+ /** Get all messages (for building request) */
61
+ messages(): Message[];
62
+ /** Get messages as entries (with metadata) */
63
+ all(): readonly HistoryEntry[];
64
+ /** Get entry by index */
65
+ at(index: number): HistoryEntry | undefined;
66
+ /** Get last N entries */
67
+ last(n: number): HistoryEntry[];
68
+ /** Get last N messages */
69
+ lastMessages(n: number): Message[];
70
+ /** Find entries matching a predicate */
71
+ filter(fn: (entry: HistoryEntry) => boolean): HistoryEntry[];
72
+ /** Get entries by role */
73
+ byRole(role: Message['role']): HistoryEntry[];
74
+ /** Iterate entries */
75
+ [Symbol.iterator](): Iterator<HistoryEntry>;
76
+ /** Cumulative usage across all turns */
77
+ totalUsage(): Usage;
78
+ /**
79
+ * Estimated total input tokens for the next request.
80
+ *
81
+ * Uses a hybrid: exact count for entries already sent (from last response's usage)
82
+ * plus estimate of entries added since. Accurate because only the delta is estimated.
83
+ *
84
+ * If no actual usage has been recorded yet (fresh history), estimates everything.
85
+ */
86
+ estimatedTokens(): number;
87
+ private estimateTokensForMessage;
88
+ /** Clear all history */
89
+ clear(): void;
90
+ /** Truncate to last N entries */
91
+ truncate(keepLast: number): HistoryEntry[];
92
+ /** Replace entries in [from, to) with a single synthetic message.
93
+ * Used by ContextGuard when compacting a range into a summary entry.
94
+ * - from, to are inclusive-exclusive indices against current entries (0-based).
95
+ * - replacement becomes a new HistoryEntry with timestamp = max timestamp in
96
+ * the replaced range (preserves chronological order).
97
+ * Returns the removed entries. Entry indices past `to` shift down. */
98
+ spliceRange(from: number, to: number, replacement: Message): HistoryEntry[];
99
+ /** Append text to the legacy system layer. Preserves backward-compat
100
+ * semantics; new code should write named registry layers directly. */
101
+ appendSystem(text: string): void;
102
+ /** Deep clone this history, including the registry. */
103
+ fork(newId?: string): ConversationHistory;
104
+ /** Export to JSON-serializable snapshot. Includes registry state. */
105
+ export(): HistorySnapshot;
106
+ /** Import from snapshot. Supports both new (registry-aware) and legacy
107
+ * (`system` field only) snapshots. */
108
+ static import(snapshot: HistorySnapshot): ConversationHistory;
109
+ /** Rough token estimate: ~4 chars per token for English */
110
+ private estimateTokens;
111
+ }
@@ -0,0 +1,67 @@
1
+ /** AgentLoop configuration. */
2
+ import type { HookBus } from '../bus/hook-bus';
3
+ import type { LLMClient } from '../llm/client';
4
+ import type { CacheConfig, ThinkingConfig } from '../llm/types/request';
5
+ import type { ConversationHistory } from './history';
6
+ import type { HistorySnapshot } from './history-types';
7
+ import type { AgentTool } from './types';
8
+ import type { Guardrail } from './guardrail-types';
9
+ import type { PermissionPolicy } from '../plugins/permissions/policy';
10
+ import type { ApprovalRequest, ApprovalDecision } from './approval-types';
11
+ import type { Persistence } from '../plugins/persistence/types';
12
+ export interface AgentLoopConfig {
13
+ /** LLM client. AgentLoop reads `client.model` and uses `client.complete`/`client.stream`. */
14
+ client: LLMClient;
15
+ /** Persona / role text for the agent. Stored as the `agentloop.system` registry
16
+ * layer (priority 10). Composed with other system-tagged layers when sending.
17
+ * When passed as a function, it is re-evaluated at the start of every
18
+ * `complete()` / `stream()` call — useful for live-reload prompts backed
19
+ * by a config file or persistence collection. */
20
+ system?: string | (() => string | Promise<string>);
21
+ /** Run-scenario context (background for the current task). Stored as the
22
+ * `agentloop.context` registry layer (priority 100). */
23
+ context?: string;
24
+ /** Executable tools. Indexed by function name (FunctionTool) or type (BuiltinTool). */
25
+ tools?: AgentTool[];
26
+ /** Reuse an existing history (or rehydrate from a snapshot). New history
27
+ * is created when omitted. */
28
+ history?: ConversationHistory | HistorySnapshot;
29
+ /** Hook bus. Optional — a fresh bus is created when omitted. */
30
+ hooks?: HookBus;
31
+ maxTokens?: number;
32
+ temperature?: number;
33
+ thinking?: ThinkingConfig;
34
+ cache?: CacheConfig;
35
+ parallelToolCalls?: boolean;
36
+ toolTimeout?: number;
37
+ /** Maximum number of tool-followup rounds per run.
38
+ * When the loop has completed this many steps and the model is still
39
+ * requesting tools, it stops before the next LLM call and sets the run
40
+ * reason to 'max_steps'.
41
+ *
42
+ * Defaults to DEFAULT_MAX_STEPS (16) when omitted or undefined.
43
+ * Values <= 0 are treated as "use the default" (not "unlimited").
44
+ * To raise the limit pass a larger number; there is no way to disable
45
+ * the cap entirely -- set a very large value (e.g. 10_000) if needed. */
46
+ maxSteps?: number;
47
+ /** Input and output guardrails. Input guardrails run before each LLM call;
48
+ * output guardrails run after each step's response is produced.
49
+ * A tripwire decision halts the run with finishReason 'guardrail'. */
50
+ guardrails?: Guardrail[];
51
+ /** Permission policy wired into the tool-execution path.
52
+ * Called after lookup, before execution.
53
+ * 'allow' -> proceed; 'deny' -> tool is blocked (error result to model);
54
+ * 'ask' -> call the `approve` callback for a human decision. */
55
+ policy?: PermissionPolicy;
56
+ /** Human-in-the-loop approver called when a policy rule says 'ask'.
57
+ * The loop suspends until the returned Promise resolves.
58
+ * The approver MUST always resolve (never reject) — return { decision: 'deny' }
59
+ * to block when the approval channel itself fails. */
60
+ approve?: (req: ApprovalRequest) => Promise<ApprovalDecision>;
61
+ /** Durable checkpoint storage for the loop snapshot.
62
+ * When set, the loop persists its state (including pending approvals) at every
63
+ * approval suspension point, enabling kill-process / restore / resume flows.
64
+ * Must be cross-env: use MemoryPersistence for browser/tests, FilePersistence for Node.
65
+ * When omitted, state is kept in-memory only. */
66
+ checkpoint?: Persistence;
67
+ }
@@ -0,0 +1,46 @@
1
+ /** Private helpers for AgentLoop — extracted from the two god-functions.
2
+ * Not exported from the library public surface. */
3
+ import type { HookBus } from '../bus/hook-bus';
4
+ import type { ToolCallPart, ContentPart } from '../llm/types/messages';
5
+ import { type CompletionResponse } from '../llm/types/response';
6
+ import type { StreamEvent } from '../llm/types/stream';
7
+ import type { TraceContext } from '../network/types';
8
+ import type { AgentStreamEvent, AgentTool, ToolCallReport, ToolExecutionContext } from './types';
9
+ import type { StepState } from './loop-step-state';
10
+ /** Create a fresh StepState for the start of a streaming step. */
11
+ export declare function makeStepState(): StepState;
12
+ /** Accumulate one SSE StreamEvent into StepState.
13
+ * Returns the AgentStreamEvent to yield upstream, or null if nothing to yield. */
14
+ export declare function accumulateStreamEvent(event: StreamEvent, state: StepState): AgentStreamEvent | null;
15
+ /** Finalize any tool calls that never received a tool_call_end event
16
+ * (Anthropic/OpenAI streaming quirk). Mutates state.stepToolCalls. */
17
+ export declare function finalizeUnendedToolCalls(state: StepState): void;
18
+ /** Build the CompletionResponse for a completed streaming step. */
19
+ export declare function buildStepResponse(state: StepState, model: string, stepStart: number): {
20
+ response: CompletionResponse;
21
+ content: ContentPart[];
22
+ stepLatency: number;
23
+ };
24
+ type LookupResult = {
25
+ found: true;
26
+ tool: AgentTool;
27
+ } | {
28
+ found: false;
29
+ errorResult: ContentPart;
30
+ };
31
+ /** Resolve a tool by name; emit not-found hooks and push an error report.
32
+ * Returns found tool or an error ContentPart to return to the model. */
33
+ export declare function lookupToolOrError(tc: ToolCallPart, tools: Map<string, AgentTool>, hooks: HookBus, runId: string, agentId: string, step: number, metrics: Map<string, {
34
+ value: number | string | boolean;
35
+ type: string;
36
+ }>, reports: ToolCallReport[], toolStart: number, runTrace?: TraceContext): Promise<LookupResult>;
37
+ /** Execute a tool with an AbortController-based timeout.
38
+ * Builds a ToolExecutionContext with the timeout signal internally. */
39
+ export declare function executeWithTimeout(tool: AgentTool, tc: ToolCallPart, baseCtx: Omit<ToolExecutionContext, 'signal'>, timeoutMs: number): Promise<string | ContentPart[]>;
40
+ /** Handle a tool execution error: emit onToolCallError, push report, return fallback result.
41
+ * Throws if the hook sets continueOnError = false. */
42
+ export declare function handleToolError(e: unknown, tc: ToolCallPart, hooks: HookBus, runId: string, agentId: string, step: number, metrics: Map<string, {
43
+ value: number | string | boolean;
44
+ type: string;
45
+ }>, reports: ToolCallReport[], toolStart: number, runTrace?: TraceContext): Promise<ContentPart>;
46
+ export {};
@@ -0,0 +1,20 @@
1
+ /** Mutable accumulator for a single agent loop step.
2
+ * Passed through stream-event helpers so they don't fight closure state. */
3
+ import type { ToolCallPart } from '../llm/types/messages';
4
+ import type { Usage } from '../llm/types/response';
5
+ /** Accumulation bucket for one in-progress tool call (before tool_call_end). */
6
+ export interface ToolCallAccumEntry {
7
+ id: string;
8
+ name: string;
9
+ args: string;
10
+ _meta?: Record<string, unknown>;
11
+ }
12
+ /** All mutable state for one streaming step inside AgentLoop.stream(). */
13
+ export interface StepState {
14
+ stepText: string;
15
+ stepThinking: string;
16
+ stepToolCalls: ToolCallPart[];
17
+ toolCallAccum: Map<string, ToolCallAccumEntry>;
18
+ stepUsage: Usage;
19
+ stepFinishReason: string;
20
+ }
@@ -0,0 +1,133 @@
1
+ /** AgentLoop — Layer 3 multi-step orchestration.
2
+ *
3
+ * Drives a conversation through `client.complete` / `client.stream` until
4
+ * the model returns a response without tool calls (or `stop()` is called).
5
+ * Owns: history, executable tools, agent-level hooks, run reports.
6
+ *
7
+ * Reshape vs old llm-sdk loop:
8
+ * - `model` removed from config — read from `client.model`.
9
+ * - `system` lives in history.registry as `agentloop.system` layer.
10
+ * - `complete(input, options?)` returns CompletionResponse (not AgentRunResult).
11
+ * - `lastReport` getter exposes run-level details (step/tool reports).
12
+ * - Input shapes (`string | ContentPart[] | Message[]`) APPEND to history. */
13
+ import { HookBus } from '../bus/hook-bus';
14
+ import { type ContentPart, type Message } from '../llm/types/messages';
15
+ import type { ExecuteOptions } from '../llm/types/options';
16
+ import { type CompletionResponse } from '../llm/types/response';
17
+ import type { LLMClient } from '../llm/client';
18
+ import { ConversationHistory } from './history';
19
+ import type { AgentLoopSnapshot, AgentRunReport, AgentStreamEvent, AgentTool } from './types';
20
+ import type { AgentLoopConfig } from './loop-config';
21
+ import type { PermissionPolicy } from '../plugins/permissions/policy';
22
+ import type { ApprovalRequest, ApprovalDecision, PendingToolCall } from './approval-types';
23
+ export declare class AgentLoop {
24
+ readonly id: string;
25
+ readonly client: LLMClient;
26
+ readonly hooks: HookBus;
27
+ private _system;
28
+ private _systemThunk;
29
+ private _context;
30
+ private _tools;
31
+ private _history;
32
+ private _reports;
33
+ private _metadata;
34
+ private _maxTokens?;
35
+ private _temperature?;
36
+ private _thinking?;
37
+ private _cache?;
38
+ private _parallelToolCalls;
39
+ private _toolTimeout;
40
+ private _maxSteps;
41
+ private _guardrails;
42
+ private _policy;
43
+ private _approve;
44
+ private _checkpoint;
45
+ /** Tool calls suspended awaiting human approval (populated during a durable pause). */
46
+ private _pendingToolCalls;
47
+ private _running;
48
+ private _stopRequested;
49
+ private _abortController;
50
+ constructor(config: AgentLoopConfig);
51
+ destroy(): void;
52
+ /** Model is owned by client. */
53
+ get model(): string;
54
+ get system(): string;
55
+ set system(v: string);
56
+ get context(): string;
57
+ set context(v: string);
58
+ get history(): ConversationHistory;
59
+ get running(): boolean;
60
+ get reports(): readonly AgentRunReport[];
61
+ get lastReport(): AgentRunReport | null;
62
+ get metadata(): Record<string, unknown>;
63
+ addTool(tool: AgentTool): void;
64
+ removeTool(name: string): void;
65
+ toolNames(): string[];
66
+ stop(): void;
67
+ complete(input: string | ContentPart[] | Message[], options?: ExecuteOptions): Promise<CompletionResponse>;
68
+ /** Run `complete` with a JSON Schema enforced via `structured`, then
69
+ * JSON.parse the response text. Tool calls are still allowed within the
70
+ * loop; only the FINAL turn is constrained. */
71
+ structuredComplete<T = unknown>(input: string | ContentPart[] | Message[], schema: Record<string, unknown>, options?: ExecuteOptions): Promise<T>;
72
+ stream(input: string | ContentPart[] | Message[], options?: ExecuteOptions): AsyncIterable<AgentStreamEvent>;
73
+ /** Yield tool_call_start events for all tool calls in this step. */
74
+ private emitToolCallStarts;
75
+ /** Yield tool_call_end events for all completed tool reports. */
76
+ private emitToolCallEnds;
77
+ private executeToolCalls;
78
+ private executeSingleTool;
79
+ /** Build a skipped-tool result content part and push the report. */
80
+ private buildSkippedResult;
81
+ /** Build an overridden-tool result content part and push the report. */
82
+ private buildOverriddenResult;
83
+ /** Build a denied-tool result (policy deny) and push the report. */
84
+ private buildDeniedResult;
85
+ /** Suspend at the approval gate: emit hooks, optionally persist, await decision. */
86
+ private runApprovalGate;
87
+ /** Emit onToolCallComplete, push report, and return success content part. */
88
+ private buildSuccessResult;
89
+ /** Merge agent's tool definitions with caller-provided tools (caller wins on conflict). */
90
+ private toolDefinitions;
91
+ private beginRun;
92
+ private finalizeRun;
93
+ /** Run all input-kind guardrails in order. Returns the trip reason on the first
94
+ * tripwire, or null when all pass. Emits onGuardrailTriggered on trip. */
95
+ private runInputGuardrails;
96
+ /** Run all output-kind guardrails in order. Returns the trip reason on the first
97
+ * tripwire, or null when all pass. Emits onGuardrailTriggered on trip. */
98
+ private runOutputGuardrails;
99
+ dump(): AgentLoopSnapshot;
100
+ static restore(snapshot: AgentLoopSnapshot, config: {
101
+ client: LLMClient;
102
+ hooks?: HookBus;
103
+ tools: AgentTool[];
104
+ policy?: PermissionPolicy;
105
+ approve?: (req: ApprovalRequest) => Promise<ApprovalDecision>;
106
+ checkpoint?: import('../plugins/persistence/types').Persistence;
107
+ }): AgentLoop;
108
+ /** Return the list of tool calls currently suspended awaiting approval. */
109
+ get pendingApprovals(): readonly PendingToolCall[];
110
+ /** Feed an approval decision for a pending tool call identified by callId.
111
+ *
112
+ * This is the resume entry point after a process restart:
113
+ * 1. Restore the loop from a snapshot (AgentLoop.restore).
114
+ * 2. Call resumeWithApproval(callId, decision) to remove the pending record.
115
+ * 3. Re-run the agent — the approver will be invoked again with the SAME
116
+ * callId; provide a pass-through approver that returns the pre-fed decision.
117
+ *
118
+ * Rationale: the tool call itself is not stored in pending state (only its
119
+ * metadata), because the actual execution requires re-running the LLM step.
120
+ * The canonical resume model is: restore -> re-complete with an approver that
121
+ * returns the already-obtained decision for the known callId. This keeps the
122
+ * loop stateless w.r.t. execution and the approval decision authoritative. */
123
+ resumeWithApproval(callId: string, decision: ApprovalDecision): void;
124
+ /** Pre-fed decisions supplied via resumeWithApproval — consumed on the next run. */
125
+ private _prefedApprovals;
126
+ /** Return a prefed decision (consumed once) or fall back to the real approver. */
127
+ private resolveApproval;
128
+ }
129
+ /** Default maximum number of tool-followup steps per agent run.
130
+ * Callers that need more steps should set `maxSteps` in AgentLoopConfig. */
131
+ export declare const DEFAULT_MAX_STEPS = 16;
132
+ /** Default tool execution timeout in milliseconds. */
133
+ export declare const DEFAULT_TOOL_TIMEOUT_MS = 30000;
@@ -0,0 +1,3 @@
1
+ /** Registry key for an AgentTool: the function name, else the builtin type. */
2
+ import type { AgentTool } from './types';
3
+ export declare function toolKey(tool: AgentTool): string;
@@ -0,0 +1,138 @@
1
+ /** Agent-layer shared types — TokenCounter contract used by ContextRegistry,
2
+ * ConversationHistory, and the ContextMeasurer plugin.
3
+ * Also defines AgentTool (executable tool) and run-report types. */
4
+ import type { ContentPart, Message } from '../llm/types/messages';
5
+ import type { Tool } from '../llm/types/tools';
6
+ import type { Usage } from '../llm/types/response';
7
+ import type { HistorySnapshot } from './history-types';
8
+ import type { PendingToolCall } from './approval-types';
9
+ import type { TraceContext } from '../network/types';
10
+ export type ContentClass = 'prose' | 'code' | 'mixed' | 'structured';
11
+ export interface TokenCountContext {
12
+ provider?: string;
13
+ model?: string;
14
+ contentClass?: ContentClass;
15
+ accuracy?: 'fast' | 'exact';
16
+ }
17
+ export interface TokenCounter {
18
+ /** Fast estimate — synchronous, no I/O. Used on every request. */
19
+ estimate(text: string, ctx?: TokenCountContext): number;
20
+ /** Estimate for a whole message, including multi-part content. */
21
+ estimateMessage(msg: Message, ctx?: TokenCountContext): number;
22
+ /** Accurate count — may perform I/O (lazy tokenizer load, API call). */
23
+ measure(text: string, ctx?: TokenCountContext): Promise<number>;
24
+ /** Accurate count for a whole message. */
25
+ measureMessage(msg: Message, ctx?: TokenCountContext): Promise<number>;
26
+ /** Feed actual usage for calibration refinement. */
27
+ learn(input: LearnInput): void;
28
+ }
29
+ export interface LearnInput {
30
+ provider: string;
31
+ model: string;
32
+ bytesSent: number;
33
+ actualTokens: number;
34
+ contentClass?: ContentClass;
35
+ timestamp?: number;
36
+ }
37
+ export interface AgentTool {
38
+ /** Tool schema sent to the LLM. */
39
+ definition: Tool;
40
+ /** Execute the tool. Return string or structured content. */
41
+ execute: (args: Record<string, unknown>, context: ToolExecutionContext) => Promise<string | ContentPart[]>;
42
+ }
43
+ export interface ToolExecutionContext {
44
+ step: number;
45
+ callId: string;
46
+ signal: AbortSignal;
47
+ metrics: Map<string, {
48
+ value: number | string | boolean;
49
+ type: string;
50
+ }>;
51
+ /** Run trace identity: sessionId = agentId (ConversationHistory id),
52
+ * requestId = runId for this .complete()/.stream() invocation,
53
+ * callId = this tool call's id (same as ctx.callId). */
54
+ trace?: TraceContext;
55
+ }
56
+ export interface ToolCallReport {
57
+ callId: string;
58
+ toolName: string;
59
+ arguments: Record<string, unknown>;
60
+ resultSizeBytes: number;
61
+ latencyMs: number;
62
+ skipped: boolean;
63
+ error: string | null;
64
+ metrics: Record<string, {
65
+ value: number | string | boolean;
66
+ type: string;
67
+ }>;
68
+ }
69
+ export interface StepReport {
70
+ index: number;
71
+ type: 'initial' | 'tool_followup';
72
+ llmLatencyMs: number;
73
+ usage: Usage;
74
+ finishReason: string;
75
+ toolCalls: ToolCallReport[];
76
+ toolTotalMs: number;
77
+ }
78
+ export interface AgentRunReport {
79
+ id: string;
80
+ model: string;
81
+ startedAt: number;
82
+ completedAt: number;
83
+ totalMs: number;
84
+ reason: 'done' | 'stopped' | 'error' | 'guardrail' | 'max_steps';
85
+ userMessage: string | ContentPart[] | Message[];
86
+ finalText: string;
87
+ error?: string;
88
+ steps: StepReport[];
89
+ stepCount: number;
90
+ toolCallCount: number;
91
+ totalUsage: Usage;
92
+ totalLlmTimeMs: number;
93
+ totalToolTimeMs: number;
94
+ }
95
+ export type AgentStreamEvent = {
96
+ type: 'step_start';
97
+ step: number;
98
+ } | {
99
+ type: 'text';
100
+ text: string;
101
+ } | {
102
+ type: 'thinking';
103
+ text: string;
104
+ } | {
105
+ type: 'tool_call_start';
106
+ step: number;
107
+ callId: string;
108
+ toolName: string;
109
+ arguments: Record<string, unknown>;
110
+ } | {
111
+ type: 'tool_call_end';
112
+ step: number;
113
+ callId: string;
114
+ latencyMs: number;
115
+ } | {
116
+ type: 'step_end';
117
+ step: number;
118
+ usage: Usage;
119
+ latencyMs: number;
120
+ } | {
121
+ type: 'done';
122
+ response: import('../llm/types/response').CompletionResponse;
123
+ };
124
+ export interface AgentLoopSnapshot {
125
+ version: 1;
126
+ system: string;
127
+ context: string;
128
+ history: HistorySnapshot;
129
+ toolNames: string[];
130
+ reports: AgentRunReport[];
131
+ metadata: Record<string, unknown>;
132
+ createdAt: number;
133
+ savedAt: number;
134
+ /** Tool calls suspended awaiting human approval. Present when the loop was
135
+ * checkpointed at an approval gate. Empty or absent otherwise. */
136
+ pendingToolCalls?: PendingToolCall[];
137
+ }
138
+ export type { PendingToolCall };
@@ -0,0 +1,99 @@
1
+ /** AgentBus — cross-module event bus for agent systems.
2
+ *
3
+ * Distinct from HookBus:
4
+ * - HookBus carries SDK-internal instrumentation (onCompletion, onContextMeasure,
5
+ * onToolCallStart, ...). Strongly typed per-event.
6
+ * - AgentBus carries business events between agent modules and the app layer
7
+ * (ask.permission, log.progress, notify, lifecycle.*). Open envelope,
8
+ * user-declared kinds, pattern matching on kind names.
9
+ *
10
+ * Pattern matching rules for `on(kindPattern, handler)`:
11
+ * - Exact match: 'ask.permission' matches only 'ask.permission'
12
+ * - Prefix match: 'ask.*' matches 'ask.permission', 'ask.choice', 'ask.anything'
13
+ * - Wildcard: '*' matches everything
14
+ *
15
+ * Correlation IDs link request events to reply events:
16
+ * bus.emit({ kind: 'ask.permission', correlationId: 'q1', payload: {...} });
17
+ * bus.onReply('q1', (replyEvent) => ...);
18
+ * // Elsewhere:
19
+ * bus.reply('q1', { kind: 'ask.answer', source: 'user', payload: { granted: true } });
20
+ *
21
+ * Ordering guarantees (by design — see orchestrator decomposition spec):
22
+ * - Top-level `emit()` calls are processed FIFO globally. Two parallel
23
+ * `emit(A)` and `emit(B)` complete in submission order.
24
+ * - Reentrant `emit()` (called from within a handler) runs depth-first:
25
+ * the nested event's handlers complete before the next sibling handler
26
+ * of the outer event runs. This preserves causal order and avoids
27
+ * deadlock when a handler awaits its own nested emit. */
28
+ export interface AgentEvent {
29
+ /** Stable unique id assigned by emit() if not supplied. Used by Journal,
30
+ * Aggregator, ResumeManager to reference specific events. */
31
+ id: string;
32
+ /** Logical identity of the emitter (usually a module id). */
33
+ source: string;
34
+ /** Dot-delimited kind, e.g. 'ask.permission', 'log.progress', 'lifecycle.created'. */
35
+ kind: string;
36
+ /** Kind-specific payload. Caller and listener agree on shape by convention. */
37
+ payload: unknown;
38
+ /** Milliseconds since epoch — filled in by emit() if not provided. */
39
+ timestamp: number;
40
+ /** Optional correlation ID linking a reply or progress update to its origin. */
41
+ correlationId?: string;
42
+ /** Optional id of the parent event (set by callers when they emit from
43
+ * within a handler and want to record the causal link). The bus does not
44
+ * populate this automatically — callers opt in. */
45
+ causedBy?: string;
46
+ }
47
+ /** Emit form — id and timestamp filled in by bus if absent. */
48
+ export type AgentEventInput = Omit<AgentEvent, 'id' | 'timestamp'> & {
49
+ id?: string;
50
+ timestamp?: number;
51
+ };
52
+ export type AgentEventHandler = (event: AgentEvent) => void | Promise<void>;
53
+ export interface SubscribeOptions {
54
+ /** Optional subscriber identity. Stored on the subscription so future
55
+ * components (Wiring, Journal, Aggregator) can record which subscriber
56
+ * processed which event. The bus itself does not introspect it today. */
57
+ name?: string;
58
+ }
59
+ /** `handlerContext` marks "we are currently inside a handler frame". Reentrant
60
+ * emits (called from a handler, possibly across awaits) inherit this context;
61
+ * external emits do not. This is how we tell "nested" from "concurrent
62
+ * external" when both happen while a slow handler is suspended. Imported from
63
+ * ./async-context so the browser build can swap AsyncLocalStorage for a
64
+ * no-node:async_hooks fallback via the package "browser" field. */
65
+ /** Lightweight, asynchronous, pattern-matched event bus. */
66
+ export declare class AgentBus {
67
+ /** Pattern → subscribers. Exact patterns and prefix patterns stored separately for speed. */
68
+ private exact;
69
+ private prefix;
70
+ private wildcard;
71
+ /** correlationId → reply subscribers. Cleared when handler unsubscribes. */
72
+ private replies;
73
+ /** FIFO queue for top-level emits. Reentrant emits bypass the queue. */
74
+ private queue;
75
+ private draining;
76
+ /** Subscribe to events whose kind matches the pattern.
77
+ * - 'foo.bar' → exact
78
+ * - 'foo.*' → prefix 'foo.' (matches 'foo.bar', 'foo.baz.qux', etc.)
79
+ * - '*' → all
80
+ * Returns unsubscribe function. */
81
+ on(kindPattern: string, handler: AgentEventHandler, options?: SubscribeOptions): () => void;
82
+ /** Subscribe to all events carrying this correlation ID. Returns unsubscribe. */
83
+ onReply(correlationId: string, handler: AgentEventHandler, options?: SubscribeOptions): () => void;
84
+ /** Publish an event. Top-level calls are queued and processed FIFO globally;
85
+ * reentrant calls (from inside a handler) run depth-first.
86
+ * Resolves once all handlers for this event have completed. */
87
+ emit(input: AgentEventInput): Promise<void>;
88
+ /** Convenience: publish a reply carrying the given correlationId.
89
+ * Ensures correlationId is set (caller can't forget it). */
90
+ reply(correlationId: string, input: Omit<AgentEventInput, 'correlationId'>): Promise<void>;
91
+ /** Number of registered handlers (sum across exact + prefix + wildcard + reply).
92
+ * Useful for leak detection in tests. */
93
+ get handlerCount(): number;
94
+ /** Remove all handlers. Use during teardown. */
95
+ clear(): void;
96
+ private drain;
97
+ private processEvent;
98
+ private emitInternalError;
99
+ }
@@ -0,0 +1,13 @@
1
+ /** Browser reentrancy context — a single shared flag, no node:async_hooks.
2
+ *
3
+ * AsyncLocalStorage does not exist in browsers. We keep one module-scoped flag
4
+ * and hold it for the WHOLE duration of handler dispatch (including while the
5
+ * async handler loop is suspended), restoring it when dispatch fully settles.
6
+ *
7
+ * Trade-off vs. the Node AsyncLocalStorage impl: a genuinely concurrent
8
+ * top-level emit that arrives while a handler is suspended sees the flag set
9
+ * and runs inline instead of FIFO-queued. That softly weakens global ordering
10
+ * but never deadlocks — acceptable for browser, single-user usage. Reentrant
11
+ * (nested) emits, the case that matters for correctness, are detected exactly. */
12
+ import type { HandlerContext } from './async-context.types';
13
+ export declare const handlerContext: HandlerContext;
@@ -0,0 +1,11 @@
1
+ /** Node/Bun reentrancy context — backed by AsyncLocalStorage.
2
+ *
3
+ * AsyncLocalStorage propagates through awaits, so a reentrant emit (made from
4
+ * inside a handler, even after the handler suspended) sees the store while a
5
+ * genuinely concurrent external emit does not. This is what lets AgentBus tell
6
+ * "nested" from "concurrent external" with full correctness on the server.
7
+ *
8
+ * The browser build never loads this file: the package "browser" field maps it
9
+ * to ./async-context.browser.ts, which has no node:async_hooks dependency. */
10
+ import type { HandlerContext } from './async-context.types';
11
+ export declare const handlerContext: HandlerContext;
@@ -0,0 +1,14 @@
1
+ /** Shared shape for the reentrancy context used by AgentBus.
2
+ *
3
+ * Two implementations satisfy it:
4
+ * - async-context.ts → Node/Bun, backed by AsyncLocalStorage
5
+ * (full isolation across awaits and concurrent emits).
6
+ * - async-context.browser.ts → browser, a single shared flag held for the
7
+ * duration of handler dispatch (no node:async_hooks). The package "browser"
8
+ * field swaps the former for the latter at bundle time. */
9
+ export interface HandlerContext {
10
+ /** Run `fn` with the store set to `value`. Returns whatever `fn` returns. */
11
+ run<R>(value: boolean, fn: () => R): R;
12
+ /** The current store value, or undefined when outside any `run`. */
13
+ getStore(): boolean | undefined;
14
+ }