@orkestrel/agent 0.0.10 → 0.0.12

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.
@@ -11,6 +11,12 @@ import { RunnerInterface } from '@orkestrel/workflow';
11
11
  import { SchedulerInterface } from '@orkestrel/workflow';
12
12
  import { TableInterface } from '@orkestrel/database';
13
13
  import { TokenUsage } from '@orkestrel/budget';
14
+ import { ToolCall } from '@orkestrel/tool';
15
+ import { ToolDefinition } from '@orkestrel/tool';
16
+ import { ToolInterface } from '@orkestrel/tool';
17
+ import { ToolManagerInterface } from '@orkestrel/tool';
18
+ import { ToolResult } from '@orkestrel/tool';
19
+ import { WorkspaceManagerInterface } from '@orkestrel/workspace';
14
20
 
15
21
  /**
16
22
  * The agent loop — composes a {@link ProviderInterface}, an {@link AgentContext}, and
@@ -100,15 +106,17 @@ export declare type AgentChunk = {
100
106
  * The richer turn context the agent loop assembles a provider request from — the optional
101
107
  * system prompt, the observable context managers (instructions / workspaces), the
102
108
  * {@link ConversationManagerInterface} message source (whose active conversation IS `messages`),
103
- * the {@link ToolManagerInterface} registry, and a mutable active {@link ScopeInterface}.
109
+ * the {@link ToolManagerInterface} registry, and an active {@link ScopeInterface} changed through
110
+ * {@link AgentContextInterface.apply}.
104
111
  *
105
112
  * @remarks
106
113
  * - **Composition.** `system` is the optional system prompt; `instructions` / `tools` /
107
114
  * `workspaces` / `conversations` are the registries passed in `options` (bring your own), or
108
115
  * fresh empty ones when omitted (so `workspaces` is ALWAYS present); `messages` is the ACTIVE
109
116
  * conversation's live tail (ALWAYS defined — see below). `scope` is the active filter —
110
- * `undefined` (the default) ⇒ no filtering; settable afterwards. `workspaces` / `conversations`
111
- * are likewise SETTABLE (swap the whole registry between runs).
117
+ * `undefined` (the default) ⇒ no filtering; change it through `apply(scope)`. The structural
118
+ * `workspaces` / `conversations` registries are fixed at construction; switch their active
119
+ * members through their own `switch(id)` methods.
112
120
  * - **The message source — the conversation registry's ACTIVE conversation.** `conversations` is a
113
121
  * {@link ConversationManagerInterface}; the context ENSURES it always has an active conversation
114
122
  * (at construction it `add`s a default when the manager has none), so the DYNAMIC `messages`
@@ -167,13 +175,11 @@ export declare class AgentContext implements AgentContextInterface {
167
175
  get system(): string | undefined;
168
176
  get instructions(): InstructionManagerInterface;
169
177
  get workspaces(): WorkspaceManagerInterface;
170
- set workspaces(value: WorkspaceManagerInterface);
171
178
  get messages(): MessageManagerInterface;
172
179
  get conversations(): ConversationManagerInterface;
173
- set conversations(value: ConversationManagerInterface);
174
180
  get tools(): ToolManagerInterface;
175
181
  get scope(): ScopeInterface | undefined;
176
- set scope(value: ScopeInterface | undefined);
182
+ apply(scope: ScopeInterface | undefined): void;
177
183
  build(format?: ContextFormatInterface): readonly MessageInterface[];
178
184
  }
179
185
 
@@ -182,8 +188,8 @@ export declare class AgentContext implements AgentContextInterface {
182
188
  * the conversation, applying the active scope per category.
183
189
  *
184
190
  * @remarks
185
- * The richer context — `system` (the optional system prompt), the context managers
186
- * (`instructions` / `tools` / `workspaces` / `conversations`), `messages` (the active
191
+ * The richer context — `system` (the optional system prompt), the prompt context managers
192
+ * (`instructions` / `workspaces` / `conversations`), `messages` (the active
187
193
  * conversation's live tail, satisfying {@link MessageManagerInterface}), and the current `scope`
188
194
  * (the active {@link ScopeInterface} filter, or `undefined` for no filtering). `build()` folds the
189
195
  * scoped instructions into ONE leading `system` message (under the manager's `description`,
@@ -214,8 +220,8 @@ export declare interface AgentContextInterface {
214
220
  * construction). It IS the active {@link ConversationInterface} itself (which satisfies
215
221
  * {@link MessageManagerInterface} structurally), so appends through `messages` route to the
216
222
  * active conversation's tail and `build()` folds its `view()`. Computed dynamically (it follows
217
- * a `conversations.switch(id)` or a `conversations` swap), the SAME reference the active
218
- * conversation exposes — no duplication.
223
+ * `conversations.switch(id)`), the SAME reference the active conversation exposes — no
224
+ * duplication.
219
225
  */
220
226
  readonly messages: MessageManagerInterface;
221
227
  /**
@@ -225,12 +231,28 @@ export declare interface AgentContextInterface {
225
231
  * `messages` is always defined. Switch the active conversation through
226
232
  * `conversations.switch(id)` — so one agent can serve MANY conversations (set the active one per
227
233
  * request). Switch BETWEEN runs, not during one; for CONCURRENT threads use separate agents (see
228
- * clause 25).
234
+ * clause 23).
229
235
  */
230
236
  readonly conversations: ConversationManagerInterface;
237
+ /**
238
+ * The loop's tool registry for provider advertising and call dispatch. Tools are structural
239
+ * loop machinery and never render into `build()`'s prompt.
240
+ */
231
241
  readonly tools: ToolManagerInterface;
232
242
  /** The active scope applied at `build()` time + the loop's tool-advertise step (`undefined` ⇒ no filtering). */
233
243
  readonly scope: ScopeInterface | undefined;
244
+ /**
245
+ * Apply the active per-turn scope filter. Passing `undefined` removes filtering.
246
+ *
247
+ * @param scope - The scope to apply, or `undefined` to remove the active filter
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * context.apply(scope)
252
+ * context.apply(undefined)
253
+ * ```
254
+ */
255
+ apply(scope: ScopeInterface | undefined): void;
234
256
  /**
235
257
  * The provider input for the next turn: a leading `system` message folding the prompt
236
258
  * + the scoped instructions + the ACTIVE workspace's scoped-in TEXT files (rendered as
@@ -240,12 +262,13 @@ export declare interface AgentContextInterface {
240
262
  *
241
263
  * @remarks
242
264
  * **The active workspace (rendered by carrier) — the SOLE document/image context.** When
243
- * `workspaces.active` is set, its {@link WorkspaceInterface.files} are filtered by
265
+ * `workspaces.active` is set, its
266
+ * {@link import('@orkestrel/workspace').WorkspaceInterface.files} are filtered by
244
267
  * `scope.files` (a three-way allow-list; `undefined` ⇒ all active files), then split by
245
- * carrier: TEXT files ({@link import('./helpers.js').isText}) render into a dedicated
268
+ * carrier: TEXT files ({@link import('@orkestrel/workspace').isText}) render into a dedicated
246
269
  * `## Workspace` section in the system block — each a fenced
247
270
  * `` File: <path>\n```<language>\n<text>\n``` `` block — placed just after the instructions
248
- * section; IMAGE files ({@link import('./helpers.js').isImage}) have their base64 `data`
271
+ * section; binary files whose MIME starts with `image/` have their base64 `data`
249
272
  * attached to the LAST user message (a vision provider reads images off a user turn).
250
273
  * ACTIVE-ONLY — never the other registered workspaces; with NO active workspace nothing is
251
274
  * rendered for workspaces.
@@ -291,21 +314,25 @@ export declare interface AgentContextInterface {
291
314
  * Options for `createAgentContext` — the richer context's configuration.
292
315
  *
293
316
  * @remarks
294
- * `system` is the optional system prompt prepended to the turn's input. `tools` /
295
- * `instructions` / `workspaces` are optional pre-built managers to reuse (bring your own
296
- * registry); when one is omitted, the context creates a fresh empty one. `scope` is the
317
+ * `system` is the optional system prompt prepended to the turn's input. `instructions` /
318
+ * `workspaces` are optional pre-built context managers to reuse (bring your own registry);
319
+ * when one is omitted, the context creates a fresh empty one. `tools` supplies the loop's
320
+ * call-dispatch and provider-advertising registry; it never renders into the prompt. `scope` is the
297
321
  * initial active filter applied at `build()` time (and at the loop's tool-advertise step); it
298
- * defaults to `undefined` — no filtering — and is mutable through the context's `scope` setter
299
- * afterwards. `conversations` is the {@link ConversationManagerInterface} the context's message
300
- * source flows from: `messages` IS the manager's ACTIVE conversation's live tail and `build()`
301
- * folds that conversation's `view()` (section summaries + live). When omitted, a fresh
322
+ * defaults to `undefined` — no filtering — and can be changed through the context's `apply`
323
+ * method afterwards. `conversations` is the structural {@link ConversationManagerInterface} the
324
+ * context's message source flows from: `messages` IS the manager's ACTIVE conversation's live tail
325
+ * and `build()` folds that conversation's `view()` (section summaries + live). When omitted, a fresh
302
326
  * {@link ConversationManagerInterface} is created and a default conversation is added (so
303
327
  * `messages` is ALWAYS defined). All default to a context with no system prompt, empty registries,
304
328
  * no scope, and a fresh conversation registry holding one default conversation.
305
329
  */
306
330
  export declare interface AgentContextOptions {
307
331
  readonly system?: string;
308
- /** A pre-built tool registry to reuse; an empty one is created when omitted. */
332
+ /**
333
+ * The loop's pre-built tool registry for provider advertising and call dispatch; an empty one is
334
+ * created when omitted. Tools never render into `build()`'s prompt.
335
+ */
309
336
  readonly tools?: ToolManagerInterface;
310
337
  /** A pre-built instruction registry to reuse; an empty one is created when omitted. */
311
338
  readonly instructions?: InstructionManagerInterface;
@@ -313,8 +340,8 @@ export declare interface AgentContextOptions {
313
340
  * A pre-built {@link WorkspaceManagerInterface} to reuse; a fresh empty one is created when
314
341
  * omitted (so `context.workspaces` is ALWAYS present). `build()` renders the ACTIVE workspace's
315
342
  * files by carrier — text files into the system block (fenced), image files attached to the last
316
- * user message — and is mutable through the context's `workspaces` setter afterwards. The active
317
- * workspace is the SOLE document/image context.
343
+ * user message. The registry is structural; change its active workspace through
344
+ * `workspaces.switch(id)`. The active workspace is the SOLE document/image context.
318
345
  */
319
346
  readonly workspaces?: WorkspaceManagerInterface;
320
347
  /** The initial active scope (the build-time filter); `undefined` ⇒ no filtering. */
@@ -326,9 +353,8 @@ export declare interface AgentContextOptions {
326
353
  * conversation's LIVE tail — is ALWAYS defined. `build()` folds the active conversation's
327
354
  * `view()` (the per-section summaries + the live tail) as its AUTHORITATIVE message inclusion —
328
355
  * the scope does NOT filter the conversation (it owns inclusion via compaction; scope filters
329
- * only instructions / tools / workspace files). A SETTABLE mutable
330
- * property afterwards (the context's `conversations` setter — swap the whole registry between
331
- * runs), and the active conversation is switchable through the manager's `switch(id)`.
356
+ * only instructions / tools / workspace files). The registry is structural; change its active
357
+ * conversation through the manager's `switch(id)`.
332
358
  */
333
359
  readonly conversations?: ConversationManagerInterface;
334
360
  }
@@ -932,8 +958,8 @@ export declare type AgentStreamInterface = StreamInterface<AgentChunk, AgentResu
932
958
  * (an ALLOWLIST — only matched rules that allow get through), pass an `allowed: false`
933
959
  * `fallback`.
934
960
  * - **Consulted before each tool call.** The agent loop calls `evaluate({ call })` for
935
- * every {@link import('./types.js').ToolCall} the model emits; a denied call is fed
936
- * back to the model as a denial {@link import('./types.js').ToolResult} (a `tool`
961
+ * every {@link import('@orkestrel/tool').ToolCall} the model emits; a denied call is fed
962
+ * back to the model as a denial {@link import('@orkestrel/tool').ToolResult} (a `tool`
937
963
  * chunk + a tool message) instead of being executed — no tool run, no budget cost —
938
964
  * so the model sees the denial and can react.
939
965
  * - **Synchronous now.** The async human-approval handshake (request / grant / deny) is
@@ -1038,37 +1064,6 @@ export declare interface AuthorityRule {
1038
1064
  readonly reason?: string;
1039
1065
  }
1040
1066
 
1041
- /**
1042
- * The binary MIME types a {@link FileContent} binary arm carries — the labels for a
1043
- * base64-encoded payload.
1044
- *
1045
- * @remarks
1046
- * Initialized to the four image MIMEs a vision-capable provider accepts; the union is OPEN
1047
- * by design — a future binary MIME (e.g. `'application/pdf'`) slots in purely additively (a
1048
- * new member), with nothing above it changed. {@link import('./helpers.js').isImage}
1049
- * narrows a binary arm to an image by the `image/` prefix, so an image is just a binary with
1050
- * an image MIME.
1051
- */
1052
- export declare type BinaryMIME = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
1053
-
1054
- /**
1055
- * Project a {@link ToolResult} into the MCP `CallToolResult` shape — a top-level `error`
1056
- * maps to a single `isError: true` text block (the failure reason), otherwise the `value`
1057
- * is JSON-stringified into a single text block with no `isError`.
1058
- *
1059
- * @param result - The {@link ToolResult} to project (as returned by {@link import('./tools/ToolManager.js').ToolManager.execute})
1060
- * @returns The equivalent {@link ToolCallResult}
1061
- *
1062
- * @example
1063
- * ```ts
1064
- * buildToolResult({ id: 'c1', name: 'search', error: 'max depth' })
1065
- * // { content: [{ type: 'text', text: 'max depth' }], isError: true }
1066
- * buildToolResult({ id: 'c1', name: 'search', value: { count: 1 } })
1067
- * // { content: [{ type: 'text', text: '{"count":1}' }] }
1068
- * ```
1069
- */
1070
- export declare function buildToolResult(result: ToolResult): ToolCallResult;
1071
-
1072
1067
  /**
1073
1068
  * A minimal unbounded async channel — the eager pump WRITES chunks into it (`push`)
1074
1069
  * and ends it (`close` / `fail`) regardless of consumption; a consumer READS them back
@@ -1089,47 +1084,6 @@ export declare class Channel<T> {
1089
1084
  drain(): AsyncGenerator<T, void>;
1090
1085
  }
1091
1086
 
1092
- /**
1093
- * Clamp a 1-based {@link Position} to the bounds of `text` — every component pinned into a
1094
- * caret that actually exists in the content.
1095
- *
1096
- * @remarks
1097
- * `line` is pinned to `[1, lineCount]` and `column` to `[1, lineLength + 1]` of the
1098
- * resolved line (column `lineLength + 1` is the caret just past the line's last
1099
- * character). So a position beyond the end of the text resolves to the end rather than
1100
- * overflowing. Total — never throws.
1101
- *
1102
- * @param text - The text the position addresses
1103
- * @param position - The 1-based position to clamp
1104
- * @returns The clamped {@link Position}
1105
- *
1106
- * @example
1107
- * ```ts
1108
- * clampPosition('ab\ncd', { line: 9, column: 9 }) // { line: 2, column: 3 } (end of 'cd')
1109
- * ```
1110
- */
1111
- export declare function clampPosition(text: string, position: Position): Position;
1112
-
1113
- /**
1114
- * Clamp both ends of a {@link Range} to the bounds of `text` — the actual span a ranged
1115
- * read / write applies (and the `range` a {@link import('./types.js').ReadResult} reports).
1116
- *
1117
- * @remarks
1118
- * Clamps `start` and `end` independently via {@link clampPosition}, so a range reaching
1119
- * past the end of the content is trimmed to the content's end. Total — never throws.
1120
- *
1121
- * @param text - The text the range addresses
1122
- * @param range - The 1-based range to clamp
1123
- * @returns The clamped {@link Range}
1124
- *
1125
- * @example
1126
- * ```ts
1127
- * clampRange('ab\ncd', { start: { line: 1, column: 1 }, end: { line: 9, column: 9 } })
1128
- * // { start: { line: 1, column: 1 }, end: { line: 2, column: 3 } }
1129
- * ```
1130
- */
1131
- export declare function clampRange(text: string, range: Range): Range;
1132
-
1133
1087
  /**
1134
1088
  * The PER-RUN auto-compaction state threaded through an {@link AgentInterface}'s loop —
1135
1089
  * a tiny immutable value replaced within a holder created fresh per run (so no state leaks across runs or a
@@ -1165,28 +1119,6 @@ export declare interface CompactOptions {
1165
1119
  readonly sections?: number;
1166
1120
  }
1167
1121
 
1168
- /**
1169
- * Compute the byte size of a {@link FileContent} — the `size` a {@link FileInterface} derives
1170
- * once when built.
1171
- *
1172
- * @remarks
1173
- * Dispatches on {@link isText}: a text arm is its UTF-8 byte length (via `new
1174
- * TextEncoder().encode(text).length` — the standard Web/Node API, so a multi-byte
1175
- * character like `'é'` or `'😀'` counts as its real encoded bytes, NOT its character
1176
- * count); a binary arm is the decoded payload byte length of its base64 `data` (via
1177
- * {@link decodedSize}, computed arithmetically — no `atob` / `Buffer`). Total — never throws.
1178
- *
1179
- * @param content - The file content to size
1180
- * @returns The byte size (UTF-8 bytes for text; decoded payload bytes for binary)
1181
- *
1182
- * @example
1183
- * ```ts
1184
- * computeSize({ text: 'café', language: 'text' }) // 5 (é is two bytes)
1185
- * computeSize({ data: 'AAAA', mime: 'image/png' }) // 3
1186
- * ```
1187
- */
1188
- export declare function computeSize(content: FileContent): number;
1189
-
1190
1122
  /**
1191
1123
  * A provider's OPTIONAL context-framing default, keyed by section kind — the framing a
1192
1124
  * model prefers (e.g. XML tags vs. Markdown headers), declared by a
@@ -1397,7 +1329,8 @@ export declare type ConversationEventMap = {
1397
1329
  * (initial {@link ConversationEventMap} listeners). `snapshot` is
1398
1330
  * the construction-time hydration seam — a {@link ConversationSnapshot} whose `id` / `summary` /
1399
1331
  * `sections` / live tail are RESTORED into the new conversation (the live `summarize` / `keep` /
1400
- * `on` re-supplied alongside it), the conversation analogue of {@link WorkspaceInput.seed} that a
1332
+ * `on` re-supplied alongside it), the conversation analogue of
1333
+ * {@link import('@orkestrel/workspace').WorkspaceOptions}'s `seed` that a
1401
1334
  * {@link ConversationManagerInterface.open} reads a stored snapshot back through; hydration is
1402
1335
  * silent (no events). When both `snapshot.id` and `id` are given, `snapshot.id` wins (the snapshot
1403
1336
  * IS the conversation's identity).
@@ -1411,7 +1344,7 @@ export declare interface ConversationInput {
1411
1344
  /** Overrides the manager's default `sections` cap for this conversation. */
1412
1345
  readonly sections?: number;
1413
1346
  readonly on?: EmitterHooks<ConversationEventMap>;
1414
- /** A {@link ConversationSnapshot} to hydrate FROM (its `id` / `summary` / `sections` / live tail restored); the analogue of `WorkspaceInput.seed`. */
1347
+ /** A {@link ConversationSnapshot} to hydrate from, analogous to the workspace package's seed input. */
1415
1348
  readonly snapshot?: ConversationSnapshot;
1416
1349
  }
1417
1350
 
@@ -1587,8 +1520,9 @@ export declare interface ConversationInterface {
1587
1520
  *
1588
1521
  * @remarks
1589
1522
  * The container serializes ITSELF (`{ id, summary, sections, messages: this.messages() }`) — the
1590
- * {@link ConversationStoreInterface} persistence seam's payload, the EXACT analogue of
1591
- * {@link WorkspaceInterface.snapshot}. The summarizer / `keep` are NOT serialized — they are live
1523
+ * {@link ConversationStoreInterface} persistence seam's payload, the exact analogue of
1524
+ * {@link import('@orkestrel/workspace').WorkspaceInterface}'s `snapshot`. The summarizer /
1525
+ * `keep` are NOT serialized — they are live
1592
1526
  * CONFIG re-supplied on hydrate (a `ConversationSummarizer` is a function, not data). The snapshot
1593
1527
  * is the durable analogue of the constructor `seed`: a {@link ConversationManagerInterface}
1594
1528
  * HYDRATES a conversation from it through that seam (see {@link ConversationManagerInterface.open}).
@@ -1604,7 +1538,7 @@ export declare interface ConversationInterface {
1604
1538
  * The registry of {@link Conversation}s keyed by `id`, in insertion order, WITH an active pointer
1605
1539
  * — the §9 store over the conversation layer PLUS the `active` / `switch` seam the
1606
1540
  * {@link import('../AgentContext.js').AgentContext} renders. Event-free (a registry, like
1607
- * {@link import('../workspaces/WorkspaceManager.js').WorkspaceManager}); the observability lives
1541
+ * {@link import('@orkestrel/workspace').WorkspaceManager}); the observability lives
1608
1542
  * on each {@link Conversation}.
1609
1543
  *
1610
1544
  * @remarks
@@ -1654,7 +1588,7 @@ export declare class ConversationManager implements ConversationManagerInterface
1654
1588
  * A registry of {@link ConversationInterface}s keyed by their `id`, in insertion order, WITH an
1655
1589
  * active pointer — the §9 store over the conversation layer PLUS the `active` / `switch` seam the
1656
1590
  * {@link AgentContextInterface} renders. Event-free (a registry, like
1657
- * {@link WorkspaceManagerInterface}); the observability lives on each
1591
+ * {@link import('@orkestrel/workspace').WorkspaceManagerInterface}); the observability lives on each
1658
1592
  * {@link ConversationInterface}.
1659
1593
  *
1660
1594
  * @remarks
@@ -1677,8 +1611,8 @@ export declare class ConversationManager implements ConversationManagerInterface
1677
1611
  * miss (rebuilding it through the constructor `seed` from the snapshot, flowing the manager's
1678
1612
  * default `summarize` / `keep` in) and `save(id)` PERSISTS a registered conversation's
1679
1613
  * {@link ConversationInterface.snapshot}. Both are LENIENT without a store — `open` resolves only
1680
- * registered ids, `save` is a no-op (`false`) — consistent with the lenient `switch`. The EXACT
1681
- * analogue of {@link WorkspaceManagerInterface.open} / {@link WorkspaceManagerInterface.save}.
1614
+ * registered ids, `save` is a no-op (`false`) — consistent with the lenient `switch`. It mirrors
1615
+ * the workspace package manager's `open` / `save` seam.
1682
1616
  * - **Event-free.** A purely registry store — no Emitter, no events (each conversation owns
1683
1617
  * its own).
1684
1618
  */
@@ -1749,8 +1683,8 @@ export declare interface ConversationManagerOptions {
1749
1683
  * {@link ConversationManagerInterface.open} / {@link ConversationManagerInterface.save} — a memory
1750
1684
  * / JSON / SQLite / IndexedDB store a conversation is HYDRATED from (`open` a registry-miss) and
1751
1685
  * PERSISTED to (`save`). Omitted ⇒ the manager is registry-only: `open` resolves only what is
1752
- * already registered, and `save` is a no-op (`false`). The EXACT analogue of
1753
- * {@link WorkspaceManagerOptions.store}.
1686
+ * already registered, and `save` is a no-op (`false`). The exact analogue of
1687
+ * {@link import('@orkestrel/workspace').WorkspaceManagerOptions}'s `store`.
1754
1688
  */
1755
1689
  readonly store?: ConversationStoreInterface;
1756
1690
  }
@@ -1816,7 +1750,8 @@ export declare interface ConversationReferenceOptions {
1816
1750
  /**
1817
1751
  * A JSON-serializable snapshot of a conversation's state — its `id`, the rollup `summary`, the
1818
1752
  * compacted `sections`, and the live tail `messages` — the durable payload the
1819
- * {@link ConversationStoreInterface} persists. The EXACT analogue of {@link WorkspaceSnapshot}.
1753
+ * {@link ConversationStoreInterface} persists. The exact analogue of
1754
+ * {@link import('@orkestrel/workspace').WorkspaceSnapshot}.
1820
1755
  *
1821
1756
  * @remarks
1822
1757
  * Pure JSON DATA (no class instances, no functions): each {@link SectionInterface} and
@@ -1845,12 +1780,13 @@ export declare interface ConversationSnapshot {
1845
1780
  * One row of the table a
1846
1781
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
1847
1782
  * persists — a conversation `id` plus its {@link ConversationSnapshot} held as ONE OPAQUE JSON
1848
- * column. The EXACT analogue of {@link WorkspaceSnapshotRow}.
1783
+ * column. The exact analogue of {@link import('@orkestrel/workspace').WorkspaceSnapshotRow}.
1849
1784
  *
1850
1785
  * @remarks
1851
1786
  * The Database twin of {@link ConversationStoreInterface} stores the snapshot whole (the `snapshot`
1852
- * column is a `rawShape`, an opaque JSON blob — exactly as {@link WorkspaceSnapshotRow} stores a
1853
- * workspace snapshot), so the row type stays FLAT and the sections/messages snapshot shape never
1787
+ * column is a `rawShape`, an opaque JSON blob — exactly as
1788
+ * {@link import('@orkestrel/workspace').WorkspaceSnapshotRow} stores a workspace snapshot), so the
1789
+ * row type stays FLAT and the sections/messages snapshot shape never
1854
1790
  * forces the contract to `Infer` it. The column therefore reads back as the broad `unknown`; the
1855
1791
  * store narrows it to a {@link ConversationSnapshot} on `get`
1856
1792
  * ({@link import('./helpers.js').isConversationSnapshot}, the AGENTS §14 boundary narrow). `id`
@@ -1865,8 +1801,8 @@ export declare interface ConversationSnapshotRow {
1865
1801
 
1866
1802
  /**
1867
1803
  * The durable persistence seam for a {@link ConversationSnapshot} — three async primitives
1868
- * (`get` / `set` / `delete`) keyed by a conversation id, the EXACT analogue of
1869
- * {@link WorkspaceStoreInterface}.
1804
+ * (`get` / `set` / `delete`) keyed by a conversation id, the exact analogue of
1805
+ * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}.
1870
1806
  *
1871
1807
  * @remarks
1872
1808
  * The store persists the {@link ConversationSnapshot} — the self-contained, pure-JSON conversation
@@ -1882,7 +1818,9 @@ export declare interface ConversationSnapshotRow {
1882
1818
  *
1883
1819
  * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
1884
1820
  * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
1885
- * param (mirroring {@link WorkspaceStoreInterface.set}). UNLIKE a session store there is NO idle-TTL
1821
+ * param (mirroring
1822
+ * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`). UNLIKE a session store
1823
+ * there is NO idle-TTL
1886
1824
  * / eviction — a persisted conversation lives until an explicit `delete`. It is concrete over
1887
1825
  * {@link ConversationSnapshot} — no generic parameter (AGENTS §21 minimal-interface), since the
1888
1826
  * snapshot is the ONE payload a conversation store persists.
@@ -1897,7 +1835,7 @@ export declare interface ConversationStoreInterface {
1897
1835
  get(id: string): Promise<ConversationSnapshot | undefined>;
1898
1836
  /**
1899
1837
  * Insert or replace a snapshot under its own `snapshot.id` (no separate id param —
1900
- * mirroring {@link WorkspaceStoreInterface.set}).
1838
+ * mirroring {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`).
1901
1839
  *
1902
1840
  * @param snapshot - The snapshot to store (keyed by its `id`)
1903
1841
  */
@@ -1928,28 +1866,6 @@ export declare interface ConversationStoreInterface {
1928
1866
  */
1929
1867
  export declare type ConversationSummarizer = (messages: readonly MessageInterface[]) => Promise<string>;
1930
1868
 
1931
- /**
1932
- * Count the lines of a {@link FileContent} — the `lines` a {@link FileInterface} derives once
1933
- * when built.
1934
- *
1935
- * @remarks
1936
- * A text arm's line count is `0` for the empty string, otherwise one more than the
1937
- * number of newline (`\n`) separators it contains — so `'a'` is one line, `'a\nb'` is two,
1938
- * and a trailing newline `'a\n'` counts the empty final line as two. A binary arm has no
1939
- * lines and returns `0`. Total — never throws.
1940
- *
1941
- * @param content - The file content to count lines for
1942
- * @returns The line count (text line count; `0` for a binary arm or empty text)
1943
- *
1944
- * @example
1945
- * ```ts
1946
- * countLines({ text: '', language: 'text' }) // 0
1947
- * countLines({ text: 'a\nb\nc', language: 'text' }) // 3
1948
- * countLines({ data: 'AAAA', mime: 'image/png' }) // 0
1949
- * ```
1950
- */
1951
- export declare function countLines(content: FileContent): number;
1952
-
1953
1869
  /**
1954
1870
  * Create an agent loop — an {@link AgentInterface} composing a
1955
1871
  * {@link ProviderInterface}, its {@link AgentContextInterface}, and a tool registry
@@ -2001,8 +1917,8 @@ export declare function createAgent(provider: ProviderInterface, options?: Agent
2001
1917
  * @remarks
2002
1918
  * `system` is the optional system prompt; `tools` / `instructions` / `workspaces` are pre-built
2003
1919
  * managers to reuse (empty ones are created when omitted, so `context.workspaces` is ALWAYS
2004
- * present); `scope` is the initial active filter (`undefined` ⇒ no filtering, mutable afterwards
2005
- * via `context.scope`). The `messages` store is always fresh. `build()` folds the scoped
1920
+ * present); `scope` is the initial active filter (`undefined` ⇒ no filtering, changeable afterwards
1921
+ * via `context.apply(...)`). The `messages` store is always fresh. `build()` folds the scoped
2006
1922
  * instructions — PLUS the ACTIVE workspace's scope-filtered text files (fenced) — into ONE leading
2007
1923
  * `system` message and appends the scoped conversation (attaching the active workspace's
2008
1924
  * scope-filtered image files' `data` to the last user message), built fresh each call; the active
@@ -2084,8 +2000,9 @@ export declare function createAgentQueue(options: AgentQueueOptions): QueueInter
2084
2000
  *
2085
2001
  * @example
2086
2002
  * ```ts
2003
+ * import { createAgentRegistry } from '@src/core'
2087
2004
  * import type { ProviderInterface } from '@src/core'
2088
- * import { createAgentRegistry, createTool } from '@src/core'
2005
+ * import { createTool } from '@orkestrel/tool'
2089
2006
  *
2090
2007
  * declare const provider: ProviderInterface // any concrete implementation supplied by the host app
2091
2008
  * const registry = createAgentRegistry({
@@ -2166,24 +2083,6 @@ export declare function createAgentRunner(options: AgentRunnerOptions): RunnerIn
2166
2083
  */
2167
2084
  export declare function createAuthority(options?: AuthorityOptions): AuthorityInterface;
2168
2085
 
2169
- /**
2170
- * Build the BINARY {@link FileContent} arm — the §4.2.3 split constructor for binary (a
2171
- * separate function per arm, not one constructor dispatching on a discriminator parameter).
2172
- * An image is just a binary with an image {@link BinaryMIME}.
2173
- *
2174
- * @param data - The base64-encoded binary payload
2175
- * @param mime - The {@link BinaryMIME} that labels the payload
2176
- * @returns A `{ data; mime }` content arm
2177
- *
2178
- * @example
2179
- * ```ts
2180
- * import { createBinaryContent } from '@src/core'
2181
- *
2182
- * createBinaryContent('<base64>', 'image/png') // { data: '<base64>', mime: 'image/png' }
2183
- * ```
2184
- */
2185
- export declare function createBinaryContent(data: string, mime: BinaryMIME): FileContent;
2186
-
2187
2086
  /**
2188
2087
  * Create a conversation — a {@link ConversationInterface} grouping messages above a flat
2189
2088
  * message store it OWNS DIRECTLY, with compaction into summarized sections, a regenerated
@@ -2255,12 +2154,14 @@ export declare function createConversationManager(options?: ConversationManagerO
2255
2154
  /**
2256
2155
  * Create a {@link DatabaseConversationStore} over any {@link DriverInterface} — the durable,
2257
2156
  * driver-pluggable backing for the conversation persistence seam, the opt-in twin of
2258
- * {@link createMemoryConversationStore}. The EXACT twin of {@link createDatabaseWorkspaceStore}.
2157
+ * {@link createMemoryConversationStore}. The exact twin of
2158
+ * {@link import('@orkestrel/workspace').createDatabaseWorkspaceStore}.
2259
2159
  *
2260
2160
  * @remarks
2261
2161
  * Builds a one-table database (`conversations`, keyed by `id`) over the supplied driver, the snapshot
2262
2162
  * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
2263
- * `rawShape` (a JSON blob), exactly as {@link createDatabaseWorkspaceStore} stores its snapshot. The
2163
+ * `rawShape` (a JSON blob), exactly as
2164
+ * {@link import('@orkestrel/workspace').createDatabaseWorkspaceStore} stores its snapshot. The
2264
2165
  * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
2265
2166
  * AND keeps the row type FLAT (the column reads back as `unknown`, narrowed on `get` by
2266
2167
  * {@link import('./helpers.js').isConversationSnapshot}). The `driver` DEFAULTS to
@@ -2285,65 +2186,6 @@ export declare function createConversationManager(options?: ConversationManagerO
2285
2186
  */
2286
2187
  export declare function createDatabaseConversationStore(driver?: DriverInterface): ConversationStoreInterface;
2287
2188
 
2288
- /**
2289
- * Create a {@link DatabaseWorkspaceStore} over any {@link DriverInterface} — the durable,
2290
- * driver-pluggable backing for the workspace persistence seam, the opt-in twin of
2291
- * {@link createMemoryWorkspaceStore}.
2292
- *
2293
- * @remarks
2294
- * Builds a one-table database (`workspaces`, keyed by `id`) over the supplied driver, the snapshot
2295
- * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
2296
- * `rawShape` (a JSON blob), exactly as
2297
- * the analogous `createDatabaseWorkflowStore` in `@orkestrel/workflow` stores its snapshot. The
2298
- * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
2299
- * AND keeps the row type FLAT (the column reads back as `unknown`, narrowed on `get` by
2300
- * {@link import('./helpers.js').isWorkspaceSnapshot}). The `driver` DEFAULTS to
2301
- * {@link createMemoryDriver}, so the store ALSO works in memory out of the box; pass a server
2302
- * `createJSONDriver` / `createSQLiteDriver` (or a browser IndexedDB driver) for a persistent one —
2303
- * the durability is the driver's job, the store engine is shared. It swaps in behind
2304
- * {@link WorkspaceStoreInterface} WITHOUT touching the manager or the workspace.
2305
- *
2306
- * @param driver - The storage backend the snapshots persist to (defaults to {@link createMemoryDriver})
2307
- * @returns A {@link WorkspaceStoreInterface} over the driver
2308
- *
2309
- * @example
2310
- * ```ts
2311
- * import { createDatabaseWorkspaceStore, createMemoryDriver, createWorkspaceManager } from '@src/core'
2312
- *
2313
- * const store = createDatabaseWorkspaceStore(createMemoryDriver()) // a durable driver swaps in here
2314
- * const manager = createWorkspaceManager({ store })
2315
- * const workspace = manager.add()
2316
- * workspace.write('notes.txt', 'hello')
2317
- * await manager.save(workspace.id) // persist the workspace (one JSON column)
2318
- * ```
2319
- */
2320
- export declare function createDatabaseWorkspaceStore(driver?: DriverInterface): WorkspaceStoreInterface;
2321
-
2322
- /**
2323
- * Create a file — an immutable {@link FileInterface} from its `path` + {@link FileContent}
2324
- * and optional {@link import('./types.js').FileState}, with `size` / `lines` DERIVED from the
2325
- * content.
2326
- *
2327
- * @remarks
2328
- * Returns a PLAIN `Object.freeze`d record (NOT a class instance) — the `path` IS its identity
2329
- * (there is no `id`). Only `path` / `content` are required; `state` defaults to `'created'`
2330
- * when omitted. `size` (via {@link computeSize}) and `lines` (via {@link countLines}) are
2331
- * computed from the content here (so they never drift from it). Frozen + plain, so it
2332
- * `structuredClone`s losslessly and is never mutated after creation.
2333
- *
2334
- * @param input - `path` / `content` (required) and an optional `state` (see {@link FileInput})
2335
- * @returns A frozen {@link FileInterface} record
2336
- *
2337
- * @example
2338
- * ```ts
2339
- * import { createFile, createTextContent } from '@src/core'
2340
- *
2341
- * const file = createFile({ path: 'src/main.ts', content: createTextContent('const x = 1', 'typescript') })
2342
- * file.lines // 1
2343
- * ```
2344
- */
2345
- export declare function createFile(input: FileInput): FileInterface;
2346
-
2347
2189
  /**
2348
2190
  * Create an instruction — an immutable {@link InstructionInterface} (a named directive)
2349
2191
  * from its `name` / `content` and optional `priority`, the `id` minted at construction.
@@ -2400,11 +2242,13 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2400
2242
  * Create the in-memory conversation store — a {@link ConversationStoreInterface} backed by a
2401
2243
  * process-lifetime `Map` of {@link import('./types.js').ConversationSnapshot}s keyed by conversation
2402
2244
  * id, the DEFAULT backing for the durable {@link ConversationManagerInterface.open} /
2403
- * {@link ConversationManagerInterface.save} seam. The EXACT twin of {@link createMemoryWorkspaceStore}.
2245
+ * {@link ConversationManagerInterface.save} seam. The exact twin of
2246
+ * {@link import('@orkestrel/workspace').createMemoryWorkspaceStore}.
2404
2247
  *
2405
2248
  * @remarks
2406
2249
  * A plain `Map` (the snapshot is already pure JSON, so no encoding is needed for the memory tier),
2407
- * the structural twin of {@link createMemoryWorkspaceStore}. `get` / `set` / `delete` are async (the
2250
+ * the structural twin of {@link import('@orkestrel/workspace').createMemoryWorkspaceStore}.
2251
+ * `get` / `set` / `delete` are async (the
2408
2252
  * same shape a durable backend fits); UNLIKE a session store there is NO idle-TTL / eviction — a
2409
2253
  * persisted conversation lives until an explicit `delete`. Its driver-pluggable twin is
2410
2254
  * {@link createDatabaseConversationStore} (the snapshot as one opaque JSON column over a `databases`
@@ -2428,37 +2272,6 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2428
2272
  */
2429
2273
  export declare function createMemoryConversationStore(): ConversationStoreInterface;
2430
2274
 
2431
- /**
2432
- * Create the in-memory workspace store — a {@link WorkspaceStoreInterface} backed by a
2433
- * process-lifetime `Map` of {@link import('./types.js').WorkspaceSnapshot}s keyed by workspace id,
2434
- * the DEFAULT backing for the durable {@link WorkspaceManagerInterface.open} /
2435
- * {@link WorkspaceManagerInterface.save} seam.
2436
- *
2437
- * @remarks
2438
- * A plain `Map` (the snapshot is already pure JSON, so no encoding is needed for the memory tier),
2439
- * the structural twin of the analogous `createMemoryWorkflowStore` in `@orkestrel/workflow`.
2440
- * `get` / `set` / `delete` are async (the same shape a durable backend fits); UNLIKE a session
2441
- * store there is NO idle-TTL / eviction — a persisted workspace lives until an explicit `delete`.
2442
- * Its driver-pluggable twin is {@link createDatabaseWorkspaceStore} (the snapshot as one opaque
2443
- * JSON column over a `databases` table) — for a DURABLE store pass it a JSON / SQLite / IndexedDB
2444
- * driver, and it swaps in WITHOUT touching the manager or the workspace. Hydration stays a manager
2445
- * concern: read a snapshot back and rebuild the live workspace through the constructor `seed`.
2446
- *
2447
- * @returns A memory-backed {@link WorkspaceStoreInterface}
2448
- *
2449
- * @example
2450
- * ```ts
2451
- * import { createMemoryWorkspaceStore, createWorkspaceManager } from '@src/core'
2452
- *
2453
- * const store = createMemoryWorkspaceStore()
2454
- * const manager = createWorkspaceManager({ store })
2455
- * const workspace = manager.add()
2456
- * workspace.write('notes.txt', 'hello')
2457
- * await manager.save(workspace.id) // persist the workspace
2458
- * ```
2459
- */
2460
- export declare function createMemoryWorkspaceStore(): WorkspaceStoreInterface;
2461
-
2462
2275
  /**
2463
2276
  * Create a named scope — an immutable {@link ScopeInterface} from its `name` and the four
2464
2277
  * optional per-category allow-lists, the `id` minted at construction.
@@ -2508,23 +2321,6 @@ export declare function createScope(input: ScopeInput): ScopeInterface;
2508
2321
  */
2509
2322
  export declare function createScopeManager(options?: ScopeManagerOptions): ScopeManagerInterface;
2510
2323
 
2511
- /**
2512
- * Build the TEXT {@link FileContent} arm — the §4.2.3 split constructor for text (a separate
2513
- * function per arm, not one constructor dispatching on a discriminator parameter).
2514
- *
2515
- * @param text - The literal text body
2516
- * @param language - The fenced-code language the text renders as (e.g. `'typescript'`)
2517
- * @returns A `{ text; language }` content arm
2518
- *
2519
- * @example
2520
- * ```ts
2521
- * import { createTextContent } from '@src/core'
2522
- *
2523
- * createTextContent('const x = 1', 'typescript') // { text: 'const x = 1', language: 'typescript' }
2524
- * ```
2525
- */
2526
- export declare function createTextContent(text: string, language: string): FileContent;
2527
-
2528
2324
  /**
2529
2325
  * Create a stream-stateful `<think>` separator — a {@link ThinkSplitterInterface} that
2530
2326
  * splits a thinking model's in-content `<think>…</think>` reasoning spans away from
@@ -2554,122 +2350,12 @@ export declare function createTextContent(text: string, language: string): FileC
2554
2350
  */
2555
2351
  export declare function createThinkSplitter(): ThinkSplitterInterface;
2556
2352
 
2557
- /**
2558
- * Create a tool — a {@link ToolInterface} binding a {@link ToolDefinition} schema
2559
- * (the `name` / `description` / `parameters` the model sees) to the `execute` handler
2560
- * that runs a call.
2561
- *
2562
- * @remarks
2563
- * Only `name` is required (it keys the tool in a {@link ToolManagerInterface} and is
2564
- * what the model calls); `description` / `parameters` are the optional JSON-Schema the
2565
- * provider advertises (forwarded verbatim). The handler's `args` is the model-supplied
2566
- * `unknown` arguments record — narrow it inside (§14); a `createToolManager` registry
2567
- * isolates a throw into a `ToolResult.error`.
2568
- *
2569
- * @param options - `name` (required), optional `description` / `parameters`, and the
2570
- * `execute` handler (see {@link ToolOptions})
2571
- * @returns A working {@link ToolInterface}
2572
- *
2573
- * @example
2574
- * ```ts
2575
- * import { createTool } from '@src/core'
2576
- *
2577
- * const add = createTool({
2578
- * name: 'add',
2579
- * description: 'Add two numbers',
2580
- * execute: (args) => Number(args.a) + Number(args.b),
2581
- * })
2582
- * ```
2583
- */
2584
- export declare function createTool(options: ToolOptions): ToolInterface;
2585
-
2586
- /**
2587
- * Create a tool registry — a {@link ToolManagerInterface} that resolves tool names,
2588
- * lists {@link ToolDefinition}s for the provider, and executes calls with per-call
2589
- * error isolation.
2590
- *
2591
- * @remarks
2592
- * Starts empty; `add` registers one tool or a batch (§9.2), `definitions()` yields the
2593
- * schemas to hand a provider, and `execute` runs a {@link ToolCall} (or a batch),
2594
- * ALWAYS resolving a {@link ToolResult} — a handler throw becomes an `error` result and
2595
- * an unknown name a not-found `error`, so a tool throw never escapes and a batch never
2596
- * fails as a whole.
2597
- *
2598
- * @returns An empty {@link ToolManagerInterface}
2599
- *
2600
- * @example
2601
- * ```ts
2602
- * import { createTool, createToolManager } from '@src/core'
2603
- *
2604
- * const tools = createToolManager()
2605
- * tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
2606
- * const result = await tools.execute({ id: '1', name: 'add', arguments: { x: 1, y: 2 } })
2607
- * ```
2608
- */
2609
- export declare function createToolManager(): ToolManagerInterface;
2610
-
2611
- /**
2612
- * Create a workspace — a mutable, `path`-keyed working set of immutable
2613
- * {@link FileInterface}s with the in-memory edit surface (read / write / search / replace /
2614
- * move / remove), observable through its `EmitterInterface` (from `@orkestrel/emitter`).
2615
- *
2616
- * @param options - Optional initial {@link import('./types.js').WorkspaceEventMap} listeners (`on`) and the emitter's `error` handler (see {@link WorkspaceOptions})
2617
- * @returns A working {@link WorkspaceInterface}
2618
- *
2619
- * @example
2620
- * ```ts
2621
- * import { createWorkspace } from '@src/core'
2622
- *
2623
- * const workspace = createWorkspace({ on: { write: (file) => console.log(file.path) } })
2624
- * workspace.write('src/main.ts', 'const x = 1')
2625
- * workspace.file('src/main.ts')?.state // 'created'
2626
- * ```
2627
- */
2628
- export declare function createWorkspace(options?: WorkspaceOptions): WorkspaceInterface;
2629
-
2630
- /**
2631
- * Create a workspace registry — a {@link WorkspaceManagerInterface} holding
2632
- * {@link WorkspaceInterface}s keyed by their `id` (in insertion order) WITH an active pointer:
2633
- * the §9 store over the workspace layer plus the `active` / `switch` seam the context renders.
2634
- *
2635
- * @remarks
2636
- * Starts empty; `add(input?)` mints a {@link WorkspaceInterface} (its `id` from the input or a
2637
- * random UUID), flowing the manager's default `on` / `error` in unless the input overrides them,
2638
- * and stores it (an already-present `id` overwrites — last write wins) — and AUTO-ACTIVATES the
2639
- * FIRST one (a registry with workspaces always has one `active`); a later `add` leaves `active`
2640
- * unchanged. `switch(id)` re-points `active` (an unknown `id` returns `undefined`, leaving
2641
- * `active` unchanged — lenient, never throws); `workspace(id)` / `workspaces()` look up;
2642
- * `remove` (one or a batch, §9.2) reports whether any was removed AND clears `active` if it was
2643
- * the removed one; `clear` empties it and clears `active`. Event-free (each workspace owns its
2644
- * own observable `emitter`).
2645
- *
2646
- * With the optional `store` ({@link WorkspaceStoreInterface}, e.g. `createMemoryWorkspaceStore` /
2647
- * `createDatabaseWorkspaceStore`), `open(id)` HYDRATES a workspace on a registry miss (rebuilding it
2648
- * through the constructor `seed` from the snapshot's `files`, then activating it) and `save(id)`
2649
- * PERSISTS a registered workspace's `snapshot()`. Both are LENIENT without a store (open resolves
2650
- * only registered ids, save is a no-op `false`).
2651
- *
2652
- * @param options - Optional default `on` / `error` for created workspaces + the durable `store`
2653
- * (see {@link WorkspaceManagerOptions})
2654
- * @returns An empty {@link WorkspaceManagerInterface}
2655
- *
2656
- * @example
2657
- * ```ts
2658
- * import { createWorkspaceManager } from '@src/core'
2659
- *
2660
- * const workspaces = createWorkspaceManager()
2661
- * const scratch = workspaces.add() // auto-activates — workspaces.active === scratch
2662
- * scratch.write('notes.txt', 'hello')
2663
- * ```
2664
- */
2665
- export declare function createWorkspaceManager(options?: WorkspaceManagerOptions): WorkspaceManagerInterface;
2666
-
2667
2353
  /**
2668
2354
  * A {@link ConversationStoreInterface} backed by one table of the `databases` layer — a
2669
2355
  * conversation's durable state IS a row, so persistence reduces to keyed point-access (`get` / `set`
2670
2356
  * / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
2671
2357
  * {@link import('./MemoryConversationStore.js').MemoryConversationStore}. The EXACT twin of
2672
- * {@link import('../../workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore}.
2358
+ * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}.
2673
2359
  *
2674
2360
  * @remarks
2675
2361
  * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
@@ -2677,7 +2363,7 @@ export declare function createWorkspaceManager(options?: WorkspaceManagerOptions
2677
2363
  * IndexedDB backend swaps in WITHOUT touching the
2678
2364
  * {@link import('../ConversationManager.js').ConversationManager} or the
2679
2365
  * {@link import('../Conversation.js').Conversation} — the same seam as
2680
- * {@link import('../../workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore}. The
2366
+ * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}. The
2681
2367
  * driver defaults to memory ({@link import('../../factories.js').createDatabaseConversationStore}
2682
2368
  * passes `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the
2683
2369
  * durable plumbing by passing a JSON / SQLite / IndexedDB driver.
@@ -2730,96 +2416,6 @@ export declare class DatabaseConversationStore implements ConversationStoreInter
2730
2416
  delete(id: string): Promise<void>;
2731
2417
  }
2732
2418
 
2733
- /**
2734
- * A {@link WorkspaceStoreInterface} backed by one table of the `databases` layer — a workspace's
2735
- * durable state IS a row, so persistence reduces to keyed point-access (`get` / `set` / `delete`)
2736
- * over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
2737
- * {@link import('./MemoryWorkspaceStore.js').MemoryWorkspaceStore}.
2738
- *
2739
- * @remarks
2740
- * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
2741
- * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
2742
- * IndexedDB backend swaps in WITHOUT touching the
2743
- * {@link import('../WorkspaceManager.js').WorkspaceManager} or the
2744
- * {@link import('../Workspace.js').Workspace} — the same seam as
2745
- * the analogous `DatabaseWorkflowStore` in `@orkestrel/workflow`. The
2746
- * driver defaults to memory ({@link import('../../factories.js').createDatabaseWorkspaceStore}
2747
- * passes `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the
2748
- * durable plumbing by passing a JSON / SQLite / IndexedDB driver.
2749
- *
2750
- * The {@link WorkspaceSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of
2751
- * `{ id; snapshot }` ({@link WorkspaceSnapshotRow}), the snapshot the whole JSON blob (a `rawShape`
2752
- * column the factory builds) — exactly as `DatabaseWorkflowStore` stores its snapshot. The snapshot
2753
- * is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless AND
2754
- * keeps the row type flat (`snapshot` reads back as `unknown`).
2755
- *
2756
- * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes
2757
- * the row `{ id: snapshot.id, snapshot }`.
2758
- * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
2759
- * a {@link WorkspaceSnapshot} ({@link import('../../helpers.js').isWorkspaceSnapshot} — the AGENTS
2760
- * §14 boundary narrow for an untrusted storage read), or `undefined` if none is stored.
2761
- * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
2762
- *
2763
- * UNLIKE a session store there is NO idle-TTL / eviction — a persisted workspace lives until an
2764
- * explicit `delete`. The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the
2765
- * §22 method bijection with {@link WorkspaceStoreInterface}). Hydration stays a caller concern: a
2766
- * {@link import('../WorkspaceManager.js').WorkspaceManager} reads a snapshot back and rebuilds the
2767
- * live workspace through the constructor `seed` (its `open` / `save`).
2768
- *
2769
- * @example
2770
- * ```ts
2771
- * import { createDatabaseWorkspaceStore, createMemoryDriver, createWorkspace } from '@src/core'
2772
- *
2773
- * const store = createDatabaseWorkspaceStore(createMemoryDriver()) // a durable driver swaps in here
2774
- * const workspace = createWorkspace()
2775
- * workspace.write('notes.txt', 'hello')
2776
- * await store.set(workspace.snapshot()) // persist the workspace (one JSON column)
2777
- * const snapshot = await store.get(workspace.id)
2778
- * await store.delete(workspace.id) // drop it
2779
- * ```
2780
- */
2781
- export declare class DatabaseWorkspaceStore implements WorkspaceStoreInterface {
2782
- #private;
2783
- /**
2784
- * Wrap a table as a workspace store.
2785
- *
2786
- * @param table - The {@link TableInterface} holding the snapshots — its row is the
2787
- * {@link WorkspaceSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)
2788
- */
2789
- constructor(table: TableInterface<WorkspaceSnapshotRow>);
2790
- /** Resolve the persisted snapshot for `id`, narrowing the opaque JSON column back to a `WorkspaceSnapshot`. */
2791
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
2792
- /** Insert or replace under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
2793
- set(snapshot: WorkspaceSnapshot): Promise<void>;
2794
- /** Drop a snapshot by id; an absent id is a no-op (no throw). */
2795
- delete(id: string): Promise<void>;
2796
- }
2797
-
2798
- /**
2799
- * Compute the decoded byte length of a base64 string ARITHMETICALLY — the image-payload
2800
- * sizing primitive {@link computeSize} uses, with no `atob` / `Buffer` decode.
2801
- *
2802
- * @remarks
2803
- * A base64 string encodes each group of 3 input bytes as 4 characters, so a well-formed
2804
- * string's length is a multiple of 4 and its decoded length is `(length / 4) * 3` MINUS
2805
- * the trailing `=` padding (one `=` ⇒ the last group held 2 bytes, two `==` ⇒ 1 byte).
2806
- * The padding is counted from the trailing `=` characters, so both the `=` and `==` cases
2807
- * are handled. The empty string decodes to `0`. Total — never throws (a malformed,
2808
- * non-multiple-of-4 length still yields a defined non-negative estimate via `floor`).
2809
- *
2810
- * @param base64 - The base64-encoded string (e.g. an image's `data`)
2811
- * @returns The decoded payload's byte length
2812
- *
2813
- * @example
2814
- * ```ts
2815
- * decodedSize('') // 0
2816
- * decodedSize('AAAA') // 3 (no padding)
2817
- * decodedSize('AAA=') // 2 (one '=')
2818
- * decodedSize('AA==') // 1 (two '=')
2819
- * ```
2820
- */
2821
- export declare function decodedSize(base64: string): number;
2822
-
2823
2419
  /**
2824
2420
  * The default cap on an {@link AgentInterface} turn's tool iterations — the maximum
2825
2421
  * number of context → provider → tools cycles before the loop stops, so a model that
@@ -2847,28 +2443,6 @@ export declare const DEFAULT_AUTHORITY_ZONE = "default";
2847
2443
  */
2848
2444
  export declare const DEFAULT_CONVERSATION_KEEP = 0;
2849
2445
 
2850
- /**
2851
- * Escape a string's regex-special characters so it matches LITERALLY inside a `RegExp` — the
2852
- * primitive a {@link import('./workspaces/Workspace.js').Workspace} search builds a literal-text
2853
- * search pattern through (as opposed to a caller-supplied regex pattern).
2854
- *
2855
- * @remarks
2856
- * Prefixes every character in the class `. * + ? ^ $ { } ( ) | [ ] \` with a backslash, so the
2857
- * escaped string, when compiled into a `RegExp`, matches only its own literal characters — no
2858
- * character acts as a quantifier, anchor, group, or class. Pure string assembly, total — never
2859
- * throws.
2860
- *
2861
- * @param value - The text to escape for literal use inside a `RegExp` pattern
2862
- * @returns `value` with every regex-special character backslash-escaped
2863
- *
2864
- * @example
2865
- * ```ts
2866
- * escapeRegExp('a.b*c') // 'a\\.b\\*c'
2867
- * new RegExp(escapeRegExp('a.b*c')).test('a.b*c') // true
2868
- * ```
2869
- */
2870
- export declare function escapeRegExp(value: string): string;
2871
-
2872
2446
  /**
2873
2447
  * Estimate the context-token footprint of a batch of messages — the default `consume`
2874
2448
  * estimator for an agent's context `BudgetInterface` (a budgets surface's tracking contract)
@@ -2923,13 +2497,6 @@ export declare function estimateMessages(messages: readonly MessageInterface[]):
2923
2497
  */
2924
2498
  export declare function estimateTokens(text: string): number;
2925
2499
 
2926
- /**
2927
- * The extension→language table {@link import('./helpers.js').inferLanguage} reads to
2928
- * map a file path's extension to a fenced-code language tag. Covers the common source /
2929
- * markup / data extensions; an unlisted extension falls back to `'text'`.
2930
- */
2931
- export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2932
-
2933
2500
  /**
2934
2501
  * Render a path-addressed text body as a fenced reference block — the framing an
2935
2502
  * {@link import('./AgentContext.js').AgentContext}'s ACTIVE-workspace text-file render emits (the
@@ -2940,7 +2507,7 @@ export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2940
2507
  * fenced code block tagged with `language`, the `content` verbatim inside. Pure string assembly,
2941
2508
  * total — never throws. The one fenced-file format string for the whole module — `AgentContext.build()`
2942
2509
  * frames an active workspace's text files with it (each carries its own `language` on its
2943
- * {@link FileContent} text arm).
2510
+ * {@link import('@orkestrel/workspace').FileContent} text arm).
2944
2511
  *
2945
2512
  * @param path - The file path shown on the `File:` label line
2946
2513
  * @param language - The fenced-code language tag (e.g. `'typescript'`)
@@ -2957,74 +2524,6 @@ export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2957
2524
  */
2958
2525
  export declare function fencedFile(path: string, language: string, content: string): string;
2959
2526
 
2960
- /**
2961
- * A {@link FileInterface}'s content — a TAGLESS union of a TEXT arm and a BINARY arm, narrowed
2962
- * by the {@link import('./helpers.js').isText} / {@link import('./helpers.js').isBinary}
2963
- * guards (AGENTS §14: an untyped arm is narrowed via a guard, never an `as`).
2964
- *
2965
- * @remarks
2966
- * - The TEXT arm carries the literal `text` plus the fenced-code `language` it renders as.
2967
- * - The BINARY arm carries the base64-encoded `data` plus the {@link BinaryMIME} that labels it.
2968
- *
2969
- * There is no `modality` discriminant — the arms are told apart structurally
2970
- * (`'text' in content` vs `'data' in content`) by the guards. A binary arm whose `mime`
2971
- * starts with `image/` IS an image ({@link import('./helpers.js').isImage}). A future
2972
- * non-image binary (a PDF) is the same binary arm with a new {@link BinaryMIME} — additive,
2973
- * with nothing above it changed.
2974
- */
2975
- export declare type FileContent = {
2976
- readonly text: string;
2977
- readonly language: string;
2978
- } | {
2979
- readonly data: string;
2980
- readonly mime: BinaryMIME;
2981
- };
2982
-
2983
- /**
2984
- * The minimal data to author a {@link FileInterface} — `size` / `lines` are DERIVED from the
2985
- * `content`, so a caller supplies only `path` / `content` (and an optional `state`, defaulting
2986
- * to `'created'`).
2987
- */
2988
- export declare interface FileInput {
2989
- readonly path: string;
2990
- readonly content: FileContent;
2991
- /** The lifecycle state; defaults to `'created'` when omitted. */
2992
- readonly state?: FileState;
2993
- }
2994
-
2995
- /**
2996
- * An immutable file value object — a `path`-addressed {@link FileContent} carrying a
2997
- * lifecycle {@link FileState}, with `size` / `lines` DERIVED once when built.
2998
- *
2999
- * @remarks
3000
- * A PLAIN frozen record (not a class instance) produced by
3001
- * {@link import('./factories.js').createFile} — the `path` IS its identity (there is no
3002
- * `id`). `size` and `lines` are computed from `content` when built (so they can never drift
3003
- * from it) and re-read for free thereafter. Never mutated — a Workspace REPLACES a file
3004
- * rather than editing it in place. A DATA-ONLY value object — no methods, plus it
3005
- * `structuredClone`s losslessly (every field is a plain primitive / record).
3006
- */
3007
- export declare interface FileInterface {
3008
- readonly path: string;
3009
- readonly content: FileContent;
3010
- readonly state: FileState;
3011
- /** Derived byte size: UTF-8 byte length (text) / decoded payload bytes (binary). */
3012
- readonly size: number;
3013
- /** Derived line count: the text line count; `0` for a binary arm. */
3014
- readonly lines: number;
3015
- }
3016
-
3017
- /**
3018
- * The lifecycle state of a {@link FileInterface} relative to its (future) durable backing
3019
- * — a multi-value lifecycle union (AGENTS §10), not a boolean.
3020
- *
3021
- * @remarks
3022
- * `'created'` is a brand-new file not yet persisted; `'modified'` has unsaved edits over a
3023
- * persisted original; `'loaded'` was read from the backing and is unchanged; `'deleted'`
3024
- * is tombstoned. A `File` authored without a `state` defaults to `'created'`.
3025
- */
3026
- export declare type FileState = 'created' | 'modified' | 'loaded' | 'deleted';
3027
-
3028
2527
  /**
3029
2528
  * Filter a list of items by a {@link import('./types.js').ScopeInterface} allow-list of
3030
2529
  * keys — the pure, total set-membership primitive the context's build step and the agent
@@ -3096,34 +2595,13 @@ export declare function handleAgentRunnerJob(registry: AgentRegistryInterface, a
3096
2595
  */
3097
2596
  export declare const IMAGE_TOKEN_ESTIMATE = 512;
3098
2597
 
3099
- /**
3100
- * Infer a fenced-code language tag from a file path's extension — what a workspace text file
3101
- * (or the {@link fencedFile} renderer) renders its content block as.
3102
- *
3103
- * @remarks
3104
- * Reads the extension after the last `.` (case-insensitive) and maps it through a
3105
- * fixed extension→language table. An unknown extension, or a path with no extension,
3106
- * falls back to `'text'` (a safe, language-agnostic fence). Total — never throws.
3107
- *
3108
- * @param path - The document's file path (e.g. `'src/main.ts'`)
3109
- * @returns The inferred language tag (e.g. `'typescript'`), or `'text'` when unknown
3110
- *
3111
- * @example
3112
- * ```ts
3113
- * inferLanguage('src/main.ts') // 'typescript'
3114
- * inferLanguage('README.md') // 'markdown'
3115
- * inferLanguage('LICENSE') // 'text'
3116
- * ```
3117
- */
3118
- export declare function inferLanguage(path: string): string;
3119
-
3120
2598
  /**
3121
2599
  * An immutable named directive — a {@link InstructionInterface} assembled once from its
3122
2600
  * input (`name` / `content`, an optional `priority` defaulting to `0`), the `id` minted
3123
2601
  * at construction.
3124
2602
  *
3125
2603
  * @remarks
3126
- * A thin immutable value object (mirroring {@link import('../tools/Tool.js').Tool}): the
2604
+ * A thin immutable value object (mirroring {@link import('@orkestrel/tool').Tool}): the
3127
2605
  * constructor mints a fresh `id` (`crypto.randomUUID()`), copies the input's `name` /
3128
2606
  * `content`, resolves `priority` to the input's value or `0`, and carries the input's
3129
2607
  * per-item `format` override ONLY when supplied (assigned just when present, mirroring a
@@ -3363,32 +2841,10 @@ export declare function isAgentError(value: unknown): value is AgentError;
3363
2841
  export declare function isAgentJobError(value: unknown): value is AgentJobError;
3364
2842
 
3365
2843
  /**
3366
- * Whether a {@link FileContent} is the BINARY arm — the narrowing guard for the text-vs-binary
3367
- * union (AGENTS §14: narrow an untyped arm via a guard, never an `as`).
3368
- *
3369
- * @remarks
3370
- * Tests `'data' in content` structurally — there is no `modality` discriminant. A `true`
3371
- * narrows `content` to `{ data: string; mime: BinaryMIME }`, unlocking `.data` / `.mime`. An
3372
- * image is a binary whose `mime` starts with `image/` ({@link isImage}). Total — never throws.
2844
+ * Narrow an unknown caught value to a {@link ConversationError}.
3373
2845
  *
3374
- * @param content - The file content to test
3375
- * @returns `true` when `content` is the binary arm (carries base64 `data` + `mime`)
3376
- *
3377
- * @example
3378
- * ```ts
3379
- * if (isBinary(file.content)) file.content.data // the base64 payload + .mime
3380
- * ```
3381
- */
3382
- export declare function isBinary(content: FileContent): content is {
3383
- readonly data: string;
3384
- readonly mime: BinaryMIME;
3385
- };
3386
-
3387
- /**
3388
- * Narrow an unknown caught value to a {@link ConversationError}.
3389
- *
3390
- * @param value - The value to test (typically a `catch` binding)
3391
- * @returns `true` when `value` is a {@link ConversationError}
2846
+ * @param value - The value to test (typically a `catch` binding)
2847
+ * @returns `true` when `value` is a {@link ConversationError}
3392
2848
  *
3393
2849
  * @example
3394
2850
  * ```ts
@@ -3406,7 +2862,7 @@ export declare function isConversationError(value: unknown): value is Conversati
3406
2862
  * UNTRUSTED snapshot read (a storage row a
3407
2863
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
3408
2864
  * reads back from its opaque JSON column, a snapshot loaded from disk). The EXACT analogue of
3409
- * {@link isWorkspaceSnapshot}.
2865
+ * {@link import('@orkestrel/workspace').isWorkspaceSnapshot}.
3410
2866
  *
3411
2867
  * @remarks
3412
2868
  * A total guard (it NEVER throws — adversarial input returns `false`). It checks the snapshot's
@@ -3415,7 +2871,7 @@ export declare function isConversationError(value: unknown): value is Conversati
3415
2871
  * {@link SectionInterface} ({@link isSection}), and a `messages` array EVERY element of which is a
3416
2872
  * valid {@link MessageInterface} ({@link isMessage}) — enough to safely impose the
3417
2873
  * {@link ConversationSnapshot} type at a storage boundary WITHOUT a cast. The structural twin of
3418
- * {@link isWorkspaceSnapshot}. A malformed blob (a non-record, a missing / non-string `id`, a
2874
+ * {@link import('@orkestrel/workspace').isWorkspaceSnapshot}. A malformed blob (a non-record, a missing / non-string `id`, a
3419
2875
  * non-string `summary` when present, a non-array `sections` / `messages`, or any malformed
3420
2876
  * element) resolves `false`, so a
3421
2877
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
@@ -3434,68 +2890,23 @@ export declare function isConversationError(value: unknown): value is Conversati
3434
2890
  */
3435
2891
  export declare function isConversationSnapshot(value: unknown): value is ConversationSnapshot;
3436
2892
 
3437
- /**
3438
- * Whether an `unknown` is structurally a {@link FileInterface} record — the per-file step of the
3439
- * {@link isWorkspaceSnapshot} read-boundary narrow (AGENTS §14: narrow an untrusted storage read
3440
- * via a guard, never an `as`).
3441
- *
3442
- * @remarks
3443
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the file's SHAPE:
3444
- * a record with a `string` `path`, a `string` `state`, `number` `size` / `lines`, and a `content`
3445
- * that is EITHER the TEXT arm (`{ text: string; language: string }`) OR the BINARY arm
3446
- * (`{ data: string; mime: string }`) — the tagless {@link FileContent} union, told apart
3447
- * structurally exactly as {@link isText} / {@link isBinary} do (no `modality` discriminant). Enough
3448
- * to safely impose the {@link FileInterface} type at a storage boundary WITHOUT a cast; the `mime`
3449
- * is left as a broad `string` here (an open {@link BinaryMIME}, so any storage-read MIME string is
3450
- * accepted defensively rather than rejected against the current literal set).
3451
- *
3452
- * @param value - The value to test (one element of a snapshot's opaque `files` array)
3453
- * @returns `true` when `value` has the structural shape of a {@link FileInterface}
3454
- *
3455
- * @example
3456
- * ```ts
3457
- * isFile({ path: 'a.ts', content: { text: 'x', language: 'typescript' }, state: 'created', size: 1, lines: 1 }) // true
3458
- * isFile({ path: 'a.png', content: { data: 'AAAA', mime: 'image/png' }, state: 'created', size: 3, lines: 0 }) // true
3459
- * isFile({ path: 'a.ts' }) // false (missing content / state / size / lines)
3460
- * ```
3461
- */
3462
- export declare function isFile(value: unknown): value is FileInterface;
3463
-
3464
- /**
3465
- * Whether a {@link FileContent} is an IMAGE — a {@link isBinary} arm whose `mime` is an
3466
- * `image/*` type (an image is just a binary with an image MIME).
3467
- *
3468
- * @remarks
3469
- * Narrows to the binary arm first, then checks the `image/` MIME prefix — so a future
3470
- * non-image binary (a PDF, `'application/pdf'`) is binary but NOT an image. Total — never
3471
- * throws.
3472
- *
3473
- * @param content - The file content to test
3474
- * @returns `true` when `content` is a binary arm with an `image/*` MIME
3475
- *
3476
- * @example
3477
- * ```ts
3478
- * isImage({ data: '<base64>', mime: 'image/png' }) // true
3479
- * isImage({ text: 'hi', language: 'text' }) // false
3480
- * ```
3481
- */
3482
- export declare function isImage(content: FileContent): boolean;
3483
-
3484
2893
  /**
3485
2894
  * Whether an `unknown` is structurally a {@link MessageInterface} record — the per-message step of
3486
2895
  * the {@link isConversationSnapshot} read-boundary narrow (AGENTS §14: narrow an untrusted storage
3487
- * read via a guard, never an `as`). The conversation analogue of {@link isFile}.
2896
+ * read via a guard, never an `as`). The conversation analogue of
2897
+ * {@link import('@orkestrel/workspace').isFile}.
3488
2898
  *
3489
2899
  * @remarks
3490
2900
  * A total guard (it NEVER throws — adversarial input returns `false`). It checks the message's
3491
2901
  * SHAPE: a record with a `string` `id`, a `string` `role`, a `string` `content`, and — WHEN present
3492
- * — a `calls` array EVERY element of which is a valid {@link ToolCall} ({@link isToolCall} — the
2902
+ * — a `calls` array EVERY element of which is a valid
2903
+ * {@link import('@orkestrel/tool').ToolCall} ({@link isToolCall} — the
3493
2904
  * ASI06 fail-closed deepening: a tampered `calls` element rejects the message, so the snapshot
3494
2905
  * reads back as absent rather than replaying a malformed call) and an `images` that is an array
3495
2906
  * (an absent optional passes). The `role` is left as a broad `string` here (an open
3496
2907
  * {@link import('./types.js').MessageRole}, so any storage-read role string is accepted
3497
- * defensively rather than rejected against the current literal set) — exactly as {@link isFile}
3498
- * accepts a broad `state` / `mime`. Enough to safely impose the {@link MessageInterface} type at
2908
+ * defensively rather than rejected against the current literal set). Enough to safely impose the
2909
+ * {@link MessageInterface} type at
3499
2910
  * a storage boundary WITHOUT a cast.
3500
2911
  *
3501
2912
  * @param value - The value to test (one element of a snapshot's `messages` / a section's `messages`)
@@ -3551,132 +2962,16 @@ export declare function isProviderAbortError(value: unknown): value is ProviderA
3551
2962
  */
3552
2963
  export declare function isSection(value: unknown): value is SectionInterface;
3553
2964
 
3554
- /**
3555
- * Whether a {@link FileContent} is the TEXT arm — the narrowing guard for the text-vs-binary
3556
- * union (AGENTS §14: narrow an untyped arm via a guard, never an `as`).
3557
- *
3558
- * @remarks
3559
- * Tests `'text' in content` structurally — there is no `modality` discriminant. A `true`
3560
- * narrows `content` to `{ text: string; language: string }`, unlocking `.text` / `.language`.
3561
- * Total — never throws.
3562
- *
3563
- * @param content - The file content to test
3564
- * @returns `true` when `content` is the text arm (carries `text` + `language`)
3565
- *
3566
- * @example
3567
- * ```ts
3568
- * if (isText(file.content)) file.content.text // the literal text + .language
3569
- * ```
3570
- */
3571
- export declare function isText(content: FileContent): content is {
3572
- readonly text: string;
3573
- readonly language: string;
3574
- };
3575
-
3576
- /**
3577
- * Whether an `unknown` is structurally a {@link ToolCall} record — the per-call step of the
3578
- * {@link isMessage} read-boundary narrow (AGENTS §14: narrow an untrusted storage read via a
3579
- * guard, never an `as`).
3580
- *
3581
- * @remarks
3582
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the call's
3583
- * SHAPE: a record with a `string` `id`, a `string` `name`, and a record `arguments` — the real
3584
- * {@link ToolCall} shape a restored assistant turn replays to a provider. This is the ASI06
3585
- * fail-CLOSED element check: a tampered persisted call (`null`, a bare string, a non-record
3586
- * `arguments`) fails the guard, so {@link isMessage} — and through it
3587
- * {@link isConversationSnapshot} — rejects the whole snapshot and a poisoned store row reads
3588
- * back as ABSENT instead of replaying a malformed call into a chat template.
3589
- *
3590
- * @param value - The value to test (one element of an assistant message's `calls`)
3591
- * @returns `true` when `value` has the structural shape of a {@link ToolCall}
3592
- *
3593
- * @example
3594
- * ```ts
3595
- * isToolCall({ id: 'c1', name: 'search', arguments: { q: 'acme' } }) // true
3596
- * isToolCall({ id: 'c1', name: 'search' }) // false (missing arguments)
3597
- * isToolCall({ id: 'c1', name: 123, arguments: {} }) // false (non-string name)
3598
- * ```
3599
- */
3600
- export declare function isToolCall(value: unknown): value is ToolCall;
3601
-
3602
- /**
3603
- * Whether a {@link Range} is STRUCTURALLY valid — the predicate a ranged `write` checks
3604
- * before applying (a `false` here is the `RANGE` throw).
3605
- *
3606
- * @remarks
3607
- * Structural validity is independent of any content: every component must be `>= 1`
3608
- * (1-based lines and columns), and `start` must not come after `end` (an inverted range
3609
- * is invalid). A range that is structurally valid but reaches PAST the end of a specific
3610
- * text is still valid — it is {@link clampRange}d to the bounds when applied, not rejected.
3611
- * Total — never throws.
3612
- *
3613
- * @param range - The range to validate
3614
- * @returns `true` when every component is `>= 1` and `start` is at or before `end`
3615
- *
3616
- * @example
3617
- * ```ts
3618
- * isValidRange({ start: { line: 1, column: 1 }, end: { line: 2, column: 1 } }) // true
3619
- * isValidRange({ start: { line: 2, column: 1 }, end: { line: 1, column: 1 } }) // false (inverted)
3620
- * isValidRange({ start: { line: 0, column: 1 }, end: { line: 1, column: 1 } }) // false (sub-1 line)
3621
- * ```
3622
- */
3623
- export declare function isValidRange(range: Range): boolean;
3624
-
3625
- /**
3626
- * Narrow an unknown caught value to a {@link WorkspaceError}.
3627
- *
3628
- * @param value - The value to test (typically a `catch` binding)
3629
- * @returns `true` when `value` is a {@link WorkspaceError}
3630
- *
3631
- * @example
3632
- * ```ts
3633
- * try {
3634
- * workspace.prepend('icon.png', '// header')
3635
- * } catch (error) {
3636
- * if (isWorkspaceError(error) && error.code === 'MODALITY') skipBinary()
3637
- * }
3638
- * ```
3639
- */
3640
- export declare function isWorkspaceError(value: unknown): value is WorkspaceError;
3641
-
3642
- /**
3643
- * Narrow an `unknown` to a {@link WorkspaceSnapshot} — the AGENTS §14 boundary guard for an
3644
- * UNTRUSTED snapshot read (a storage row a
3645
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} reads back
3646
- * from its opaque JSON column, a snapshot loaded from disk).
3647
- *
3648
- * @remarks
3649
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the snapshot's
3650
- * SHAPE: a `string` `id` and a `files` array EVERY element of which is a valid {@link FileInterface}
3651
- * record ({@link isFile}) — enough to safely impose the {@link WorkspaceSnapshot} type at a storage
3652
- * boundary WITHOUT a cast. The structural twin of
3653
- * the analogous `isWorkflowSnapshot` in `@orkestrel/workflow`. A malformed blob (a non-record, a
3654
- * missing / non-string `id`, a non-array `files`, or any malformed file element) resolves `false`,
3655
- * so a {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} read
3656
- * yields `undefined` rather than a broken workspace.
3657
- *
3658
- * @param value - The value to test (an opaque storage read)
3659
- * @returns `true` when `value` has the structural shape of a {@link WorkspaceSnapshot}
3660
- *
3661
- * @example
3662
- * ```ts
3663
- * isWorkspaceSnapshot({ id: 'w1', files: [] }) // true
3664
- * isWorkspaceSnapshot({ id: 'w1', files: 'nope' }) // false
3665
- * isWorkspaceSnapshot({ files: [] }) // false (missing id)
3666
- * ```
3667
- */
3668
- export declare function isWorkspaceSnapshot(value: unknown): value is WorkspaceSnapshot;
3669
-
3670
2965
  /**
3671
2966
  * The in-memory {@link ConversationStoreInterface} — a process-lifetime `Map` of
3672
2967
  * {@link ConversationSnapshot}s keyed by conversation id, the DEFAULT store
3673
2968
  * {@link import('../../factories.js').createMemoryConversationStore} builds. The EXACT twin of
3674
- * {@link import('../../workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore}.
2969
+ * {@link import('@orkestrel/workspace').MemoryWorkspaceStore}.
3675
2970
  *
3676
2971
  * @remarks
3677
2972
  * A plain `Map<string, ConversationSnapshot>` (AGENTS §21 — the snapshot is already pure,
3678
2973
  * self-contained JSON, so no encoding is needed for the memory tier). Like the
3679
- * {@link import('../../workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore} it twins,
2974
+ * {@link import('@orkestrel/workspace').MemoryWorkspaceStore} it twins,
3680
2975
  * there is NO idle-TTL and NO eviction: a persisted conversation lives until an explicit `delete`. A
3681
2976
  * durable backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching
3682
2977
  * the {@link import('../ConversationManager.js').ConversationManager} or the
@@ -3712,50 +3007,6 @@ export declare class MemoryConversationStore implements ConversationStoreInterfa
3712
3007
  delete(id: string): Promise<void>;
3713
3008
  }
3714
3009
 
3715
- /**
3716
- * The in-memory {@link WorkspaceStoreInterface} — a process-lifetime `Map` of
3717
- * {@link WorkspaceSnapshot}s keyed by workspace id, the DEFAULT store
3718
- * {@link import('../../factories.js').createMemoryWorkspaceStore} builds.
3719
- *
3720
- * @remarks
3721
- * A plain `Map<string, WorkspaceSnapshot>` (AGENTS §21 — the snapshot is already pure,
3722
- * self-contained JSON, so no encoding is needed for the memory tier). Like the
3723
- * the analogous `MemoryWorkflowStore` in `@orkestrel/workflow` it twins,
3724
- * there is NO idle-TTL and NO eviction: a persisted workspace lives until an explicit `delete`. A
3725
- * durable backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching
3726
- * the {@link import('../WorkspaceManager.js').WorkspaceManager} or the
3727
- * {@link import('../Workspace.js').Workspace} — its driver-pluggable twin is
3728
- * {@link import('./DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} (the snapshot as one opaque
3729
- * JSON column).
3730
- *
3731
- * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.
3732
- * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).
3733
- * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).
3734
- *
3735
- * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method
3736
- * bijection with {@link WorkspaceStoreInterface}). Hydration is a caller concern: a
3737
- * {@link import('../WorkspaceManager.js').WorkspaceManager} reads a snapshot back and rebuilds the
3738
- * live workspace through the constructor `seed` (its `open` / `save`).
3739
- *
3740
- * @example
3741
- * ```ts
3742
- * import { createMemoryWorkspaceStore, createWorkspace } from '@src/core'
3743
- *
3744
- * const store = createMemoryWorkspaceStore()
3745
- * const workspace = createWorkspace()
3746
- * workspace.write('notes.txt', 'hello')
3747
- * await store.set(workspace.snapshot()) // persist the workspace
3748
- * const snapshot = await store.get(workspace.id)
3749
- * await store.delete(workspace.id) // drop it
3750
- * ```
3751
- */
3752
- export declare class MemoryWorkspaceStore implements WorkspaceStoreInterface {
3753
- #private;
3754
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
3755
- set(snapshot: WorkspaceSnapshot): Promise<void>;
3756
- delete(id: string): Promise<void>;
3757
- }
3758
-
3759
3010
  /**
3760
3011
  * The estimated per-message role/framing overhead {@link import('./helpers.js').estimateMessages}
3761
3012
  * adds on top of a message's content estimate — accounts for the fixed wire framing every
@@ -3833,42 +3084,6 @@ export declare interface MessageManagerInterface {
3833
3084
  /** The role a {@link MessageInterface} plays in a conversation turn. */
3834
3085
  export declare type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
3835
3086
 
3836
- /**
3837
- * Convert a 1-based {@link Position} to a 0-based string offset into `text` — the indexing
3838
- * primitive {@link sliceRange} / {@link spliceRange} use, clamped to `text.length`.
3839
- *
3840
- * @remarks
3841
- * Sums each preceding line's length plus its `\n` separator, then adds the `column - 1`
3842
- * within the target line, capped at `text.length`. Total — never throws (an out-of-bounds
3843
- * position yields a defined in-range offset).
3844
- *
3845
- * @param text - The text to index into
3846
- * @param position - The 1-based position to resolve
3847
- * @returns The 0-based offset (in `[0, text.length]`)
3848
- *
3849
- * @example
3850
- * ```ts
3851
- * offsetAt('ab\ncd', { line: 2, column: 1 }) // 3 (just after 'ab\n')
3852
- * ```
3853
- */
3854
- export declare function offsetAt(text: string, position: Position): number;
3855
-
3856
- /**
3857
- * A 1-based caret position inside a text file — `line` and `column` both count from `1`
3858
- * (column `1` is the first character of the line), matching an editor's gutter.
3859
- *
3860
- * @remarks
3861
- * The {@link import('./workspaces/Workspace.js').Workspace} edit surface speaks 1-based
3862
- * positions end to end ({@link Range}, {@link ReadResult}, {@link SearchMatch}). A
3863
- * structurally invalid component (`line < 1` or `column < 1`) is rejected by a range op
3864
- * (`RANGE`); an in-bounds-but-past-the-end one is CLAMPED to the nearest valid caret rather
3865
- * than rejected.
3866
- */
3867
- export declare interface Position {
3868
- readonly line: number;
3869
- readonly column: number;
3870
- }
3871
-
3872
3087
  /**
3873
3088
  * Thrown by a {@link ProviderInterface}'s `stream` when its bound signal aborts
3874
3089
  * mid-flight — carries the {@link ProviderResult} assembled from whatever streamed
@@ -3933,7 +3148,7 @@ export declare interface ProviderInterface {
3933
3148
  * built-in framing, beaten by a manager-options or per-item override). Omitted ⇒ the
3934
3149
  * provider is framing-agnostic and the managers' built-in defaults apply unchanged.
3935
3150
  */
3936
- readonly format?: ContextFormatInterface;
3151
+ readonly format?: ContextFormatInterface | undefined;
3937
3152
  /**
3938
3153
  * Generate one complete turn — resolves the assembled {@link ProviderResult}.
3939
3154
  *
@@ -4011,87 +3226,6 @@ export declare interface ProviderStreamOptions {
4011
3226
  readonly schema?: Readonly<Record<string, unknown>>;
4012
3227
  }
4013
3228
 
4014
- /**
4015
- * A half-open span of text between two {@link Position}s — `start` INCLUSIVE, `end`
4016
- * EXCLUSIVE (the character at `end` is not part of the span).
4017
- *
4018
- * @remarks
4019
- * A range whose `start` is at or before its `end` is valid; an inverted range
4020
- * (`start` after `end`) or one with a sub-1 component is structurally invalid and a range
4021
- * op throws `RANGE`. Each in-bounds component is clamped to the text before it is applied,
4022
- * so a range reaching past the end of the content reads / splices up to the end.
4023
- */
4024
- export declare interface Range {
4025
- readonly start: Position;
4026
- readonly end: Position;
4027
- }
4028
-
4029
- /**
4030
- * Assemble a 1-based nested {@link Range} from four FLAT caret integers — the bridge a
4031
- * workspace-editing tool (see `@orkestrel/tool`) uses from its small-model FLAT surface back to the
4032
- * nested {@link Range} the {@link import('./workspaces/Workspace.js').Workspace} edit surface
4033
- * speaks. Published as a public helper for that consumer.
4034
- *
4035
- * @remarks
4036
- * Pairs `(fromLine, fromColumn)` into `start` and `(toLine, toColumn)` into `end` verbatim — a pure
4037
- * structural lift, no validation (a structurally invalid range is rejected downstream by the ranged
4038
- * `write` it feeds, via {@link isValidRange}). Total — never throws. Zero-Node.
4039
- *
4040
- * @param fromLine - The 1-based start line
4041
- * @param fromColumn - The 1-based start column
4042
- * @param toLine - The 1-based end line
4043
- * @param toColumn - The 1-based end column
4044
- * @returns The `{ start, end }` {@link Range}
4045
- *
4046
- * @example
4047
- * ```ts
4048
- * rangeOf(1, 11, 1, 12) // { start: { line: 1, column: 11 }, end: { line: 1, column: 12 } }
4049
- * ```
4050
- */
4051
- export declare function rangeOf(fromLine: number, fromColumn: number, toLine: number, toColumn: number): Range;
4052
-
4053
- /**
4054
- * The outcome of a ranged {@link WorkspaceInterface.read} — the sliced `content` plus the
4055
- * `range` actually applied after clamping (so a caller learns the real span it got).
4056
- *
4057
- * @remarks
4058
- * `range` is the input {@link Range} CLAMPED to the file's bounds — when the requested
4059
- * span reached past the end of the text, `range` reflects the trimmed span that was read,
4060
- * never the original out-of-bounds request.
4061
- */
4062
- export declare interface ReadResult {
4063
- readonly content: string;
4064
- /** The actual (clamped) range applied to produce `content`. */
4065
- readonly range: Range;
4066
- }
4067
-
4068
- /**
4069
- * Options for {@link WorkspaceInterface.replace} — how the `query` matches (the same axes
4070
- * as {@link SearchOptions}).
4071
- *
4072
- * @param options - The replace options
4073
- *
4074
- * @remarks
4075
- * - `regex` — treat `query` as a regular expression source rather than a literal substring; defaults to `false`.
4076
- * - `exact` — match case-sensitively; defaults to `true`.
4077
- * - `limit` — stop after this many replacements across all files; defaults to unlimited.
4078
- */
4079
- export declare interface ReplaceOptions {
4080
- readonly regex?: boolean;
4081
- readonly exact?: boolean;
4082
- readonly limit?: number;
4083
- }
4084
-
4085
- /**
4086
- * The tally returned by {@link WorkspaceInterface.replace} — the `query` that ran, how
4087
- * many occurrences were `replaced`, and the number of `files` changed.
4088
- */
4089
- export declare interface ReplaceResult {
4090
- readonly query: string;
4091
- readonly replaced: number;
4092
- readonly files: number;
4093
- }
4094
-
4095
3229
  /**
4096
3230
  * The immutable per-run outcome value an {@link AgentInterface}'s loop replaces as it runs — the
4097
3231
  * assembled value its `stream`'s `result` promise resolves into a settled
@@ -4208,7 +3342,7 @@ export declare interface ScopeConfiguration {
4208
3342
  /**
4209
3343
  * Allowed ACTIVE-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
4210
3344
  * the filter {@link AgentContextInterface.build} applies to the active workspace's
4211
- * {@link WorkspaceInterface.files} before rendering them (text → the system block, image →
3345
+ * {@link import('@orkestrel/workspace').WorkspaceInterface.files} before rendering them (text → the system block, image →
4212
3346
  * the last user message).
4213
3347
  */
4214
3348
  readonly files?: readonly string[];
@@ -4223,7 +3357,7 @@ export declare interface ScopeInput extends ScopeConfiguration {
4223
3357
  }
4224
3358
 
4225
3359
  /**
4226
- * A named, immutable filter over a richer context's items — the four per-category
3360
+ * A named, immutable filter over a richer context's items — the three per-category
4227
3361
  * allow-lists ({@link ScopeConfiguration}) plus an `id` / `name`, and a `narrow` that
4228
3362
  * composes a tighter child by set-INTERSECTION.
4229
3363
  *
@@ -4311,7 +3445,7 @@ export declare type ScopeManagerEventMap = {
4311
3445
  * mints + stores one (never overwrites), `scopes()` lists them in insertion order.
4312
3446
  *
4313
3447
  * @remarks
4314
- * - **Registry.** `create(input)` mints a scope (an `id` + the four allow-lists) and
3448
+ * - **Registry.** `create(input)` mints a scope (an `id` + the three allow-lists) and
4315
3449
  * stores it; `count` is how many are stored. `scope(id)` looks one up; `scopes()` lists
4316
3450
  * them in insertion order. (Keyed by minted `id`, not `name`, so two scopes may share a
4317
3451
  * `name` and `create` always adds.)
@@ -4341,40 +3475,6 @@ export declare interface ScopeManagerOptions {
4341
3475
  readonly error?: EmitterErrorHandler;
4342
3476
  }
4343
3477
 
4344
- /**
4345
- * One hit from {@link WorkspaceInterface.search} — where the match was found and the line
4346
- * that carried it.
4347
- *
4348
- * @remarks
4349
- * `line` / `column` are 1-based (column `1` is the first character of the line); `length`
4350
- * is the matched substring's length; `content` is the FULL line the match sits on (not
4351
- * just the matched fragment), so a caller can render the hit in context.
4352
- */
4353
- export declare interface SearchMatch {
4354
- readonly path: string;
4355
- readonly line: number;
4356
- readonly column: number;
4357
- readonly length: number;
4358
- /** The full text of the line the match was found on. */
4359
- readonly content: string;
4360
- }
4361
-
4362
- /**
4363
- * Options for {@link WorkspaceInterface.search} — how the `query` matches.
4364
- *
4365
- * @param options - The search options
4366
- *
4367
- * @remarks
4368
- * - `regex` — treat `query` as a regular expression source rather than a literal substring; defaults to `false`.
4369
- * - `exact` — match case-sensitively; defaults to `true` (set `false` for a case-insensitive scan).
4370
- * - `limit` — stop after this many matches across all files; defaults to unlimited.
4371
- */
4372
- export declare interface SearchOptions {
4373
- readonly regex?: boolean;
4374
- readonly exact?: boolean;
4375
- readonly limit?: number;
4376
- }
4377
-
4378
3478
  /**
4379
3479
  * A slice of folded messages digested into a summary — the unit of compaction a
4380
3480
  * {@link ConversationInterface} produces when it `compact`s its live tail.
@@ -4417,48 +3517,6 @@ export declare interface SectionInterface {
4417
3517
  */
4418
3518
  export declare function settleAgentJob(agent: AgentInterface, allowPartial: boolean): Promise<AgentResult>;
4419
3519
 
4420
- /**
4421
- * Slice the substring of `text` spanned by a {@link Range} (start INCLUSIVE, end
4422
- * EXCLUSIVE), clamping the range to the text's bounds first — the read half of the ranged
4423
- * edit surface.
4424
- *
4425
- * @remarks
4426
- * Clamps via {@link clampRange}, resolves each end to an offset via {@link offsetAt}, and
4427
- * returns `text.slice(startOffset, endOffset)`. Total — never throws.
4428
- *
4429
- * @param text - The text to slice
4430
- * @param range - The 1-based range to extract
4431
- * @returns The spanned substring (empty when the clamped span is empty)
4432
- *
4433
- * @example
4434
- * ```ts
4435
- * sliceRange('hello\nworld', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }) // 'hello'
4436
- * ```
4437
- */
4438
- export declare function sliceRange(text: string, range: Range): string;
4439
-
4440
- /**
4441
- * Replace the span of `text` covered by a {@link Range} with `replacement` (start
4442
- * INCLUSIVE, end EXCLUSIVE), clamping the range to the text's bounds first — the write
4443
- * half of the ranged edit surface.
4444
- *
4445
- * @remarks
4446
- * Clamps via {@link clampRange}, then stitches `before + replacement + after` around the
4447
- * resolved offsets. An empty span (`start === end`) becomes a pure insertion. Total —
4448
- * never throws.
4449
- *
4450
- * @param text - The original text
4451
- * @param range - The 1-based range to overwrite
4452
- * @param replacement - The text to splice in place of the spanned range
4453
- * @returns The text with the spanned range replaced by `replacement`
4454
- *
4455
- * @example
4456
- * ```ts
4457
- * spliceRange('hello', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }, 'bye') // 'bye'
4458
- * ```
4459
- */
4460
- export declare function spliceRange(text: string, range: Range, replacement: string): string;
4461
-
4462
3520
  /**
4463
3521
  * A live event stream paired with the eventual settled result and a cancel — the
4464
3522
  * generic pull/streaming handle a long-running operation hands back.
@@ -4588,299 +3646,6 @@ export declare interface ThinkSplitterInterface {
4588
3646
  flush(): string;
4589
3647
  }
4590
3648
 
4591
- /**
4592
- * A registered tool — its {@link import('../types.js').ToolDefinition} schema (the
4593
- * `name` / `description` / `parameters` the model sees) bound to the `execute`
4594
- * handler that runs a call.
4595
- *
4596
- * @remarks
4597
- * A thin value object: the constructor stores the schema fields and the handler, and
4598
- * `execute` delegates to the handler verbatim. `parameters` is kept by reference (the
4599
- * open JSON-Schema the provider forwards as-is) — not cloned. The handler's `args` is
4600
- * the model-supplied `unknown` arguments record, narrowed inside the handler (§14);
4601
- * isolating a throw is the {@link ToolManager}'s job, not this class's. Event-free —
4602
- * no Emitter, no events.
4603
- *
4604
- * @example
4605
- * ```ts
4606
- * const tool = new Tool({
4607
- * name: 'add',
4608
- * description: 'Add two numbers',
4609
- * parameters: { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } } },
4610
- * execute: (args) => Number(args.a) + Number(args.b),
4611
- * })
4612
- * ```
4613
- */
4614
- export declare class Tool implements ToolInterface {
4615
- #private;
4616
- readonly name: string;
4617
- readonly description?: string;
4618
- readonly summary?: string;
4619
- readonly parameters?: Readonly<Record<string, unknown>>;
4620
- constructor(options: ToolOptions);
4621
- execute(args: Readonly<Record<string, unknown>>): Promise<unknown> | unknown;
4622
- }
4623
-
4624
- /**
4625
- * A tool call the model emitted — the tool `name` and its parsed `arguments`.
4626
- *
4627
- * @remarks
4628
- * `id` correlates the call with its later {@link ToolResult}; when the wire omits an
4629
- * id the provider mints one (a random UUID) so every call is addressable. `arguments`
4630
- * is always a record — the provider narrows the wire value (an object, or a JSON
4631
- * string it parses) to one, defaulting to `{}` when neither.
4632
- */
4633
- export declare interface ToolCall {
4634
- readonly id: string;
4635
- readonly name: string;
4636
- readonly arguments: Readonly<Record<string, unknown>>;
4637
- }
4638
-
4639
- /**
4640
- * The MCP `CallToolResult` shape a {@link ToolResult} maps to via {@link buildToolResult} —
4641
- * `content` blocks plus an `isError` flag set ONLY on failure (never `false`).
4642
- */
4643
- export declare interface ToolCallResult {
4644
- readonly content: readonly ToolResultContent[];
4645
- readonly isError?: true;
4646
- }
4647
-
4648
- /**
4649
- * A tool the model may call — its name, an optional description, and an optional
4650
- * JSON-Schema `parameters` object describing its arguments.
4651
- *
4652
- * @remarks
4653
- * Passed to a {@link ProviderInterface}'s `generate` / `stream`; the provider maps
4654
- * each into the wire's function-tool shape. `parameters` is an open JSON-Schema
4655
- * record (the provider forwards it verbatim).
4656
- */
4657
- export declare interface ToolDefinition {
4658
- readonly name: string;
4659
- readonly description?: string;
4660
- readonly parameters?: Readonly<Record<string, unknown>>;
4661
- }
4662
-
4663
- /**
4664
- * A registered tool: its {@link ToolDefinition} (the schema the model sees) plus the
4665
- * handler that runs a call.
4666
- *
4667
- * @remarks
4668
- * The definition fields (`name` / `description` / `parameters`) are what a
4669
- * {@link ProviderInterface} advertises to the model; `execute` is the local handler a
4670
- * {@link ToolManagerInterface} invokes when the model calls the tool. The handler's
4671
- * `args` is the model-supplied `unknown` JSON object — narrow it inside the handler
4672
- * (§14), never assert; the manager isolates a throw into a {@link ToolResult} `error`.
4673
- */
4674
- export declare interface ToolInterface extends ToolDefinition {
4675
- /**
4676
- * A one-or-two-sentence concise description advertised to the model IN PLACE of the
4677
- * full `description` (which stays on the tool for on-demand retrieval, e.g. a
4678
- * describe tool); absent ⇒ the full `description` is advertised exactly as today.
4679
- */
4680
- readonly summary?: string;
4681
- /**
4682
- * Run the tool. `args` is the model-supplied `unknown` JSON object — narrow it
4683
- * inside (§14); the manager isolates a throw into a {@link ToolResult} `error`.
4684
- *
4685
- * @param args - The model-supplied arguments record (an open `unknown` object)
4686
- * @returns The tool's result (sync or async) — folded into a `ToolResult.value`
4687
- */
4688
- execute(args: Readonly<Record<string, unknown>>): Promise<unknown> | unknown;
4689
- }
4690
-
4691
- /**
4692
- * The tool registry the agent loop dispatches model tool-calls through — resolves
4693
- * names, lists {@link ToolDefinition}s for the provider, and executes calls with
4694
- * per-call error isolation.
4695
- *
4696
- * @remarks
4697
- * - **Registry.** Tools live in an insertion-ordered `Map` keyed by `tool.name`;
4698
- * `add` takes one or a batch (§9.2) and a re-`add` of the same name OVERWRITES it
4699
- * (last write wins). `count` is the map size, `tool(name)` looks one up, `tools()`
4700
- * lists them in insertion order, and `definitions()` maps each to a plain
4701
- * {@link ToolDefinition} (`name` / `description?` / `parameters?`, the `execute`
4702
- * handler stripped) — exactly what a provider advertises to the model, with
4703
- * `tool.summary ?? tool.description` advertised as `description` (a lean `summary`
4704
- * stands in for the full text when set).
4705
- * - **Per-call error isolation (the load-bearing part).** `execute` resolves a
4706
- * {@link ToolCall}'s tool by name and ALWAYS resolves a {@link ToolResult}: an
4707
- * unknown name → `{ id, name, error: 'tool not found: <name>' }`; a successful run →
4708
- * `{ id, name, value }`; a handler throw is CAUGHT into `{ id, name, error }` (an
4709
- * `Error`'s message, else the stringified throw). A tool throw never escapes — it
4710
- * becomes a result the model can react to.
4711
- * - **Batch never fails as a whole.** `execute(calls)` runs every call via
4712
- * `Promise.all(calls.map(...))` and resolves the results correlated by `id` in the
4713
- * input order — a mix of success, throw, and not-found all resolve; one bad call
4714
- * does not reject the batch.
4715
- * - **Event-free.** A purely functional registry — no Emitter, no events.
4716
- *
4717
- * @example
4718
- * ```ts
4719
- * const manager = new ToolManager()
4720
- * manager.add(new Tool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
4721
- * const result = await manager.execute({ id: '1', name: 'add', arguments: { x: 1, y: 2 } })
4722
- * result.value // 3
4723
- * ```
4724
- */
4725
- export declare class ToolManager implements ToolManagerInterface {
4726
- #private;
4727
- get count(): number;
4728
- add(tool: ToolInterface): void;
4729
- add(tools: readonly ToolInterface[]): void;
4730
- tool(name: string): ToolInterface | undefined;
4731
- tools(): readonly ToolInterface[];
4732
- definitions(): readonly ToolDefinition[];
4733
- execute(call: ToolCall): Promise<ToolResult>;
4734
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
4735
- remove(name: string): boolean;
4736
- remove(names: readonly string[]): boolean;
4737
- clear(): void;
4738
- }
4739
-
4740
- /**
4741
- * A registry of tools — resolves names, lists definitions for the provider, and
4742
- * executes calls with per-call error isolation.
4743
- *
4744
- * @remarks
4745
- * - **Registry.** `add` registers one tool or a batch (§9.2), keyed by `tool.name`
4746
- * (a re-`add` of the same name overwrites — last write wins); `count` is the number
4747
- * registered. `tool(name)` looks one up; `tools()` lists them in insertion order;
4748
- * `definitions()` strips each to a plain {@link ToolDefinition} for the provider,
4749
- * advertising `tool.summary ?? tool.description` in the definition's `description`
4750
- * field — a lean `summary` stands in for the full `description` when set.
4751
- * - **Per-call error isolation.** `execute` resolves a {@link ToolCall}'s tool by name
4752
- * and runs it, ALWAYS resolving a {@link ToolResult}: a success carries `value`, a
4753
- * handler throw is caught into `error`, and an unknown name becomes a not-found
4754
- * `error`. A tool throw NEVER escapes — it becomes a result the model can react to.
4755
- * - **Batch never fails as a whole.** The array `execute` runs every call (via
4756
- * `Promise.all`) and resolves results correlated by `id` in order — one bad call
4757
- * (throw or not-found) does not fail the batch.
4758
- * - **Event-free.** A purely functional registry — no Emitter, no events.
4759
- */
4760
- export declare interface ToolManagerInterface {
4761
- readonly count: number;
4762
- add(tool: ToolInterface): void;
4763
- add(tools: readonly ToolInterface[]): void;
4764
- tool(name: string): ToolInterface | undefined;
4765
- tools(): readonly ToolInterface[];
4766
- definitions(): readonly ToolDefinition[];
4767
- execute(call: ToolCall): Promise<ToolResult>;
4768
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
4769
- remove(name: string): boolean;
4770
- remove(names: readonly string[]): boolean;
4771
- clear(): void;
4772
- }
4773
-
4774
- /**
4775
- * Options for `createTool` — the schema the model sees (`name` / `description` /
4776
- * `parameters`) plus the `execute` handler that runs a call.
4777
- *
4778
- * @remarks
4779
- * `name` is required (it keys the tool in a {@link ToolManagerInterface} and is what
4780
- * the model calls); `description` and `parameters` are the optional JSON-Schema the
4781
- * provider advertises (forwarded verbatim). `summary`, when set, is a lean
4782
- * one-or-two-sentence advertisement used IN PLACE of `description` in a
4783
- * {@link ToolManagerInterface.definitions} listing — `description` stays available on
4784
- * the tool for on-demand retrieval. `execute` receives the model-supplied arguments
4785
- * record and returns the tool's result (sync or async).
4786
- */
4787
- export declare interface ToolOptions {
4788
- readonly name: string;
4789
- readonly description?: string;
4790
- readonly summary?: string;
4791
- readonly parameters?: Readonly<Record<string, unknown>>;
4792
- readonly execute: (args: Readonly<Record<string, unknown>>) => Promise<unknown> | unknown;
4793
- }
4794
-
4795
- /**
4796
- * The outcome of executing a {@link ToolCall} — keyed back to the call by `id` and
4797
- * `name`, carrying either a `value` (the result) or an `error` string (the failure).
4798
- */
4799
- export declare interface ToolResult {
4800
- readonly id: string;
4801
- readonly name: string;
4802
- readonly value?: unknown;
4803
- readonly error?: string;
4804
- }
4805
-
4806
- /**
4807
- * One block of an MCP-shaped tool call response — plain text, the only content kind
4808
- * {@link buildToolResult} emits.
4809
- */
4810
- export declare interface ToolResultContent {
4811
- readonly type: 'text';
4812
- readonly text: string;
4813
- }
4814
-
4815
- /**
4816
- * A mutable, `path`-keyed working set of immutable {@link FileInterface}s — the
4817
- * in-memory editing surface over the file primitive.
4818
- *
4819
- * @remarks
4820
- * - **Registry.** Files live in an insertion-ordered `Map` keyed by `path`; `count` is the
4821
- * map size, `file(path)` looks one up, and `files()` lists them in insertion order.
4822
- * - **Write replaces the File (§11 immutability).** Every edit MINTS a replacement
4823
- * {@link FileInterface} (via {@link import('../factories.js').createFile}) rather than
4824
- * mutating in place, transitioning `state` to `'created'` for a brand-new path or
4825
- * `'modified'` for an existing one. A whole-file string write preserves an existing text
4826
- * file's `language`, else infers it from the `path` (`inferLanguage`); writing a string
4827
- * onto an existing BINARY path replaces it with a text file (a deliberate retype). A
4828
- * ranged `write` splices a `Range` of an existing text file.
4829
- * - **Modality rules.** Text-only ops on a binary file are rejected: a ranged `read` /
4830
- * `write`, `prepend`, and `append` throw `MODALITY`; a plain `read(path)` of a binary
4831
- * file returns `undefined`; `search` / `replace` skip binary files (0 matches).
4832
- * - **In-memory removal.** `remove(path)` / `remove(paths)` drop files outright (no
4833
- * tombstone — the `'loaded'` / `'deleted'` states are reserved for a future FileStore);
4834
- * `remove()` and `clear()` both empty the registry and emit the single `clear` signal.
4835
- * The disk/sync lifecycle (`load` / `revert` / `accept` / `purge` / `dirty`) is NOT part
4836
- * of this surface.
4837
- * - **Observable (§13).** The owned {@link emitter} ({@link WorkspaceEventMap}) carries
4838
- * `write` (the resulting file) / `remove` (the path) / `move` (`{ from, to }`) / `clear`.
4839
- * Every event is emitted directly, strictly AFTER the map mutation completes; the
4840
- * emitter isolates a listener throw and routes it to its `error` handler, so a buggy
4841
- * observer can never corrupt a mutation.
4842
- *
4843
- * @example
4844
- * ```ts
4845
- * const workspace = new Workspace()
4846
- * workspace.write('src/main.ts', 'const x = 1')
4847
- * workspace.file('src/main.ts')?.state // 'created'
4848
- * workspace.append('src/main.ts', '\nconst y = 2')
4849
- * workspace.file('src/main.ts')?.state // 'modified'
4850
- * workspace.read('src/main.ts') // 'const x = 1\nconst y = 2'
4851
- * ```
4852
- */
4853
- export declare class Workspace implements WorkspaceInterface {
4854
- #private;
4855
- constructor(options?: WorkspaceOptions, seed?: Iterable<readonly [string, FileInterface]>);
4856
- get id(): string;
4857
- get emitter(): EmitterInterface<WorkspaceEventMap>;
4858
- get count(): number;
4859
- file(path: string): FileInterface | undefined;
4860
- files(): readonly FileInterface[];
4861
- read(path: string): string | undefined;
4862
- read(path: string, range: Range): ReadResult | undefined;
4863
- read(paths: readonly string[]): Readonly<Record<string, string>>;
4864
- has(path: string): boolean;
4865
- has(paths: readonly string[]): boolean;
4866
- search(query: string, options?: SearchOptions): readonly SearchMatch[];
4867
- replace(query: string, replacement: string, options?: ReplaceOptions): ReplaceResult;
4868
- write(path: string, content: string): void;
4869
- write(path: string, content: string, range: Range): void;
4870
- write(files: Readonly<Record<string, string>>): void;
4871
- prepend(path: string, content: string): void;
4872
- prepend(files: Readonly<Record<string, string>>): void;
4873
- append(path: string, content: string): void;
4874
- append(files: Readonly<Record<string, string>>): void;
4875
- move(from: string, to: string): boolean;
4876
- move(mapping: Readonly<Record<string, string>>): boolean;
4877
- remove(): void;
4878
- remove(path: string): boolean;
4879
- remove(paths: readonly string[]): boolean;
4880
- clear(): void;
4881
- snapshot(): WorkspaceSnapshot;
4882
- }
4883
-
4884
3649
  /**
4885
3650
  * The section header {@link import('./AgentContext.js').AgentContext}'s `build()` renders the
4886
3651
  * ACTIVE workspace's TEXT files under — the leading line of the dedicated workspace block in the
@@ -4895,393 +3660,4 @@ export declare class Workspace implements WorkspaceInterface {
4895
3660
  */
4896
3661
  export declare const WORKSPACE_SECTION_HEADER = "## Workspace";
4897
3662
 
4898
- /**
4899
- * An error thrown by the in-memory {@link import('./workspaces/Workspace.js').Workspace} edit
4900
- * surface.
4901
- *
4902
- * @remarks
4903
- * Carries a {@link WorkspaceErrorCode} and an optional `context` bag naming the offending
4904
- * path / range. Thrown for a text-only operation aimed at an image file (`MODALITY` — a
4905
- * ranged read / write, `prepend`, or `append`), an invalid `search` / `replace` regular
4906
- * expression (`PATTERN`), and a structurally invalid ranged-write {@link import('./types.js').Range}
4907
- * (`RANGE` — inverted, or a sub-1 line / column).
4908
- */
4909
- export declare class WorkspaceError extends Error {
4910
- readonly code: WorkspaceErrorCode;
4911
- readonly context?: Readonly<Record<string, unknown>>;
4912
- constructor(code: WorkspaceErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
4913
- }
4914
-
4915
- /**
4916
- * The machine-readable code a {@link import('./errors.js').WorkspaceError} carries
4917
- * (AGENTS §12) — a `catch` branches on `error.code` instead of parsing the message.
4918
- *
4919
- * @remarks
4920
- * - `MODALITY` — a text-only op (ranged read/write, `prepend`, `append`) was aimed at a binary file.
4921
- * - `PATTERN` — a `search` / `replace` `query` was an invalid regular expression (with `regex: true`).
4922
- * - `RANGE` — a ranged `write` got a structurally invalid {@link Range} (inverted, or a sub-1 line/column).
4923
- * - `TOOL` — a handler-level validation fault raised by a workspace-editing tool boundary (see
4924
- * `@orkestrel/tool`) when the agent-supplied args were malformed / unknown. Distinct from the
4925
- * surface faults above (which the live workspace raises) — this is the tool boundary rejecting an
4926
- * un-parseable operation before any workspace call.
4927
- */
4928
- export declare type WorkspaceErrorCode = 'MODALITY' | 'PATTERN' | 'RANGE' | 'TOOL';
4929
-
4930
- /**
4931
- * The observable events a {@link import('./workspaces/Workspace.js').Workspace} emits
4932
- * (AGENTS §13) — each fired AFTER the corresponding mutation completes.
4933
- *
4934
- * @remarks
4935
- * - `write` — a file was written / edited, carrying the resulting {@link FileInterface}.
4936
- * - `remove` — a single file was dropped, carrying its `path`.
4937
- * - `move` — a file was re-keyed, carrying `{ from, to }`.
4938
- * - `clear` — the workspace was emptied (the canonical "emptied" signal, fired by both `remove()` and `clear()`).
4939
- */
4940
- export declare type WorkspaceEventMap = {
4941
- readonly write: readonly [file: FileInterface];
4942
- readonly remove: readonly [path: string];
4943
- readonly move: readonly [move: {
4944
- readonly from: string;
4945
- readonly to: string;
4946
- }];
4947
- readonly clear: readonly [];
4948
- };
4949
-
4950
- /**
4951
- * The data to author a {@link WorkspaceInterface} through a
4952
- * {@link WorkspaceManagerInterface} — the optional `id`, the reserved `on` hooks + `error`
4953
- * handler (§13), and an optional `seed` of initial files.
4954
- *
4955
- * @remarks
4956
- * `id` is the workspace's identity (minted when omitted). `on` / `error` are the §8/§13
4957
- * keys (initial {@link WorkspaceEventMap} listeners + the listener-error handler), flowing
4958
- * the manager's defaults in unless overridden. `seed` is the construction-time hydration
4959
- * seam (path → {@link FileInterface}) — the only way to seat a non-text (binary) file, and
4960
- * what a future FileStore reads a snapshot into; seeding is silent (no `write` events).
4961
- */
4962
- export declare interface WorkspaceInput {
4963
- readonly id?: string;
4964
- readonly on?: EmitterHooks<WorkspaceEventMap>;
4965
- readonly error?: EmitterErrorHandler;
4966
- /** Pre-seeded initial files (path → File), placed silently at construction. */
4967
- readonly seed?: Iterable<readonly [string, FileInterface]>;
4968
- }
4969
-
4970
- /**
4971
- * A mutable, `path`-keyed working set of immutable {@link FileInterface}s — the in-memory
4972
- * editing surface over the file primitive.
4973
- *
4974
- * @remarks
4975
- * Files live in an insertion-ordered map keyed by `path`. Every edit REPLACES the
4976
- * immutable {@link FileInterface} at a path with a new one (transitioning its `state` to
4977
- * `'created'` for a fresh path or `'modified'` for an existing one) rather than mutating in
4978
- * place. The disk/sync lifecycle (`load` / `revert` / `accept` / `purge` / `dirty`) is
4979
- * deliberately NOT part of this surface — it is deferred to a future FileStore. Text-only
4980
- * operations on a binary file are governed by the modality rules (a ranged read/write,
4981
- * `prepend`, and `append` throw `MODALITY`; a plain `read` returns `undefined`; `search` /
4982
- * `replace` skip the file). Observe mutations through the owned {@link emitter} (AGENTS §13).
4983
- */
4984
- export declare interface WorkspaceInterface {
4985
- /** The workspace's identity — its key in a {@link WorkspaceManagerInterface}; minted when not supplied. */
4986
- readonly id: string;
4987
- /** The push observation surface (AGENTS §13) — `write` / `remove` / `move` / `clear`. */
4988
- readonly emitter: EmitterInterface<WorkspaceEventMap>;
4989
- /** The number of files currently held. */
4990
- readonly count: number;
4991
- /** Look up one file by path, or `undefined` when absent. */
4992
- file(path: string): FileInterface | undefined;
4993
- /** List every file in insertion order. */
4994
- files(): readonly FileInterface[];
4995
- read(path: string): string | undefined;
4996
- read(path: string, range: Range): ReadResult | undefined;
4997
- read(paths: readonly string[]): Readonly<Record<string, string>>;
4998
- has(path: string): boolean;
4999
- has(paths: readonly string[]): boolean;
5000
- /** Scan every text file's lines for `query`, returning each hit in insertion order. */
5001
- search(query: string, options?: SearchOptions): readonly SearchMatch[];
5002
- /** Replace `query` with `replacement` across every text file, returning the tally. */
5003
- replace(query: string, replacement: string, options?: ReplaceOptions): ReplaceResult;
5004
- write(path: string, content: string): void;
5005
- write(path: string, content: string, range: Range): void;
5006
- write(files: Readonly<Record<string, string>>): void;
5007
- prepend(path: string, content: string): void;
5008
- prepend(files: Readonly<Record<string, string>>): void;
5009
- append(path: string, content: string): void;
5010
- append(files: Readonly<Record<string, string>>): void;
5011
- move(from: string, to: string): boolean;
5012
- move(mapping: Readonly<Record<string, string>>): boolean;
5013
- remove(): void;
5014
- remove(path: string): boolean;
5015
- remove(paths: readonly string[]): boolean;
5016
- /** Empty the workspace (emits `clear`). */
5017
- clear(): void;
5018
- /**
5019
- * Serialize this workspace to a plain, JSON-serializable {@link WorkspaceSnapshot} — its
5020
- * `id` plus a flat list of its {@link FileInterface}s (each already carries its `path`).
5021
- *
5022
- * @remarks
5023
- * The container serializes ITSELF (`{ id: this.id, files: this.files() }`) — the
5024
- * {@link WorkspaceStoreInterface} persistence seam's payload, paralleling the
5025
- * {@link ConversationInterface}'s own snapshot a later track adds. The snapshot is the
5026
- * durable analogue of the constructor `seed`: a {@link WorkspaceManagerInterface} HYDRATES a
5027
- * workspace from `snapshot.files` through that seam (see {@link WorkspaceManagerInterface.open}).
5028
- * Pure — the `File`s are already frozen plain records (so the snapshot `structuredClone`s /
5029
- * JSON-round-trips losslessly), and snapshotting mutates nothing.
5030
- *
5031
- * @returns The {@link WorkspaceSnapshot} (`{ id, files }`)
5032
- */
5033
- snapshot(): WorkspaceSnapshot;
5034
- }
5035
-
5036
- /**
5037
- * The registry of {@link Workspace}s keyed by `id`, in insertion order, WITH an active pointer
5038
- * — the §9 store over the workspace layer PLUS the `active` / `switch` seam the context renders.
5039
- * Event-free (a registry, like {@link ConversationManager}); the observability lives on each
5040
- * {@link Workspace}.
5041
- *
5042
- * @remarks
5043
- * - **Registry.** Workspaces live in an insertion-ordered `Map` keyed by `id`. `add(input?)`
5044
- * mints a {@link Workspace} (its `id` from `input` or `crypto.randomUUID()`), flowing the
5045
- * manager's default `on` / `error` in unless the `input` OVERRIDES them, and stores it (an
5046
- * already-present `id` OVERWRITES — last write wins). `count` is the map size, `workspace(id)`
5047
- * looks one up, `workspaces()` lists them in insertion order.
5048
- * - **Active pointer.** `active` is the active workspace (what the context renders), `undefined`
5049
- * until the FIRST `add` (which auto-activates it — a registry with workspaces always has one
5050
- * active). A subsequent `add` leaves `active` unchanged. `switch(id)` re-points `active` to the
5051
- * workspace with `id` and returns it; an unknown `id` returns `undefined` and leaves `active`
5052
- * unchanged (the lenient lookup style — never throws, no new error code).
5053
- * - **Removal.** `remove` drops one by id, or a batch (§9.2) — `true` when any was removed;
5054
- * removing the ACTIVE workspace sets `active` to `undefined`. `clear` empties the registry and
5055
- * sets `active` to `undefined`.
5056
- * - **Durable open / save (the optional `store`).** With a {@link WorkspaceStoreInterface} supplied
5057
- * (the `store` option), `open(id)` HYDRATES a workspace on a registry miss — it `store.get(id)`s
5058
- * the snapshot and rebuilds a fresh {@link Workspace} through the constructor `seed`
5059
- * (`snapshot.files` → path → File), then activates it — and `save(id)` PERSISTS a registered
5060
- * workspace's `snapshot()`. Both are LENIENT without a store (open resolves only registered ids,
5061
- * save is a no-op `false`), consistent with the lenient `switch`.
5062
- * - **Event-free.** A purely registry store — no Emitter, no events (each workspace owns its own
5063
- * observable `emitter`).
5064
- *
5065
- * @example
5066
- * ```ts
5067
- * const manager = new WorkspaceManager()
5068
- * const first = manager.add() // auto-activates — active === first
5069
- * manager.add({ id: 'scratch' }) // leaves active unchanged
5070
- * manager.switch('scratch') // re-points active to the 'scratch' workspace
5071
- * manager.count // 2
5072
- * ```
5073
- */
5074
- export declare class WorkspaceManager implements WorkspaceManagerInterface {
5075
- #private;
5076
- constructor(options?: WorkspaceManagerOptions);
5077
- get count(): number;
5078
- get active(): WorkspaceInterface | undefined;
5079
- workspace(id: string): WorkspaceInterface | undefined;
5080
- workspaces(): readonly WorkspaceInterface[];
5081
- add(input?: WorkspaceInput): WorkspaceInterface;
5082
- switch(id: string): WorkspaceInterface | undefined;
5083
- open(id: string): Promise<WorkspaceInterface | undefined>;
5084
- save(id: string): Promise<boolean>;
5085
- remove(ids: readonly string[]): boolean;
5086
- remove(id: string): boolean;
5087
- clear(): void;
5088
- }
5089
-
5090
- /**
5091
- * A registry of {@link WorkspaceInterface}s keyed by their `id`, in insertion order, WITH an
5092
- * active pointer — the §9 store over the workspace layer PLUS the `active` / `switch` seam the
5093
- * context renders. Event-free (each {@link WorkspaceInterface} owns its own `emitter`).
5094
- *
5095
- * @remarks
5096
- * - **Registry.** `count` is how many are stored. `add(input?)` mints a
5097
- * {@link WorkspaceInterface} (its `id` from `input` or a random UUID), flowing the manager's
5098
- * default `on` / `error` in unless the `input` overrides them; `add` of an already-present
5099
- * `id` OVERWRITES it (last write wins). `workspace(id)` looks one up (`undefined` when
5100
- * absent); `workspaces()` lists them in insertion order.
5101
- * - **Active pointer.** `active` is the active workspace (what the context renders), `undefined`
5102
- * until the FIRST `add` (which auto-activates it). A later `add` leaves `active` unchanged.
5103
- * `switch(id)` re-points `active` to the workspace with `id` and returns it; an unknown `id`
5104
- * returns `undefined` and leaves `active` unchanged (the lenient lookup style — never throws).
5105
- * - **Removal.** `remove` drops one by id, or a batch (§9.2, array overload FIRST) — `true`
5106
- * when any was removed; removing the ACTIVE workspace sets `active` to `undefined`. `clear`
5107
- * empties the registry and sets `active` to `undefined`.
5108
- * - **Durable open / save (the optional `store` seam).** When a {@link WorkspaceStoreInterface}
5109
- * is supplied (the `store` option), `open(id)` HYDRATES a workspace from the store on a registry
5110
- * miss (rebuilding it through the constructor `seed` from `snapshot.files`) and `save(id)`
5111
- * PERSISTS a registered workspace's {@link WorkspaceInterface.snapshot}. Both are LENIENT without
5112
- * a store — `open` resolves only registered ids, `save` is a no-op (`false`) — consistent with
5113
- * the lenient `switch`.
5114
- * - **Event-free.** A purely registry store — no Emitter, no events (each workspace owns its own).
5115
- */
5116
- export declare interface WorkspaceManagerInterface {
5117
- readonly count: number;
5118
- /** The active workspace — what the context renders; `undefined` until the first `add`. */
5119
- readonly active: WorkspaceInterface | undefined;
5120
- workspace(id: string): WorkspaceInterface | undefined;
5121
- workspaces(): readonly WorkspaceInterface[];
5122
- add(input?: WorkspaceInput): WorkspaceInterface;
5123
- switch(id: string): WorkspaceInterface | undefined;
5124
- /**
5125
- * Resolve a workspace by id, ACTIVATING it — from the registry if present, else HYDRATED from
5126
- * the optional {@link WorkspaceStoreInterface} (`store`).
5127
- *
5128
- * @remarks
5129
- * - If `id` is ALREADY registered, it is ACTIVATED (`switch`ed to) and returned — no store hit.
5130
- * - Else if a `store` is set, `store.get(id)` is awaited; on a HIT the snapshot is rehydrated
5131
- * into a fresh {@link WorkspaceInterface} through the constructor `seed`
5132
- * (`add({ id, seed: snapshot.files.map((file) => [file.path, file]) })`), which registers AND
5133
- * auto-activates it (or activates it explicitly), and it is returned.
5134
- * - Else (no store, or a store MISS) ⇒ `undefined` (lenient — no throw).
5135
- *
5136
- * @param id - The workspace id to open
5137
- * @returns The activated {@link WorkspaceInterface}, or `undefined` when neither registered nor stored
5138
- */
5139
- open(id: string): Promise<WorkspaceInterface | undefined>;
5140
- /**
5141
- * Persist a REGISTERED workspace's {@link WorkspaceInterface.snapshot} to the optional
5142
- * {@link WorkspaceStoreInterface} (`store`).
5143
- *
5144
- * @remarks
5145
- * Lenient: when a `store` is set AND `id` is registered, `store.set(workspace.snapshot())` is
5146
- * awaited and `true` is returned; otherwise (no store, OR an unknown id) it is a NO-OP returning
5147
- * `false` — never a throw, consistent with the lenient `switch`.
5148
- *
5149
- * @param id - The id of the registered workspace to persist
5150
- * @returns `true` when the snapshot was persisted; `false` when no store / unknown id
5151
- */
5152
- save(id: string): Promise<boolean>;
5153
- remove(ids: readonly string[]): boolean;
5154
- remove(id: string): boolean;
5155
- clear(): void;
5156
- }
5157
-
5158
- /**
5159
- * Options for `createWorkspaceManager` — the per-workspace `on` / `error` defaults the
5160
- * workspaces it creates inherit.
5161
- *
5162
- * @remarks
5163
- * `on` / `error` are the default {@link WorkspaceEventMap} listeners + the emitter's
5164
- * listener-error handler (§13) flowed into every workspace the manager `add`s (a per-`add`
5165
- * {@link WorkspaceInput.on} / `.error` overrides them). Kept minimal (AGENTS §21) — a
5166
- * registry needs no more.
5167
- */
5168
- export declare interface WorkspaceManagerOptions {
5169
- /** The default event listeners for workspaces this manager creates (a per-`add` override wins). */
5170
- readonly on?: EmitterHooks<WorkspaceEventMap>;
5171
- /** The default listener-error handler (a per-`add` override wins). */
5172
- readonly error?: EmitterErrorHandler;
5173
- /**
5174
- * The optional durable {@link WorkspaceStoreInterface} backing {@link WorkspaceManagerInterface.open}
5175
- * / {@link WorkspaceManagerInterface.save} — a memory / JSON / SQLite / IndexedDB store a workspace
5176
- * is HYDRATED from (`open` a registry-miss) and PERSISTED to (`save`). Omitted ⇒ the manager is
5177
- * registry-only: `open` resolves only what is already registered, and `save` is a no-op (`false`).
5178
- */
5179
- readonly store?: WorkspaceStoreInterface;
5180
- }
5181
-
5182
- /**
5183
- * Options for {@link import('./factories.js').createWorkspace} / the
5184
- * {@link import('./workspaces/Workspace.js').Workspace} constructor.
5185
- *
5186
- * @param options - The workspace options
5187
- *
5188
- * @remarks
5189
- * - `id` — the workspace's identity (its key in a {@link WorkspaceManagerInterface}); MINTED via `crypto.randomUUID()` when omitted.
5190
- * - `on` — initial {@link WorkspaceEventMap} listeners (AGENTS §8), wired at construction.
5191
- * - `error` — the emitter's listener-error handler (AGENTS §13); a throw from any listener is routed here instead of rethrown.
5192
- */
5193
- export declare interface WorkspaceOptions {
5194
- readonly id?: string;
5195
- readonly on?: EmitterHooks<WorkspaceEventMap>;
5196
- readonly error?: EmitterErrorHandler;
5197
- }
5198
-
5199
- /**
5200
- * A JSON-serializable snapshot of a workspace's state — its `id` plus a FLAT list of its
5201
- * {@link FileInterface}s — the durable payload the {@link WorkspaceStoreInterface} persists.
5202
- *
5203
- * @remarks
5204
- * Pure JSON DATA (no class instances, no functions): each {@link FileInterface} is already a
5205
- * PLAIN frozen record that `structuredClone`s / JSON-round-trips losslessly, and a `File` carries
5206
- * its OWN `path`, so a flat `files` list is enough to reconstruct the path-keyed map (no nested
5207
- * `path → File` object needed for serialization). The snapshot the container produces from itself
5208
- * ({@link WorkspaceInterface.snapshot}); the durable analogue of the constructor `seed`. A
5209
- * {@link WorkspaceManagerInterface} hydrates a workspace from `snapshot.files` through the seed
5210
- * seam (see {@link WorkspaceManagerInterface.open}). It is narrowed back from an untrusted storage
5211
- * read by {@link import('./helpers.js').isWorkspaceSnapshot} (the AGENTS §14 boundary narrow).
5212
- */
5213
- export declare interface WorkspaceSnapshot {
5214
- readonly id: string;
5215
- readonly files: readonly FileInterface[];
5216
- }
5217
-
5218
- /**
5219
- * One row of the table a
5220
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} persists
5221
- * — a workspace `id` plus its {@link WorkspaceSnapshot} held as ONE OPAQUE JSON column.
5222
- *
5223
- * @remarks
5224
- * The Database twin of {@link WorkspaceStoreInterface} stores the snapshot whole (the `snapshot`
5225
- * column is a `rawShape`, an opaque JSON blob — exactly as the analogous
5226
- * `WorkflowSnapshotRow` in `@orkestrel/workflow` stores a workflow snapshot), so the
5227
- * row type stays FLAT and the `File`-list snapshot shape never forces the contract to `Infer` it.
5228
- * The column therefore reads back as the broad `unknown`; the store narrows it to a
5229
- * {@link WorkspaceSnapshot} on `get` ({@link import('./helpers.js').isWorkspaceSnapshot}, the
5230
- * AGENTS §14 boundary narrow). `id` mirrors {@link WorkspaceSnapshot.id} (the primary key), so a
5231
- * `set` writes `{ id: snapshot.id, snapshot }`.
5232
- */
5233
- export declare interface WorkspaceSnapshotRow {
5234
- readonly id: string;
5235
- /** The whole {@link WorkspaceSnapshot} as one opaque JSON blob — read back as `unknown`, narrowed on `get`. */
5236
- readonly snapshot: unknown;
5237
- }
5238
-
5239
- /**
5240
- * The durable persistence seam for a {@link WorkspaceSnapshot} — three async primitives
5241
- * (`get` / `set` / `delete`) keyed by a workspace id, mirroring the analogous
5242
- * `WorkflowStoreInterface` in `@orkestrel/workflow` (and the driver-swap pattern a server
5243
- * surface's session store would follow).
5244
- *
5245
- * @remarks
5246
- * The store persists the {@link WorkspaceSnapshot} — the self-contained, pure-JSON workspace
5247
- * state — so a JSON / SQLite / IndexedDB backend swaps in WITHOUT touching the manager or the
5248
- * workspace: the in-memory default
5249
- * {@link import('./workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore} and its
5250
- * driver-pluggable twin
5251
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} (the
5252
- * snapshot as one opaque JSON column) share THIS one interface. Hydration is NOT a store concern
5253
- * — a {@link WorkspaceManagerInterface} reads a snapshot back and rebuilds the live workspace
5254
- * through the constructor `seed` (see {@link WorkspaceManagerInterface.open} /
5255
- * {@link WorkspaceManagerInterface.save}).
5256
- *
5257
- * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
5258
- * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
5259
- * param (mirroring the analogous `WorkflowStoreInterface.set` in `@orkestrel/workflow`). UNLIKE a
5260
- * session store there is NO idle-TTL / eviction — a persisted workspace lives until an explicit
5261
- * `delete`. It is concrete over {@link WorkspaceSnapshot} — no generic parameter (AGENTS §21
5262
- * minimal-interface), since the snapshot is the ONE payload a workspace store persists.
5263
- */
5264
- export declare interface WorkspaceStoreInterface {
5265
- /**
5266
- * Resolve the persisted snapshot for `id`, or `undefined` if none is stored.
5267
- *
5268
- * @param id - The workspace id to resolve (a {@link WorkspaceSnapshot.id})
5269
- * @returns The persisted snapshot, or `undefined` if absent
5270
- */
5271
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
5272
- /**
5273
- * Insert or replace a snapshot under its own `snapshot.id` (no separate id param —
5274
- * mirroring the analogous `WorkflowStoreInterface.set` in `@orkestrel/workflow`).
5275
- *
5276
- * @param snapshot - The snapshot to store (keyed by its `id`)
5277
- */
5278
- set(snapshot: WorkspaceSnapshot): Promise<void>;
5279
- /**
5280
- * Drop a snapshot by id; an absent id is a no-op (no throw).
5281
- *
5282
- * @param id - The workspace id to drop
5283
- */
5284
- delete(id: string): Promise<void>;
5285
- }
5286
-
5287
3663
  export { }