@orkestrel/agent 0.0.11 → 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
@@ -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`,
@@ -225,9 +231,13 @@ 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;
@@ -252,12 +262,13 @@ export declare interface AgentContextInterface {
252
262
  *
253
263
  * @remarks
254
264
  * **The active workspace (rendered by carrier) — the SOLE document/image context.** When
255
- * `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
256
267
  * `scope.files` (a three-way allow-list; `undefined` ⇒ all active files), then split by
257
- * 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
258
269
  * `## Workspace` section in the system block — each a fenced
259
270
  * `` File: <path>\n```<language>\n<text>\n``` `` block — placed just after the instructions
260
- * 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`
261
272
  * attached to the LAST user message (a vision provider reads images off a user turn).
262
273
  * ACTIVE-ONLY — never the other registered workspaces; with NO active workspace nothing is
263
274
  * rendered for workspaces.
@@ -303,9 +314,10 @@ export declare interface AgentContextInterface {
303
314
  * Options for `createAgentContext` — the richer context's configuration.
304
315
  *
305
316
  * @remarks
306
- * `system` is the optional system prompt prepended to the turn's input. `tools` /
307
- * `instructions` / `workspaces` are optional pre-built managers to reuse (bring your own
308
- * 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
309
321
  * initial active filter applied at `build()` time (and at the loop's tool-advertise step); it
310
322
  * defaults to `undefined` — no filtering — and can be changed through the context's `apply`
311
323
  * method afterwards. `conversations` is the structural {@link ConversationManagerInterface} the
@@ -317,7 +329,10 @@ export declare interface AgentContextInterface {
317
329
  */
318
330
  export declare interface AgentContextOptions {
319
331
  readonly system?: string;
320
- /** 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
+ */
321
336
  readonly tools?: ToolManagerInterface;
322
337
  /** A pre-built instruction registry to reuse; an empty one is created when omitted. */
323
338
  readonly instructions?: InstructionManagerInterface;
@@ -943,8 +958,8 @@ export declare type AgentStreamInterface = StreamInterface<AgentChunk, AgentResu
943
958
  * (an ALLOWLIST — only matched rules that allow get through), pass an `allowed: false`
944
959
  * `fallback`.
945
960
  * - **Consulted before each tool call.** The agent loop calls `evaluate({ call })` for
946
- * every {@link import('./types.js').ToolCall} the model emits; a denied call is fed
947
- * 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`
948
963
  * chunk + a tool message) instead of being executed — no tool run, no budget cost —
949
964
  * so the model sees the denial and can react.
950
965
  * - **Synchronous now.** The async human-approval handshake (request / grant / deny) is
@@ -1049,37 +1064,6 @@ export declare interface AuthorityRule {
1049
1064
  readonly reason?: string;
1050
1065
  }
1051
1066
 
1052
- /**
1053
- * The binary MIME types a {@link FileContent} binary arm carries — the labels for a
1054
- * base64-encoded payload.
1055
- *
1056
- * @remarks
1057
- * Initialized to the four image MIMEs a vision-capable provider accepts; the union is OPEN
1058
- * by design — a future binary MIME (e.g. `'application/pdf'`) slots in purely additively (a
1059
- * new member), with nothing above it changed. {@link import('./helpers.js').isImage}
1060
- * narrows a binary arm to an image by the `image/` prefix, so an image is just a binary with
1061
- * an image MIME.
1062
- */
1063
- export declare type BinaryMIME = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
1064
-
1065
- /**
1066
- * Project a {@link ToolResult} into the MCP `CallToolResult` shape — a top-level `error`
1067
- * maps to a single `isError: true` text block (the failure reason), otherwise the `value`
1068
- * is JSON-stringified into a single text block with no `isError`.
1069
- *
1070
- * @param result - The {@link ToolResult} to project (as returned by {@link import('./tools/ToolManager.js').ToolManager.execute})
1071
- * @returns The equivalent {@link ToolCallResult}
1072
- *
1073
- * @example
1074
- * ```ts
1075
- * buildToolResult({ id: 'c1', name: 'search', error: 'max depth' })
1076
- * // { content: [{ type: 'text', text: 'max depth' }], isError: true }
1077
- * buildToolResult({ id: 'c1', name: 'search', value: { count: 1 } })
1078
- * // { content: [{ type: 'text', text: '{"count":1}' }] }
1079
- * ```
1080
- */
1081
- export declare function buildToolResult(result: ToolResult): ToolCallResult;
1082
-
1083
1067
  /**
1084
1068
  * A minimal unbounded async channel — the eager pump WRITES chunks into it (`push`)
1085
1069
  * and ends it (`close` / `fail`) regardless of consumption; a consumer READS them back
@@ -1100,47 +1084,6 @@ export declare class Channel<T> {
1100
1084
  drain(): AsyncGenerator<T, void>;
1101
1085
  }
1102
1086
 
1103
- /**
1104
- * Clamp a 1-based {@link Position} to the bounds of `text` — every component pinned into a
1105
- * caret that actually exists in the content.
1106
- *
1107
- * @remarks
1108
- * `line` is pinned to `[1, lineCount]` and `column` to `[1, lineLength + 1]` of the
1109
- * resolved line (column `lineLength + 1` is the caret just past the line's last
1110
- * character). So a position beyond the end of the text resolves to the end rather than
1111
- * overflowing. Total — never throws.
1112
- *
1113
- * @param text - The text the position addresses
1114
- * @param position - The 1-based position to clamp
1115
- * @returns The clamped {@link Position}
1116
- *
1117
- * @example
1118
- * ```ts
1119
- * clampPosition('ab\ncd', { line: 9, column: 9 }) // { line: 2, column: 3 } (end of 'cd')
1120
- * ```
1121
- */
1122
- export declare function clampPosition(text: string, position: Position): Position;
1123
-
1124
- /**
1125
- * Clamp both ends of a {@link Range} to the bounds of `text` — the actual span a ranged
1126
- * read / write applies (and the `range` a {@link import('./types.js').ReadResult} reports).
1127
- *
1128
- * @remarks
1129
- * Clamps `start` and `end` independently via {@link clampPosition}, so a range reaching
1130
- * past the end of the content is trimmed to the content's end. Total — never throws.
1131
- *
1132
- * @param text - The text the range addresses
1133
- * @param range - The 1-based range to clamp
1134
- * @returns The clamped {@link Range}
1135
- *
1136
- * @example
1137
- * ```ts
1138
- * clampRange('ab\ncd', { start: { line: 1, column: 1 }, end: { line: 9, column: 9 } })
1139
- * // { start: { line: 1, column: 1 }, end: { line: 2, column: 3 } }
1140
- * ```
1141
- */
1142
- export declare function clampRange(text: string, range: Range): Range;
1143
-
1144
1087
  /**
1145
1088
  * The PER-RUN auto-compaction state threaded through an {@link AgentInterface}'s loop —
1146
1089
  * a tiny immutable value replaced within a holder created fresh per run (so no state leaks across runs or a
@@ -1176,28 +1119,6 @@ export declare interface CompactOptions {
1176
1119
  readonly sections?: number;
1177
1120
  }
1178
1121
 
1179
- /**
1180
- * Compute the byte size of a {@link FileContent} — the `size` a {@link FileInterface} derives
1181
- * once when built.
1182
- *
1183
- * @remarks
1184
- * Dispatches on {@link isText}: a text arm is its UTF-8 byte length (via `new
1185
- * TextEncoder().encode(text).length` — the standard Web/Node API, so a multi-byte
1186
- * character like `'é'` or `'😀'` counts as its real encoded bytes, NOT its character
1187
- * count); a binary arm is the decoded payload byte length of its base64 `data` (via
1188
- * {@link decodedSize}, computed arithmetically — no `atob` / `Buffer`). Total — never throws.
1189
- *
1190
- * @param content - The file content to size
1191
- * @returns The byte size (UTF-8 bytes for text; decoded payload bytes for binary)
1192
- *
1193
- * @example
1194
- * ```ts
1195
- * computeSize({ text: 'café', language: 'text' }) // 5 (é is two bytes)
1196
- * computeSize({ data: 'AAAA', mime: 'image/png' }) // 3
1197
- * ```
1198
- */
1199
- export declare function computeSize(content: FileContent): number;
1200
-
1201
1122
  /**
1202
1123
  * A provider's OPTIONAL context-framing default, keyed by section kind — the framing a
1203
1124
  * model prefers (e.g. XML tags vs. Markdown headers), declared by a
@@ -1408,7 +1329,8 @@ export declare type ConversationEventMap = {
1408
1329
  * (initial {@link ConversationEventMap} listeners). `snapshot` is
1409
1330
  * the construction-time hydration seam — a {@link ConversationSnapshot} whose `id` / `summary` /
1410
1331
  * `sections` / live tail are RESTORED into the new conversation (the live `summarize` / `keep` /
1411
- * `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
1412
1334
  * {@link ConversationManagerInterface.open} reads a stored snapshot back through; hydration is
1413
1335
  * silent (no events). When both `snapshot.id` and `id` are given, `snapshot.id` wins (the snapshot
1414
1336
  * IS the conversation's identity).
@@ -1422,7 +1344,7 @@ export declare interface ConversationInput {
1422
1344
  /** Overrides the manager's default `sections` cap for this conversation. */
1423
1345
  readonly sections?: number;
1424
1346
  readonly on?: EmitterHooks<ConversationEventMap>;
1425
- /** 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. */
1426
1348
  readonly snapshot?: ConversationSnapshot;
1427
1349
  }
1428
1350
 
@@ -1598,8 +1520,9 @@ export declare interface ConversationInterface {
1598
1520
  *
1599
1521
  * @remarks
1600
1522
  * The container serializes ITSELF (`{ id, summary, sections, messages: this.messages() }`) — the
1601
- * {@link ConversationStoreInterface} persistence seam's payload, the EXACT analogue of
1602
- * {@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
1603
1526
  * CONFIG re-supplied on hydrate (a `ConversationSummarizer` is a function, not data). The snapshot
1604
1527
  * is the durable analogue of the constructor `seed`: a {@link ConversationManagerInterface}
1605
1528
  * HYDRATES a conversation from it through that seam (see {@link ConversationManagerInterface.open}).
@@ -1615,7 +1538,7 @@ export declare interface ConversationInterface {
1615
1538
  * The registry of {@link Conversation}s keyed by `id`, in insertion order, WITH an active pointer
1616
1539
  * — the §9 store over the conversation layer PLUS the `active` / `switch` seam the
1617
1540
  * {@link import('../AgentContext.js').AgentContext} renders. Event-free (a registry, like
1618
- * {@link import('../workspaces/WorkspaceManager.js').WorkspaceManager}); the observability lives
1541
+ * {@link import('@orkestrel/workspace').WorkspaceManager}); the observability lives
1619
1542
  * on each {@link Conversation}.
1620
1543
  *
1621
1544
  * @remarks
@@ -1665,7 +1588,7 @@ export declare class ConversationManager implements ConversationManagerInterface
1665
1588
  * A registry of {@link ConversationInterface}s keyed by their `id`, in insertion order, WITH an
1666
1589
  * active pointer — the §9 store over the conversation layer PLUS the `active` / `switch` seam the
1667
1590
  * {@link AgentContextInterface} renders. Event-free (a registry, like
1668
- * {@link WorkspaceManagerInterface}); the observability lives on each
1591
+ * {@link import('@orkestrel/workspace').WorkspaceManagerInterface}); the observability lives on each
1669
1592
  * {@link ConversationInterface}.
1670
1593
  *
1671
1594
  * @remarks
@@ -1688,8 +1611,8 @@ export declare class ConversationManager implements ConversationManagerInterface
1688
1611
  * miss (rebuilding it through the constructor `seed` from the snapshot, flowing the manager's
1689
1612
  * default `summarize` / `keep` in) and `save(id)` PERSISTS a registered conversation's
1690
1613
  * {@link ConversationInterface.snapshot}. Both are LENIENT without a store — `open` resolves only
1691
- * registered ids, `save` is a no-op (`false`) — consistent with the lenient `switch`. The EXACT
1692
- * 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.
1693
1616
  * - **Event-free.** A purely registry store — no Emitter, no events (each conversation owns
1694
1617
  * its own).
1695
1618
  */
@@ -1760,8 +1683,8 @@ export declare interface ConversationManagerOptions {
1760
1683
  * {@link ConversationManagerInterface.open} / {@link ConversationManagerInterface.save} — a memory
1761
1684
  * / JSON / SQLite / IndexedDB store a conversation is HYDRATED from (`open` a registry-miss) and
1762
1685
  * PERSISTED to (`save`). Omitted ⇒ the manager is registry-only: `open` resolves only what is
1763
- * already registered, and `save` is a no-op (`false`). The EXACT analogue of
1764
- * {@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`.
1765
1688
  */
1766
1689
  readonly store?: ConversationStoreInterface;
1767
1690
  }
@@ -1827,7 +1750,8 @@ export declare interface ConversationReferenceOptions {
1827
1750
  /**
1828
1751
  * A JSON-serializable snapshot of a conversation's state — its `id`, the rollup `summary`, the
1829
1752
  * compacted `sections`, and the live tail `messages` — the durable payload the
1830
- * {@link ConversationStoreInterface} persists. The EXACT analogue of {@link WorkspaceSnapshot}.
1753
+ * {@link ConversationStoreInterface} persists. The exact analogue of
1754
+ * {@link import('@orkestrel/workspace').WorkspaceSnapshot}.
1831
1755
  *
1832
1756
  * @remarks
1833
1757
  * Pure JSON DATA (no class instances, no functions): each {@link SectionInterface} and
@@ -1856,12 +1780,13 @@ export declare interface ConversationSnapshot {
1856
1780
  * One row of the table a
1857
1781
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
1858
1782
  * persists — a conversation `id` plus its {@link ConversationSnapshot} held as ONE OPAQUE JSON
1859
- * column. The EXACT analogue of {@link WorkspaceSnapshotRow}.
1783
+ * column. The exact analogue of {@link import('@orkestrel/workspace').WorkspaceSnapshotRow}.
1860
1784
  *
1861
1785
  * @remarks
1862
1786
  * The Database twin of {@link ConversationStoreInterface} stores the snapshot whole (the `snapshot`
1863
- * column is a `rawShape`, an opaque JSON blob — exactly as {@link WorkspaceSnapshotRow} stores a
1864
- * 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
1865
1790
  * forces the contract to `Infer` it. The column therefore reads back as the broad `unknown`; the
1866
1791
  * store narrows it to a {@link ConversationSnapshot} on `get`
1867
1792
  * ({@link import('./helpers.js').isConversationSnapshot}, the AGENTS §14 boundary narrow). `id`
@@ -1876,8 +1801,8 @@ export declare interface ConversationSnapshotRow {
1876
1801
 
1877
1802
  /**
1878
1803
  * The durable persistence seam for a {@link ConversationSnapshot} — three async primitives
1879
- * (`get` / `set` / `delete`) keyed by a conversation id, the EXACT analogue of
1880
- * {@link WorkspaceStoreInterface}.
1804
+ * (`get` / `set` / `delete`) keyed by a conversation id, the exact analogue of
1805
+ * {@link import('@orkestrel/workspace').WorkspaceStoreInterface}.
1881
1806
  *
1882
1807
  * @remarks
1883
1808
  * The store persists the {@link ConversationSnapshot} — the self-contained, pure-JSON conversation
@@ -1893,7 +1818,9 @@ export declare interface ConversationSnapshotRow {
1893
1818
  *
1894
1819
  * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
1895
1820
  * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
1896
- * 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
1897
1824
  * / eviction — a persisted conversation lives until an explicit `delete`. It is concrete over
1898
1825
  * {@link ConversationSnapshot} — no generic parameter (AGENTS §21 minimal-interface), since the
1899
1826
  * snapshot is the ONE payload a conversation store persists.
@@ -1908,7 +1835,7 @@ export declare interface ConversationStoreInterface {
1908
1835
  get(id: string): Promise<ConversationSnapshot | undefined>;
1909
1836
  /**
1910
1837
  * Insert or replace a snapshot under its own `snapshot.id` (no separate id param —
1911
- * mirroring {@link WorkspaceStoreInterface.set}).
1838
+ * mirroring {@link import('@orkestrel/workspace').WorkspaceStoreInterface}'s `set`).
1912
1839
  *
1913
1840
  * @param snapshot - The snapshot to store (keyed by its `id`)
1914
1841
  */
@@ -1939,28 +1866,6 @@ export declare interface ConversationStoreInterface {
1939
1866
  */
1940
1867
  export declare type ConversationSummarizer = (messages: readonly MessageInterface[]) => Promise<string>;
1941
1868
 
1942
- /**
1943
- * Count the lines of a {@link FileContent} — the `lines` a {@link FileInterface} derives once
1944
- * when built.
1945
- *
1946
- * @remarks
1947
- * A text arm's line count is `0` for the empty string, otherwise one more than the
1948
- * number of newline (`\n`) separators it contains — so `'a'` is one line, `'a\nb'` is two,
1949
- * and a trailing newline `'a\n'` counts the empty final line as two. A binary arm has no
1950
- * lines and returns `0`. Total — never throws.
1951
- *
1952
- * @param content - The file content to count lines for
1953
- * @returns The line count (text line count; `0` for a binary arm or empty text)
1954
- *
1955
- * @example
1956
- * ```ts
1957
- * countLines({ text: '', language: 'text' }) // 0
1958
- * countLines({ text: 'a\nb\nc', language: 'text' }) // 3
1959
- * countLines({ data: 'AAAA', mime: 'image/png' }) // 0
1960
- * ```
1961
- */
1962
- export declare function countLines(content: FileContent): number;
1963
-
1964
1869
  /**
1965
1870
  * Create an agent loop — an {@link AgentInterface} composing a
1966
1871
  * {@link ProviderInterface}, its {@link AgentContextInterface}, and a tool registry
@@ -2095,8 +2000,9 @@ export declare function createAgentQueue(options: AgentQueueOptions): QueueInter
2095
2000
  *
2096
2001
  * @example
2097
2002
  * ```ts
2003
+ * import { createAgentRegistry } from '@src/core'
2098
2004
  * import type { ProviderInterface } from '@src/core'
2099
- * import { createAgentRegistry, createTool } from '@src/core'
2005
+ * import { createTool } from '@orkestrel/tool'
2100
2006
  *
2101
2007
  * declare const provider: ProviderInterface // any concrete implementation supplied by the host app
2102
2008
  * const registry = createAgentRegistry({
@@ -2177,24 +2083,6 @@ export declare function createAgentRunner(options: AgentRunnerOptions): RunnerIn
2177
2083
  */
2178
2084
  export declare function createAuthority(options?: AuthorityOptions): AuthorityInterface;
2179
2085
 
2180
- /**
2181
- * Build the BINARY {@link FileContent} arm — the §4.2.3 split constructor for binary (a
2182
- * separate function per arm, not one constructor dispatching on a discriminator parameter).
2183
- * An image is just a binary with an image {@link BinaryMIME}.
2184
- *
2185
- * @param data - The base64-encoded binary payload
2186
- * @param mime - The {@link BinaryMIME} that labels the payload
2187
- * @returns A `{ data; mime }` content arm
2188
- *
2189
- * @example
2190
- * ```ts
2191
- * import { createBinaryContent } from '@src/core'
2192
- *
2193
- * createBinaryContent('<base64>', 'image/png') // { data: '<base64>', mime: 'image/png' }
2194
- * ```
2195
- */
2196
- export declare function createBinaryContent(data: string, mime: BinaryMIME): FileContent;
2197
-
2198
2086
  /**
2199
2087
  * Create a conversation — a {@link ConversationInterface} grouping messages above a flat
2200
2088
  * message store it OWNS DIRECTLY, with compaction into summarized sections, a regenerated
@@ -2266,12 +2154,14 @@ export declare function createConversationManager(options?: ConversationManagerO
2266
2154
  /**
2267
2155
  * Create a {@link DatabaseConversationStore} over any {@link DriverInterface} — the durable,
2268
2156
  * driver-pluggable backing for the conversation persistence seam, the opt-in twin of
2269
- * {@link createMemoryConversationStore}. The EXACT twin of {@link createDatabaseWorkspaceStore}.
2157
+ * {@link createMemoryConversationStore}. The exact twin of
2158
+ * {@link import('@orkestrel/workspace').createDatabaseWorkspaceStore}.
2270
2159
  *
2271
2160
  * @remarks
2272
2161
  * Builds a one-table database (`conversations`, keyed by `id`) over the supplied driver, the snapshot
2273
2162
  * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
2274
- * `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
2275
2165
  * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
2276
2166
  * AND keeps the row type FLAT (the column reads back as `unknown`, narrowed on `get` by
2277
2167
  * {@link import('./helpers.js').isConversationSnapshot}). The `driver` DEFAULTS to
@@ -2296,65 +2186,6 @@ export declare function createConversationManager(options?: ConversationManagerO
2296
2186
  */
2297
2187
  export declare function createDatabaseConversationStore(driver?: DriverInterface): ConversationStoreInterface;
2298
2188
 
2299
- /**
2300
- * Create a {@link DatabaseWorkspaceStore} over any {@link DriverInterface} — the durable,
2301
- * driver-pluggable backing for the workspace persistence seam, the opt-in twin of
2302
- * {@link createMemoryWorkspaceStore}.
2303
- *
2304
- * @remarks
2305
- * Builds a one-table database (`workspaces`, keyed by `id`) over the supplied driver, the snapshot
2306
- * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
2307
- * `rawShape` (a JSON blob), exactly as
2308
- * the analogous `createDatabaseWorkflowStore` in `@orkestrel/workflow` stores its snapshot. The
2309
- * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
2310
- * AND keeps the row type FLAT (the column reads back as `unknown`, narrowed on `get` by
2311
- * {@link import('./helpers.js').isWorkspaceSnapshot}). The `driver` DEFAULTS to
2312
- * {@link createMemoryDriver}, so the store ALSO works in memory out of the box; pass a server
2313
- * `createJSONDriver` / `createSQLiteDriver` (or a browser IndexedDB driver) for a persistent one —
2314
- * the durability is the driver's job, the store engine is shared. It swaps in behind
2315
- * {@link WorkspaceStoreInterface} WITHOUT touching the manager or the workspace.
2316
- *
2317
- * @param driver - The storage backend the snapshots persist to (defaults to {@link createMemoryDriver})
2318
- * @returns A {@link WorkspaceStoreInterface} over the driver
2319
- *
2320
- * @example
2321
- * ```ts
2322
- * import { createDatabaseWorkspaceStore, createMemoryDriver, createWorkspaceManager } from '@src/core'
2323
- *
2324
- * const store = createDatabaseWorkspaceStore(createMemoryDriver()) // a durable driver swaps in here
2325
- * const manager = createWorkspaceManager({ store })
2326
- * const workspace = manager.add()
2327
- * workspace.write('notes.txt', 'hello')
2328
- * await manager.save(workspace.id) // persist the workspace (one JSON column)
2329
- * ```
2330
- */
2331
- export declare function createDatabaseWorkspaceStore(driver?: DriverInterface): WorkspaceStoreInterface;
2332
-
2333
- /**
2334
- * Create a file — an immutable {@link FileInterface} from its `path` + {@link FileContent}
2335
- * and optional {@link import('./types.js').FileState}, with `size` / `lines` DERIVED from the
2336
- * content.
2337
- *
2338
- * @remarks
2339
- * Returns a PLAIN `Object.freeze`d record (NOT a class instance) — the `path` IS its identity
2340
- * (there is no `id`). Only `path` / `content` are required; `state` defaults to `'created'`
2341
- * when omitted. `size` (via {@link computeSize}) and `lines` (via {@link countLines}) are
2342
- * computed from the content here (so they never drift from it). Frozen + plain, so it
2343
- * `structuredClone`s losslessly and is never mutated after creation.
2344
- *
2345
- * @param input - `path` / `content` (required) and an optional `state` (see {@link FileInput})
2346
- * @returns A frozen {@link FileInterface} record
2347
- *
2348
- * @example
2349
- * ```ts
2350
- * import { createFile, createTextContent } from '@src/core'
2351
- *
2352
- * const file = createFile({ path: 'src/main.ts', content: createTextContent('const x = 1', 'typescript') })
2353
- * file.lines // 1
2354
- * ```
2355
- */
2356
- export declare function createFile(input: FileInput): FileInterface;
2357
-
2358
2189
  /**
2359
2190
  * Create an instruction — an immutable {@link InstructionInterface} (a named directive)
2360
2191
  * from its `name` / `content` and optional `priority`, the `id` minted at construction.
@@ -2411,11 +2242,13 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2411
2242
  * Create the in-memory conversation store — a {@link ConversationStoreInterface} backed by a
2412
2243
  * process-lifetime `Map` of {@link import('./types.js').ConversationSnapshot}s keyed by conversation
2413
2244
  * id, the DEFAULT backing for the durable {@link ConversationManagerInterface.open} /
2414
- * {@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}.
2415
2247
  *
2416
2248
  * @remarks
2417
2249
  * A plain `Map` (the snapshot is already pure JSON, so no encoding is needed for the memory tier),
2418
- * 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
2419
2252
  * same shape a durable backend fits); UNLIKE a session store there is NO idle-TTL / eviction — a
2420
2253
  * persisted conversation lives until an explicit `delete`. Its driver-pluggable twin is
2421
2254
  * {@link createDatabaseConversationStore} (the snapshot as one opaque JSON column over a `databases`
@@ -2439,37 +2272,6 @@ export declare function createInstructionManager(options?: InstructionManagerOpt
2439
2272
  */
2440
2273
  export declare function createMemoryConversationStore(): ConversationStoreInterface;
2441
2274
 
2442
- /**
2443
- * Create the in-memory workspace store — a {@link WorkspaceStoreInterface} backed by a
2444
- * process-lifetime `Map` of {@link import('./types.js').WorkspaceSnapshot}s keyed by workspace id,
2445
- * the DEFAULT backing for the durable {@link WorkspaceManagerInterface.open} /
2446
- * {@link WorkspaceManagerInterface.save} seam.
2447
- *
2448
- * @remarks
2449
- * A plain `Map` (the snapshot is already pure JSON, so no encoding is needed for the memory tier),
2450
- * the structural twin of the analogous `createMemoryWorkflowStore` in `@orkestrel/workflow`.
2451
- * `get` / `set` / `delete` are async (the same shape a durable backend fits); UNLIKE a session
2452
- * store there is NO idle-TTL / eviction — a persisted workspace lives until an explicit `delete`.
2453
- * Its driver-pluggable twin is {@link createDatabaseWorkspaceStore} (the snapshot as one opaque
2454
- * JSON column over a `databases` table) — for a DURABLE store pass it a JSON / SQLite / IndexedDB
2455
- * driver, and it swaps in WITHOUT touching the manager or the workspace. Hydration stays a manager
2456
- * concern: read a snapshot back and rebuild the live workspace through the constructor `seed`.
2457
- *
2458
- * @returns A memory-backed {@link WorkspaceStoreInterface}
2459
- *
2460
- * @example
2461
- * ```ts
2462
- * import { createMemoryWorkspaceStore, createWorkspaceManager } from '@src/core'
2463
- *
2464
- * const store = createMemoryWorkspaceStore()
2465
- * const manager = createWorkspaceManager({ store })
2466
- * const workspace = manager.add()
2467
- * workspace.write('notes.txt', 'hello')
2468
- * await manager.save(workspace.id) // persist the workspace
2469
- * ```
2470
- */
2471
- export declare function createMemoryWorkspaceStore(): WorkspaceStoreInterface;
2472
-
2473
2275
  /**
2474
2276
  * Create a named scope — an immutable {@link ScopeInterface} from its `name` and the four
2475
2277
  * optional per-category allow-lists, the `id` minted at construction.
@@ -2519,23 +2321,6 @@ export declare function createScope(input: ScopeInput): ScopeInterface;
2519
2321
  */
2520
2322
  export declare function createScopeManager(options?: ScopeManagerOptions): ScopeManagerInterface;
2521
2323
 
2522
- /**
2523
- * Build the TEXT {@link FileContent} arm — the §4.2.3 split constructor for text (a separate
2524
- * function per arm, not one constructor dispatching on a discriminator parameter).
2525
- *
2526
- * @param text - The literal text body
2527
- * @param language - The fenced-code language the text renders as (e.g. `'typescript'`)
2528
- * @returns A `{ text; language }` content arm
2529
- *
2530
- * @example
2531
- * ```ts
2532
- * import { createTextContent } from '@src/core'
2533
- *
2534
- * createTextContent('const x = 1', 'typescript') // { text: 'const x = 1', language: 'typescript' }
2535
- * ```
2536
- */
2537
- export declare function createTextContent(text: string, language: string): FileContent;
2538
-
2539
2324
  /**
2540
2325
  * Create a stream-stateful `<think>` separator — a {@link ThinkSplitterInterface} that
2541
2326
  * splits a thinking model's in-content `<think>…</think>` reasoning spans away from
@@ -2565,122 +2350,12 @@ export declare function createTextContent(text: string, language: string): FileC
2565
2350
  */
2566
2351
  export declare function createThinkSplitter(): ThinkSplitterInterface;
2567
2352
 
2568
- /**
2569
- * Create a tool — a {@link ToolInterface} binding a {@link ToolDefinition} schema
2570
- * (the `name` / `description` / `parameters` the model sees) to the `execute` handler
2571
- * that runs a call.
2572
- *
2573
- * @remarks
2574
- * Only `name` is required (it keys the tool in a {@link ToolManagerInterface} and is
2575
- * what the model calls); `description` / `parameters` are the optional JSON-Schema the
2576
- * provider advertises (forwarded verbatim). The handler's `args` is the model-supplied
2577
- * `unknown` arguments record — narrow it inside (§14); a `createToolManager` registry
2578
- * isolates a throw into a `ToolResult.error`.
2579
- *
2580
- * @param options - `name` (required), optional `description` / `parameters`, and the
2581
- * `execute` handler (see {@link ToolOptions})
2582
- * @returns A working {@link ToolInterface}
2583
- *
2584
- * @example
2585
- * ```ts
2586
- * import { createTool } from '@src/core'
2587
- *
2588
- * const add = createTool({
2589
- * name: 'add',
2590
- * description: 'Add two numbers',
2591
- * execute: (args) => Number(args.a) + Number(args.b),
2592
- * })
2593
- * ```
2594
- */
2595
- export declare function createTool(options: ToolOptions): ToolInterface;
2596
-
2597
- /**
2598
- * Create a tool registry — a {@link ToolManagerInterface} that resolves tool names,
2599
- * lists {@link ToolDefinition}s for the provider, and executes calls with per-call
2600
- * error isolation.
2601
- *
2602
- * @remarks
2603
- * Starts empty; `add` registers one tool or a batch (§9.2), `definitions()` yields the
2604
- * schemas to hand a provider, and `execute` runs a {@link ToolCall} (or a batch),
2605
- * ALWAYS resolving a {@link ToolResult} — a handler throw becomes an `error` result and
2606
- * an unknown name a not-found `error`, so a tool throw never escapes and a batch never
2607
- * fails as a whole.
2608
- *
2609
- * @returns An empty {@link ToolManagerInterface}
2610
- *
2611
- * @example
2612
- * ```ts
2613
- * import { createTool, createToolManager } from '@src/core'
2614
- *
2615
- * const tools = createToolManager()
2616
- * tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
2617
- * const result = await tools.execute({ id: '1', name: 'add', arguments: { x: 1, y: 2 } })
2618
- * ```
2619
- */
2620
- export declare function createToolManager(): ToolManagerInterface;
2621
-
2622
- /**
2623
- * Create a workspace — a mutable, `path`-keyed working set of immutable
2624
- * {@link FileInterface}s with the in-memory edit surface (read / write / search / replace /
2625
- * move / remove), observable through its `EmitterInterface` (from `@orkestrel/emitter`).
2626
- *
2627
- * @param options - Optional initial {@link import('./types.js').WorkspaceEventMap} listeners (`on`) and the emitter's `error` handler (see {@link WorkspaceOptions})
2628
- * @returns A working {@link WorkspaceInterface}
2629
- *
2630
- * @example
2631
- * ```ts
2632
- * import { createWorkspace } from '@src/core'
2633
- *
2634
- * const workspace = createWorkspace({ on: { write: (file) => console.log(file.path) } })
2635
- * workspace.write('src/main.ts', 'const x = 1')
2636
- * workspace.file('src/main.ts')?.state // 'created'
2637
- * ```
2638
- */
2639
- export declare function createWorkspace(options?: WorkspaceOptions): WorkspaceInterface;
2640
-
2641
- /**
2642
- * Create a workspace registry — a {@link WorkspaceManagerInterface} holding
2643
- * {@link WorkspaceInterface}s keyed by their `id` (in insertion order) WITH an active pointer:
2644
- * the §9 store over the workspace layer plus the `active` / `switch` seam the context renders.
2645
- *
2646
- * @remarks
2647
- * Starts empty; `add(input?)` mints a {@link WorkspaceInterface} (its `id` from the input or a
2648
- * random UUID), flowing the manager's default `on` / `error` in unless the input overrides them,
2649
- * and stores it (an already-present `id` overwrites — last write wins) — and AUTO-ACTIVATES the
2650
- * FIRST one (a registry with workspaces always has one `active`); a later `add` leaves `active`
2651
- * unchanged. `switch(id)` re-points `active` (an unknown `id` returns `undefined`, leaving
2652
- * `active` unchanged — lenient, never throws); `workspace(id)` / `workspaces()` look up;
2653
- * `remove` (one or a batch, §9.2) reports whether any was removed AND clears `active` if it was
2654
- * the removed one; `clear` empties it and clears `active`. Event-free (each workspace owns its
2655
- * own observable `emitter`).
2656
- *
2657
- * With the optional `store` ({@link WorkspaceStoreInterface}, e.g. `createMemoryWorkspaceStore` /
2658
- * `createDatabaseWorkspaceStore`), `open(id)` HYDRATES a workspace on a registry miss (rebuilding it
2659
- * through the constructor `seed` from the snapshot's `files`, then activating it) and `save(id)`
2660
- * PERSISTS a registered workspace's `snapshot()`. Both are LENIENT without a store (open resolves
2661
- * only registered ids, save is a no-op `false`).
2662
- *
2663
- * @param options - Optional default `on` / `error` for created workspaces + the durable `store`
2664
- * (see {@link WorkspaceManagerOptions})
2665
- * @returns An empty {@link WorkspaceManagerInterface}
2666
- *
2667
- * @example
2668
- * ```ts
2669
- * import { createWorkspaceManager } from '@src/core'
2670
- *
2671
- * const workspaces = createWorkspaceManager()
2672
- * const scratch = workspaces.add() // auto-activates — workspaces.active === scratch
2673
- * scratch.write('notes.txt', 'hello')
2674
- * ```
2675
- */
2676
- export declare function createWorkspaceManager(options?: WorkspaceManagerOptions): WorkspaceManagerInterface;
2677
-
2678
2353
  /**
2679
2354
  * A {@link ConversationStoreInterface} backed by one table of the `databases` layer — a
2680
2355
  * conversation's durable state IS a row, so persistence reduces to keyed point-access (`get` / `set`
2681
2356
  * / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
2682
2357
  * {@link import('./MemoryConversationStore.js').MemoryConversationStore}. The EXACT twin of
2683
- * {@link import('../../workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore}.
2358
+ * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}.
2684
2359
  *
2685
2360
  * @remarks
2686
2361
  * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
@@ -2688,7 +2363,7 @@ export declare function createWorkspaceManager(options?: WorkspaceManagerOptions
2688
2363
  * IndexedDB backend swaps in WITHOUT touching the
2689
2364
  * {@link import('../ConversationManager.js').ConversationManager} or the
2690
2365
  * {@link import('../Conversation.js').Conversation} — the same seam as
2691
- * {@link import('../../workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore}. The
2366
+ * {@link import('@orkestrel/workspace').DatabaseWorkspaceStore}. The
2692
2367
  * driver defaults to memory ({@link import('../../factories.js').createDatabaseConversationStore}
2693
2368
  * passes `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the
2694
2369
  * durable plumbing by passing a JSON / SQLite / IndexedDB driver.
@@ -2741,96 +2416,6 @@ export declare class DatabaseConversationStore implements ConversationStoreInter
2741
2416
  delete(id: string): Promise<void>;
2742
2417
  }
2743
2418
 
2744
- /**
2745
- * A {@link WorkspaceStoreInterface} backed by one table of the `databases` layer — a workspace's
2746
- * durable state IS a row, so persistence reduces to keyed point-access (`get` / `set` / `delete`)
2747
- * over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
2748
- * {@link import('./MemoryWorkspaceStore.js').MemoryWorkspaceStore}.
2749
- *
2750
- * @remarks
2751
- * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
2752
- * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
2753
- * IndexedDB backend swaps in WITHOUT touching the
2754
- * {@link import('../WorkspaceManager.js').WorkspaceManager} or the
2755
- * {@link import('../Workspace.js').Workspace} — the same seam as
2756
- * the analogous `DatabaseWorkflowStore` in `@orkestrel/workflow`. The
2757
- * driver defaults to memory ({@link import('../../factories.js').createDatabaseWorkspaceStore}
2758
- * passes `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the
2759
- * durable plumbing by passing a JSON / SQLite / IndexedDB driver.
2760
- *
2761
- * The {@link WorkspaceSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of
2762
- * `{ id; snapshot }` ({@link WorkspaceSnapshotRow}), the snapshot the whole JSON blob (a `rawShape`
2763
- * column the factory builds) — exactly as `DatabaseWorkflowStore` stores its snapshot. The snapshot
2764
- * is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless AND
2765
- * keeps the row type flat (`snapshot` reads back as `unknown`).
2766
- *
2767
- * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes
2768
- * the row `{ id: snapshot.id, snapshot }`.
2769
- * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
2770
- * a {@link WorkspaceSnapshot} ({@link import('../../helpers.js').isWorkspaceSnapshot} — the AGENTS
2771
- * §14 boundary narrow for an untrusted storage read), or `undefined` if none is stored.
2772
- * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
2773
- *
2774
- * UNLIKE a session store there is NO idle-TTL / eviction — a persisted workspace lives until an
2775
- * explicit `delete`. The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the
2776
- * §22 method bijection with {@link WorkspaceStoreInterface}). Hydration stays a caller concern: a
2777
- * {@link import('../WorkspaceManager.js').WorkspaceManager} reads a snapshot back and rebuilds the
2778
- * live workspace through the constructor `seed` (its `open` / `save`).
2779
- *
2780
- * @example
2781
- * ```ts
2782
- * import { createDatabaseWorkspaceStore, createMemoryDriver, createWorkspace } from '@src/core'
2783
- *
2784
- * const store = createDatabaseWorkspaceStore(createMemoryDriver()) // a durable driver swaps in here
2785
- * const workspace = createWorkspace()
2786
- * workspace.write('notes.txt', 'hello')
2787
- * await store.set(workspace.snapshot()) // persist the workspace (one JSON column)
2788
- * const snapshot = await store.get(workspace.id)
2789
- * await store.delete(workspace.id) // drop it
2790
- * ```
2791
- */
2792
- export declare class DatabaseWorkspaceStore implements WorkspaceStoreInterface {
2793
- #private;
2794
- /**
2795
- * Wrap a table as a workspace store.
2796
- *
2797
- * @param table - The {@link TableInterface} holding the snapshots — its row is the
2798
- * {@link WorkspaceSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)
2799
- */
2800
- constructor(table: TableInterface<WorkspaceSnapshotRow>);
2801
- /** Resolve the persisted snapshot for `id`, narrowing the opaque JSON column back to a `WorkspaceSnapshot`. */
2802
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
2803
- /** Insert or replace under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
2804
- set(snapshot: WorkspaceSnapshot): Promise<void>;
2805
- /** Drop a snapshot by id; an absent id is a no-op (no throw). */
2806
- delete(id: string): Promise<void>;
2807
- }
2808
-
2809
- /**
2810
- * Compute the decoded byte length of a base64 string ARITHMETICALLY — the image-payload
2811
- * sizing primitive {@link computeSize} uses, with no `atob` / `Buffer` decode.
2812
- *
2813
- * @remarks
2814
- * A base64 string encodes each group of 3 input bytes as 4 characters, so a well-formed
2815
- * string's length is a multiple of 4 and its decoded length is `(length / 4) * 3` MINUS
2816
- * the trailing `=` padding (one `=` ⇒ the last group held 2 bytes, two `==` ⇒ 1 byte).
2817
- * The padding is counted from the trailing `=` characters, so both the `=` and `==` cases
2818
- * are handled. The empty string decodes to `0`. Total — never throws (a malformed,
2819
- * non-multiple-of-4 length still yields a defined non-negative estimate via `floor`).
2820
- *
2821
- * @param base64 - The base64-encoded string (e.g. an image's `data`)
2822
- * @returns The decoded payload's byte length
2823
- *
2824
- * @example
2825
- * ```ts
2826
- * decodedSize('') // 0
2827
- * decodedSize('AAAA') // 3 (no padding)
2828
- * decodedSize('AAA=') // 2 (one '=')
2829
- * decodedSize('AA==') // 1 (two '=')
2830
- * ```
2831
- */
2832
- export declare function decodedSize(base64: string): number;
2833
-
2834
2419
  /**
2835
2420
  * The default cap on an {@link AgentInterface} turn's tool iterations — the maximum
2836
2421
  * number of context → provider → tools cycles before the loop stops, so a model that
@@ -2858,28 +2443,6 @@ export declare const DEFAULT_AUTHORITY_ZONE = "default";
2858
2443
  */
2859
2444
  export declare const DEFAULT_CONVERSATION_KEEP = 0;
2860
2445
 
2861
- /**
2862
- * Escape a string's regex-special characters so it matches LITERALLY inside a `RegExp` — the
2863
- * primitive a {@link import('./workspaces/Workspace.js').Workspace} search builds a literal-text
2864
- * search pattern through (as opposed to a caller-supplied regex pattern).
2865
- *
2866
- * @remarks
2867
- * Prefixes every character in the class `. * + ? ^ $ { } ( ) | [ ] \` with a backslash, so the
2868
- * escaped string, when compiled into a `RegExp`, matches only its own literal characters — no
2869
- * character acts as a quantifier, anchor, group, or class. Pure string assembly, total — never
2870
- * throws.
2871
- *
2872
- * @param value - The text to escape for literal use inside a `RegExp` pattern
2873
- * @returns `value` with every regex-special character backslash-escaped
2874
- *
2875
- * @example
2876
- * ```ts
2877
- * escapeRegExp('a.b*c') // 'a\\.b\\*c'
2878
- * new RegExp(escapeRegExp('a.b*c')).test('a.b*c') // true
2879
- * ```
2880
- */
2881
- export declare function escapeRegExp(value: string): string;
2882
-
2883
2446
  /**
2884
2447
  * Estimate the context-token footprint of a batch of messages — the default `consume`
2885
2448
  * estimator for an agent's context `BudgetInterface` (a budgets surface's tracking contract)
@@ -2934,13 +2497,6 @@ export declare function estimateMessages(messages: readonly MessageInterface[]):
2934
2497
  */
2935
2498
  export declare function estimateTokens(text: string): number;
2936
2499
 
2937
- /**
2938
- * The extension→language table {@link import('./helpers.js').inferLanguage} reads to
2939
- * map a file path's extension to a fenced-code language tag. Covers the common source /
2940
- * markup / data extensions; an unlisted extension falls back to `'text'`.
2941
- */
2942
- export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2943
-
2944
2500
  /**
2945
2501
  * Render a path-addressed text body as a fenced reference block — the framing an
2946
2502
  * {@link import('./AgentContext.js').AgentContext}'s ACTIVE-workspace text-file render emits (the
@@ -2951,7 +2507,7 @@ export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2951
2507
  * fenced code block tagged with `language`, the `content` verbatim inside. Pure string assembly,
2952
2508
  * total — never throws. The one fenced-file format string for the whole module — `AgentContext.build()`
2953
2509
  * frames an active workspace's text files with it (each carries its own `language` on its
2954
- * {@link FileContent} text arm).
2510
+ * {@link import('@orkestrel/workspace').FileContent} text arm).
2955
2511
  *
2956
2512
  * @param path - The file path shown on the `File:` label line
2957
2513
  * @param language - The fenced-code language tag (e.g. `'typescript'`)
@@ -2968,74 +2524,6 @@ export declare const EXTENSION_TO_LANGUAGE: Readonly<Record<string, string>>;
2968
2524
  */
2969
2525
  export declare function fencedFile(path: string, language: string, content: string): string;
2970
2526
 
2971
- /**
2972
- * A {@link FileInterface}'s content — a TAGLESS union of a TEXT arm and a BINARY arm, narrowed
2973
- * by the {@link import('./helpers.js').isText} / {@link import('./helpers.js').isBinary}
2974
- * guards (AGENTS §14: an untyped arm is narrowed via a guard, never an `as`).
2975
- *
2976
- * @remarks
2977
- * - The TEXT arm carries the literal `text` plus the fenced-code `language` it renders as.
2978
- * - The BINARY arm carries the base64-encoded `data` plus the {@link BinaryMIME} that labels it.
2979
- *
2980
- * There is no `modality` discriminant — the arms are told apart structurally
2981
- * (`'text' in content` vs `'data' in content`) by the guards. A binary arm whose `mime`
2982
- * starts with `image/` IS an image ({@link import('./helpers.js').isImage}). A future
2983
- * non-image binary (a PDF) is the same binary arm with a new {@link BinaryMIME} — additive,
2984
- * with nothing above it changed.
2985
- */
2986
- export declare type FileContent = {
2987
- readonly text: string;
2988
- readonly language: string;
2989
- } | {
2990
- readonly data: string;
2991
- readonly mime: BinaryMIME;
2992
- };
2993
-
2994
- /**
2995
- * The minimal data to author a {@link FileInterface} — `size` / `lines` are DERIVED from the
2996
- * `content`, so a caller supplies only `path` / `content` (and an optional `state`, defaulting
2997
- * to `'created'`).
2998
- */
2999
- export declare interface FileInput {
3000
- readonly path: string;
3001
- readonly content: FileContent;
3002
- /** The lifecycle state; defaults to `'created'` when omitted. */
3003
- readonly state?: FileState;
3004
- }
3005
-
3006
- /**
3007
- * An immutable file value object — a `path`-addressed {@link FileContent} carrying a
3008
- * lifecycle {@link FileState}, with `size` / `lines` DERIVED once when built.
3009
- *
3010
- * @remarks
3011
- * A PLAIN frozen record (not a class instance) produced by
3012
- * {@link import('./factories.js').createFile} — the `path` IS its identity (there is no
3013
- * `id`). `size` and `lines` are computed from `content` when built (so they can never drift
3014
- * from it) and re-read for free thereafter. Never mutated — a Workspace REPLACES a file
3015
- * rather than editing it in place. A DATA-ONLY value object — no methods, plus it
3016
- * `structuredClone`s losslessly (every field is a plain primitive / record).
3017
- */
3018
- export declare interface FileInterface {
3019
- readonly path: string;
3020
- readonly content: FileContent;
3021
- readonly state: FileState;
3022
- /** Derived byte size: UTF-8 byte length (text) / decoded payload bytes (binary). */
3023
- readonly size: number;
3024
- /** Derived line count: the text line count; `0` for a binary arm. */
3025
- readonly lines: number;
3026
- }
3027
-
3028
- /**
3029
- * The lifecycle state of a {@link FileInterface} relative to its (future) durable backing
3030
- * — a multi-value lifecycle union (AGENTS §10), not a boolean.
3031
- *
3032
- * @remarks
3033
- * `'created'` is a brand-new file not yet persisted; `'modified'` has unsaved edits over a
3034
- * persisted original; `'loaded'` was read from the backing and is unchanged; `'deleted'`
3035
- * is tombstoned. A `File` authored without a `state` defaults to `'created'`.
3036
- */
3037
- export declare type FileState = 'created' | 'modified' | 'loaded' | 'deleted';
3038
-
3039
2527
  /**
3040
2528
  * Filter a list of items by a {@link import('./types.js').ScopeInterface} allow-list of
3041
2529
  * keys — the pure, total set-membership primitive the context's build step and the agent
@@ -3107,34 +2595,13 @@ export declare function handleAgentRunnerJob(registry: AgentRegistryInterface, a
3107
2595
  */
3108
2596
  export declare const IMAGE_TOKEN_ESTIMATE = 512;
3109
2597
 
3110
- /**
3111
- * Infer a fenced-code language tag from a file path's extension — what a workspace text file
3112
- * (or the {@link fencedFile} renderer) renders its content block as.
3113
- *
3114
- * @remarks
3115
- * Reads the extension after the last `.` (case-insensitive) and maps it through a
3116
- * fixed extension→language table. An unknown extension, or a path with no extension,
3117
- * falls back to `'text'` (a safe, language-agnostic fence). Total — never throws.
3118
- *
3119
- * @param path - The document's file path (e.g. `'src/main.ts'`)
3120
- * @returns The inferred language tag (e.g. `'typescript'`), or `'text'` when unknown
3121
- *
3122
- * @example
3123
- * ```ts
3124
- * inferLanguage('src/main.ts') // 'typescript'
3125
- * inferLanguage('README.md') // 'markdown'
3126
- * inferLanguage('LICENSE') // 'text'
3127
- * ```
3128
- */
3129
- export declare function inferLanguage(path: string): string;
3130
-
3131
2598
  /**
3132
2599
  * An immutable named directive — a {@link InstructionInterface} assembled once from its
3133
2600
  * input (`name` / `content`, an optional `priority` defaulting to `0`), the `id` minted
3134
2601
  * at construction.
3135
2602
  *
3136
2603
  * @remarks
3137
- * 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
3138
2605
  * constructor mints a fresh `id` (`crypto.randomUUID()`), copies the input's `name` /
3139
2606
  * `content`, resolves `priority` to the input's value or `0`, and carries the input's
3140
2607
  * per-item `format` override ONLY when supplied (assigned just when present, mirroring a
@@ -3374,32 +2841,10 @@ export declare function isAgentError(value: unknown): value is AgentError;
3374
2841
  export declare function isAgentJobError(value: unknown): value is AgentJobError;
3375
2842
 
3376
2843
  /**
3377
- * Whether a {@link FileContent} is the BINARY arm — the narrowing guard for the text-vs-binary
3378
- * union (AGENTS §14: narrow an untyped arm via a guard, never an `as`).
3379
- *
3380
- * @remarks
3381
- * Tests `'data' in content` structurally — there is no `modality` discriminant. A `true`
3382
- * narrows `content` to `{ data: string; mime: BinaryMIME }`, unlocking `.data` / `.mime`. An
3383
- * image is a binary whose `mime` starts with `image/` ({@link isImage}). Total — never throws.
2844
+ * Narrow an unknown caught value to a {@link ConversationError}.
3384
2845
  *
3385
- * @param content - The file content to test
3386
- * @returns `true` when `content` is the binary arm (carries base64 `data` + `mime`)
3387
- *
3388
- * @example
3389
- * ```ts
3390
- * if (isBinary(file.content)) file.content.data // the base64 payload + .mime
3391
- * ```
3392
- */
3393
- export declare function isBinary(content: FileContent): content is {
3394
- readonly data: string;
3395
- readonly mime: BinaryMIME;
3396
- };
3397
-
3398
- /**
3399
- * Narrow an unknown caught value to a {@link ConversationError}.
3400
- *
3401
- * @param value - The value to test (typically a `catch` binding)
3402
- * @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}
3403
2848
  *
3404
2849
  * @example
3405
2850
  * ```ts
@@ -3417,7 +2862,7 @@ export declare function isConversationError(value: unknown): value is Conversati
3417
2862
  * UNTRUSTED snapshot read (a storage row a
3418
2863
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
3419
2864
  * reads back from its opaque JSON column, a snapshot loaded from disk). The EXACT analogue of
3420
- * {@link isWorkspaceSnapshot}.
2865
+ * {@link import('@orkestrel/workspace').isWorkspaceSnapshot}.
3421
2866
  *
3422
2867
  * @remarks
3423
2868
  * A total guard (it NEVER throws — adversarial input returns `false`). It checks the snapshot's
@@ -3426,7 +2871,7 @@ export declare function isConversationError(value: unknown): value is Conversati
3426
2871
  * {@link SectionInterface} ({@link isSection}), and a `messages` array EVERY element of which is a
3427
2872
  * valid {@link MessageInterface} ({@link isMessage}) — enough to safely impose the
3428
2873
  * {@link ConversationSnapshot} type at a storage boundary WITHOUT a cast. The structural twin of
3429
- * {@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
3430
2875
  * non-string `summary` when present, a non-array `sections` / `messages`, or any malformed
3431
2876
  * element) resolves `false`, so a
3432
2877
  * {@link import('./conversations/stores/DatabaseConversationStore.js').DatabaseConversationStore}
@@ -3445,68 +2890,23 @@ export declare function isConversationError(value: unknown): value is Conversati
3445
2890
  */
3446
2891
  export declare function isConversationSnapshot(value: unknown): value is ConversationSnapshot;
3447
2892
 
3448
- /**
3449
- * Whether an `unknown` is structurally a {@link FileInterface} record — the per-file step of the
3450
- * {@link isWorkspaceSnapshot} read-boundary narrow (AGENTS §14: narrow an untrusted storage read
3451
- * via a guard, never an `as`).
3452
- *
3453
- * @remarks
3454
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the file's SHAPE:
3455
- * a record with a `string` `path`, a `string` `state`, `number` `size` / `lines`, and a `content`
3456
- * that is EITHER the TEXT arm (`{ text: string; language: string }`) OR the BINARY arm
3457
- * (`{ data: string; mime: string }`) — the tagless {@link FileContent} union, told apart
3458
- * structurally exactly as {@link isText} / {@link isBinary} do (no `modality` discriminant). Enough
3459
- * to safely impose the {@link FileInterface} type at a storage boundary WITHOUT a cast; the `mime`
3460
- * is left as a broad `string` here (an open {@link BinaryMIME}, so any storage-read MIME string is
3461
- * accepted defensively rather than rejected against the current literal set).
3462
- *
3463
- * @param value - The value to test (one element of a snapshot's opaque `files` array)
3464
- * @returns `true` when `value` has the structural shape of a {@link FileInterface}
3465
- *
3466
- * @example
3467
- * ```ts
3468
- * isFile({ path: 'a.ts', content: { text: 'x', language: 'typescript' }, state: 'created', size: 1, lines: 1 }) // true
3469
- * isFile({ path: 'a.png', content: { data: 'AAAA', mime: 'image/png' }, state: 'created', size: 3, lines: 0 }) // true
3470
- * isFile({ path: 'a.ts' }) // false (missing content / state / size / lines)
3471
- * ```
3472
- */
3473
- export declare function isFile(value: unknown): value is FileInterface;
3474
-
3475
- /**
3476
- * Whether a {@link FileContent} is an IMAGE — a {@link isBinary} arm whose `mime` is an
3477
- * `image/*` type (an image is just a binary with an image MIME).
3478
- *
3479
- * @remarks
3480
- * Narrows to the binary arm first, then checks the `image/` MIME prefix — so a future
3481
- * non-image binary (a PDF, `'application/pdf'`) is binary but NOT an image. Total — never
3482
- * throws.
3483
- *
3484
- * @param content - The file content to test
3485
- * @returns `true` when `content` is a binary arm with an `image/*` MIME
3486
- *
3487
- * @example
3488
- * ```ts
3489
- * isImage({ data: '<base64>', mime: 'image/png' }) // true
3490
- * isImage({ text: 'hi', language: 'text' }) // false
3491
- * ```
3492
- */
3493
- export declare function isImage(content: FileContent): boolean;
3494
-
3495
2893
  /**
3496
2894
  * Whether an `unknown` is structurally a {@link MessageInterface} record — the per-message step of
3497
2895
  * the {@link isConversationSnapshot} read-boundary narrow (AGENTS §14: narrow an untrusted storage
3498
- * 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}.
3499
2898
  *
3500
2899
  * @remarks
3501
2900
  * A total guard (it NEVER throws — adversarial input returns `false`). It checks the message's
3502
2901
  * SHAPE: a record with a `string` `id`, a `string` `role`, a `string` `content`, and — WHEN present
3503
- * — 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
3504
2904
  * ASI06 fail-closed deepening: a tampered `calls` element rejects the message, so the snapshot
3505
2905
  * reads back as absent rather than replaying a malformed call) and an `images` that is an array
3506
2906
  * (an absent optional passes). The `role` is left as a broad `string` here (an open
3507
2907
  * {@link import('./types.js').MessageRole}, so any storage-read role string is accepted
3508
- * defensively rather than rejected against the current literal set) — exactly as {@link isFile}
3509
- * 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
3510
2910
  * a storage boundary WITHOUT a cast.
3511
2911
  *
3512
2912
  * @param value - The value to test (one element of a snapshot's `messages` / a section's `messages`)
@@ -3562,132 +2962,16 @@ export declare function isProviderAbortError(value: unknown): value is ProviderA
3562
2962
  */
3563
2963
  export declare function isSection(value: unknown): value is SectionInterface;
3564
2964
 
3565
- /**
3566
- * Whether a {@link FileContent} is the TEXT arm — the narrowing guard for the text-vs-binary
3567
- * union (AGENTS §14: narrow an untyped arm via a guard, never an `as`).
3568
- *
3569
- * @remarks
3570
- * Tests `'text' in content` structurally — there is no `modality` discriminant. A `true`
3571
- * narrows `content` to `{ text: string; language: string }`, unlocking `.text` / `.language`.
3572
- * Total — never throws.
3573
- *
3574
- * @param content - The file content to test
3575
- * @returns `true` when `content` is the text arm (carries `text` + `language`)
3576
- *
3577
- * @example
3578
- * ```ts
3579
- * if (isText(file.content)) file.content.text // the literal text + .language
3580
- * ```
3581
- */
3582
- export declare function isText(content: FileContent): content is {
3583
- readonly text: string;
3584
- readonly language: string;
3585
- };
3586
-
3587
- /**
3588
- * Whether an `unknown` is structurally a {@link ToolCall} record — the per-call step of the
3589
- * {@link isMessage} read-boundary narrow (AGENTS §14: narrow an untrusted storage read via a
3590
- * guard, never an `as`).
3591
- *
3592
- * @remarks
3593
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the call's
3594
- * SHAPE: a record with a `string` `id`, a `string` `name`, and a record `arguments` — the real
3595
- * {@link ToolCall} shape a restored assistant turn replays to a provider. This is the ASI06
3596
- * fail-CLOSED element check: a tampered persisted call (`null`, a bare string, a non-record
3597
- * `arguments`) fails the guard, so {@link isMessage} — and through it
3598
- * {@link isConversationSnapshot} — rejects the whole snapshot and a poisoned store row reads
3599
- * back as ABSENT instead of replaying a malformed call into a chat template.
3600
- *
3601
- * @param value - The value to test (one element of an assistant message's `calls`)
3602
- * @returns `true` when `value` has the structural shape of a {@link ToolCall}
3603
- *
3604
- * @example
3605
- * ```ts
3606
- * isToolCall({ id: 'c1', name: 'search', arguments: { q: 'acme' } }) // true
3607
- * isToolCall({ id: 'c1', name: 'search' }) // false (missing arguments)
3608
- * isToolCall({ id: 'c1', name: 123, arguments: {} }) // false (non-string name)
3609
- * ```
3610
- */
3611
- export declare function isToolCall(value: unknown): value is ToolCall;
3612
-
3613
- /**
3614
- * Whether a {@link Range} is STRUCTURALLY valid — the predicate a ranged `write` checks
3615
- * before applying (a `false` here is the `RANGE` throw).
3616
- *
3617
- * @remarks
3618
- * Structural validity is independent of any content: every component must be `>= 1`
3619
- * (1-based lines and columns), and `start` must not come after `end` (an inverted range
3620
- * is invalid). A range that is structurally valid but reaches PAST the end of a specific
3621
- * text is still valid — it is {@link clampRange}d to the bounds when applied, not rejected.
3622
- * Total — never throws.
3623
- *
3624
- * @param range - The range to validate
3625
- * @returns `true` when every component is `>= 1` and `start` is at or before `end`
3626
- *
3627
- * @example
3628
- * ```ts
3629
- * isValidRange({ start: { line: 1, column: 1 }, end: { line: 2, column: 1 } }) // true
3630
- * isValidRange({ start: { line: 2, column: 1 }, end: { line: 1, column: 1 } }) // false (inverted)
3631
- * isValidRange({ start: { line: 0, column: 1 }, end: { line: 1, column: 1 } }) // false (sub-1 line)
3632
- * ```
3633
- */
3634
- export declare function isValidRange(range: Range): boolean;
3635
-
3636
- /**
3637
- * Narrow an unknown caught value to a {@link WorkspaceError}.
3638
- *
3639
- * @param value - The value to test (typically a `catch` binding)
3640
- * @returns `true` when `value` is a {@link WorkspaceError}
3641
- *
3642
- * @example
3643
- * ```ts
3644
- * try {
3645
- * workspace.prepend('icon.png', '// header')
3646
- * } catch (error) {
3647
- * if (isWorkspaceError(error) && error.code === 'MODALITY') skipBinary()
3648
- * }
3649
- * ```
3650
- */
3651
- export declare function isWorkspaceError(value: unknown): value is WorkspaceError;
3652
-
3653
- /**
3654
- * Narrow an `unknown` to a {@link WorkspaceSnapshot} — the AGENTS §14 boundary guard for an
3655
- * UNTRUSTED snapshot read (a storage row a
3656
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} reads back
3657
- * from its opaque JSON column, a snapshot loaded from disk).
3658
- *
3659
- * @remarks
3660
- * A total guard (it NEVER throws — adversarial input returns `false`). It checks the snapshot's
3661
- * SHAPE: a `string` `id` and a `files` array EVERY element of which is a valid {@link FileInterface}
3662
- * record ({@link isFile}) — enough to safely impose the {@link WorkspaceSnapshot} type at a storage
3663
- * boundary WITHOUT a cast. The structural twin of
3664
- * the analogous `isWorkflowSnapshot` in `@orkestrel/workflow`. A malformed blob (a non-record, a
3665
- * missing / non-string `id`, a non-array `files`, or any malformed file element) resolves `false`,
3666
- * so a {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} read
3667
- * yields `undefined` rather than a broken workspace.
3668
- *
3669
- * @param value - The value to test (an opaque storage read)
3670
- * @returns `true` when `value` has the structural shape of a {@link WorkspaceSnapshot}
3671
- *
3672
- * @example
3673
- * ```ts
3674
- * isWorkspaceSnapshot({ id: 'w1', files: [] }) // true
3675
- * isWorkspaceSnapshot({ id: 'w1', files: 'nope' }) // false
3676
- * isWorkspaceSnapshot({ files: [] }) // false (missing id)
3677
- * ```
3678
- */
3679
- export declare function isWorkspaceSnapshot(value: unknown): value is WorkspaceSnapshot;
3680
-
3681
2965
  /**
3682
2966
  * The in-memory {@link ConversationStoreInterface} — a process-lifetime `Map` of
3683
2967
  * {@link ConversationSnapshot}s keyed by conversation id, the DEFAULT store
3684
2968
  * {@link import('../../factories.js').createMemoryConversationStore} builds. The EXACT twin of
3685
- * {@link import('../../workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore}.
2969
+ * {@link import('@orkestrel/workspace').MemoryWorkspaceStore}.
3686
2970
  *
3687
2971
  * @remarks
3688
2972
  * A plain `Map<string, ConversationSnapshot>` (AGENTS §21 — the snapshot is already pure,
3689
2973
  * self-contained JSON, so no encoding is needed for the memory tier). Like the
3690
- * {@link import('../../workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore} it twins,
2974
+ * {@link import('@orkestrel/workspace').MemoryWorkspaceStore} it twins,
3691
2975
  * there is NO idle-TTL and NO eviction: a persisted conversation lives until an explicit `delete`. A
3692
2976
  * durable backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching
3693
2977
  * the {@link import('../ConversationManager.js').ConversationManager} or the
@@ -3723,50 +3007,6 @@ export declare class MemoryConversationStore implements ConversationStoreInterfa
3723
3007
  delete(id: string): Promise<void>;
3724
3008
  }
3725
3009
 
3726
- /**
3727
- * The in-memory {@link WorkspaceStoreInterface} — a process-lifetime `Map` of
3728
- * {@link WorkspaceSnapshot}s keyed by workspace id, the DEFAULT store
3729
- * {@link import('../../factories.js').createMemoryWorkspaceStore} builds.
3730
- *
3731
- * @remarks
3732
- * A plain `Map<string, WorkspaceSnapshot>` (AGENTS §21 — the snapshot is already pure,
3733
- * self-contained JSON, so no encoding is needed for the memory tier). Like the
3734
- * the analogous `MemoryWorkflowStore` in `@orkestrel/workflow` it twins,
3735
- * there is NO idle-TTL and NO eviction: a persisted workspace lives until an explicit `delete`. A
3736
- * durable backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching
3737
- * the {@link import('../WorkspaceManager.js').WorkspaceManager} or the
3738
- * {@link import('../Workspace.js').Workspace} — its driver-pluggable twin is
3739
- * {@link import('./DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} (the snapshot as one opaque
3740
- * JSON column).
3741
- *
3742
- * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.
3743
- * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).
3744
- * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).
3745
- *
3746
- * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method
3747
- * bijection with {@link WorkspaceStoreInterface}). Hydration is a caller concern: a
3748
- * {@link import('../WorkspaceManager.js').WorkspaceManager} reads a snapshot back and rebuilds the
3749
- * live workspace through the constructor `seed` (its `open` / `save`).
3750
- *
3751
- * @example
3752
- * ```ts
3753
- * import { createMemoryWorkspaceStore, createWorkspace } from '@src/core'
3754
- *
3755
- * const store = createMemoryWorkspaceStore()
3756
- * const workspace = createWorkspace()
3757
- * workspace.write('notes.txt', 'hello')
3758
- * await store.set(workspace.snapshot()) // persist the workspace
3759
- * const snapshot = await store.get(workspace.id)
3760
- * await store.delete(workspace.id) // drop it
3761
- * ```
3762
- */
3763
- export declare class MemoryWorkspaceStore implements WorkspaceStoreInterface {
3764
- #private;
3765
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
3766
- set(snapshot: WorkspaceSnapshot): Promise<void>;
3767
- delete(id: string): Promise<void>;
3768
- }
3769
-
3770
3010
  /**
3771
3011
  * The estimated per-message role/framing overhead {@link import('./helpers.js').estimateMessages}
3772
3012
  * adds on top of a message's content estimate — accounts for the fixed wire framing every
@@ -3844,42 +3084,6 @@ export declare interface MessageManagerInterface {
3844
3084
  /** The role a {@link MessageInterface} plays in a conversation turn. */
3845
3085
  export declare type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
3846
3086
 
3847
- /**
3848
- * Convert a 1-based {@link Position} to a 0-based string offset into `text` — the indexing
3849
- * primitive {@link sliceRange} / {@link spliceRange} use, clamped to `text.length`.
3850
- *
3851
- * @remarks
3852
- * Sums each preceding line's length plus its `\n` separator, then adds the `column - 1`
3853
- * within the target line, capped at `text.length`. Total — never throws (an out-of-bounds
3854
- * position yields a defined in-range offset).
3855
- *
3856
- * @param text - The text to index into
3857
- * @param position - The 1-based position to resolve
3858
- * @returns The 0-based offset (in `[0, text.length]`)
3859
- *
3860
- * @example
3861
- * ```ts
3862
- * offsetAt('ab\ncd', { line: 2, column: 1 }) // 3 (just after 'ab\n')
3863
- * ```
3864
- */
3865
- export declare function offsetAt(text: string, position: Position): number;
3866
-
3867
- /**
3868
- * A 1-based caret position inside a text file — `line` and `column` both count from `1`
3869
- * (column `1` is the first character of the line), matching an editor's gutter.
3870
- *
3871
- * @remarks
3872
- * The {@link import('./workspaces/Workspace.js').Workspace} edit surface speaks 1-based
3873
- * positions end to end ({@link Range}, {@link ReadResult}, {@link SearchMatch}). A
3874
- * structurally invalid component (`line < 1` or `column < 1`) is rejected by a range op
3875
- * (`RANGE`); an in-bounds-but-past-the-end one is CLAMPED to the nearest valid caret rather
3876
- * than rejected.
3877
- */
3878
- export declare interface Position {
3879
- readonly line: number;
3880
- readonly column: number;
3881
- }
3882
-
3883
3087
  /**
3884
3088
  * Thrown by a {@link ProviderInterface}'s `stream` when its bound signal aborts
3885
3089
  * mid-flight — carries the {@link ProviderResult} assembled from whatever streamed
@@ -4022,87 +3226,6 @@ export declare interface ProviderStreamOptions {
4022
3226
  readonly schema?: Readonly<Record<string, unknown>>;
4023
3227
  }
4024
3228
 
4025
- /**
4026
- * A half-open span of text between two {@link Position}s — `start` INCLUSIVE, `end`
4027
- * EXCLUSIVE (the character at `end` is not part of the span).
4028
- *
4029
- * @remarks
4030
- * A range whose `start` is at or before its `end` is valid; an inverted range
4031
- * (`start` after `end`) or one with a sub-1 component is structurally invalid and a range
4032
- * op throws `RANGE`. Each in-bounds component is clamped to the text before it is applied,
4033
- * so a range reaching past the end of the content reads / splices up to the end.
4034
- */
4035
- export declare interface Range {
4036
- readonly start: Position;
4037
- readonly end: Position;
4038
- }
4039
-
4040
- /**
4041
- * Assemble a 1-based nested {@link Range} from four FLAT caret integers — the bridge a
4042
- * workspace-editing tool (see `@orkestrel/tool`) uses from its small-model FLAT surface back to the
4043
- * nested {@link Range} the {@link import('./workspaces/Workspace.js').Workspace} edit surface
4044
- * speaks. Published as a public helper for that consumer.
4045
- *
4046
- * @remarks
4047
- * Pairs `(fromLine, fromColumn)` into `start` and `(toLine, toColumn)` into `end` verbatim — a pure
4048
- * structural lift, no validation (a structurally invalid range is rejected downstream by the ranged
4049
- * `write` it feeds, via {@link isValidRange}). Total — never throws. Zero-Node.
4050
- *
4051
- * @param fromLine - The 1-based start line
4052
- * @param fromColumn - The 1-based start column
4053
- * @param toLine - The 1-based end line
4054
- * @param toColumn - The 1-based end column
4055
- * @returns The `{ start, end }` {@link Range}
4056
- *
4057
- * @example
4058
- * ```ts
4059
- * rangeOf(1, 11, 1, 12) // { start: { line: 1, column: 11 }, end: { line: 1, column: 12 } }
4060
- * ```
4061
- */
4062
- export declare function rangeOf(fromLine: number, fromColumn: number, toLine: number, toColumn: number): Range;
4063
-
4064
- /**
4065
- * The outcome of a ranged {@link WorkspaceInterface.read} — the sliced `content` plus the
4066
- * `range` actually applied after clamping (so a caller learns the real span it got).
4067
- *
4068
- * @remarks
4069
- * `range` is the input {@link Range} CLAMPED to the file's bounds — when the requested
4070
- * span reached past the end of the text, `range` reflects the trimmed span that was read,
4071
- * never the original out-of-bounds request.
4072
- */
4073
- export declare interface ReadResult {
4074
- readonly content: string;
4075
- /** The actual (clamped) range applied to produce `content`. */
4076
- readonly range: Range;
4077
- }
4078
-
4079
- /**
4080
- * Options for {@link WorkspaceInterface.replace} — how the `query` matches (the same axes
4081
- * as {@link SearchOptions}).
4082
- *
4083
- * @param options - The replace options
4084
- *
4085
- * @remarks
4086
- * - `regex` — treat `query` as a regular expression source rather than a literal substring; defaults to `false`.
4087
- * - `exact` — match case-sensitively; defaults to `true`.
4088
- * - `limit` — stop after this many replacements across all files; defaults to unlimited.
4089
- */
4090
- export declare interface ReplaceOptions {
4091
- readonly regex?: boolean;
4092
- readonly exact?: boolean;
4093
- readonly limit?: number;
4094
- }
4095
-
4096
- /**
4097
- * The tally returned by {@link WorkspaceInterface.replace} — the `query` that ran, how
4098
- * many occurrences were `replaced`, and the number of `files` changed.
4099
- */
4100
- export declare interface ReplaceResult {
4101
- readonly query: string;
4102
- readonly replaced: number;
4103
- readonly files: number;
4104
- }
4105
-
4106
3229
  /**
4107
3230
  * The immutable per-run outcome value an {@link AgentInterface}'s loop replaces as it runs — the
4108
3231
  * assembled value its `stream`'s `result` promise resolves into a settled
@@ -4219,7 +3342,7 @@ export declare interface ScopeConfiguration {
4219
3342
  /**
4220
3343
  * Allowed ACTIVE-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
4221
3344
  * the filter {@link AgentContextInterface.build} applies to the active workspace's
4222
- * {@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 →
4223
3346
  * the last user message).
4224
3347
  */
4225
3348
  readonly files?: readonly string[];
@@ -4234,7 +3357,7 @@ export declare interface ScopeInput extends ScopeConfiguration {
4234
3357
  }
4235
3358
 
4236
3359
  /**
4237
- * 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
4238
3361
  * allow-lists ({@link ScopeConfiguration}) plus an `id` / `name`, and a `narrow` that
4239
3362
  * composes a tighter child by set-INTERSECTION.
4240
3363
  *
@@ -4322,7 +3445,7 @@ export declare type ScopeManagerEventMap = {
4322
3445
  * mints + stores one (never overwrites), `scopes()` lists them in insertion order.
4323
3446
  *
4324
3447
  * @remarks
4325
- * - **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
4326
3449
  * stores it; `count` is how many are stored. `scope(id)` looks one up; `scopes()` lists
4327
3450
  * them in insertion order. (Keyed by minted `id`, not `name`, so two scopes may share a
4328
3451
  * `name` and `create` always adds.)
@@ -4352,40 +3475,6 @@ export declare interface ScopeManagerOptions {
4352
3475
  readonly error?: EmitterErrorHandler;
4353
3476
  }
4354
3477
 
4355
- /**
4356
- * One hit from {@link WorkspaceInterface.search} — where the match was found and the line
4357
- * that carried it.
4358
- *
4359
- * @remarks
4360
- * `line` / `column` are 1-based (column `1` is the first character of the line); `length`
4361
- * is the matched substring's length; `content` is the FULL line the match sits on (not
4362
- * just the matched fragment), so a caller can render the hit in context.
4363
- */
4364
- export declare interface SearchMatch {
4365
- readonly path: string;
4366
- readonly line: number;
4367
- readonly column: number;
4368
- readonly length: number;
4369
- /** The full text of the line the match was found on. */
4370
- readonly content: string;
4371
- }
4372
-
4373
- /**
4374
- * Options for {@link WorkspaceInterface.search} — how the `query` matches.
4375
- *
4376
- * @param options - The search options
4377
- *
4378
- * @remarks
4379
- * - `regex` — treat `query` as a regular expression source rather than a literal substring; defaults to `false`.
4380
- * - `exact` — match case-sensitively; defaults to `true` (set `false` for a case-insensitive scan).
4381
- * - `limit` — stop after this many matches across all files; defaults to unlimited.
4382
- */
4383
- export declare interface SearchOptions {
4384
- readonly regex?: boolean;
4385
- readonly exact?: boolean;
4386
- readonly limit?: number;
4387
- }
4388
-
4389
3478
  /**
4390
3479
  * A slice of folded messages digested into a summary — the unit of compaction a
4391
3480
  * {@link ConversationInterface} produces when it `compact`s its live tail.
@@ -4428,48 +3517,6 @@ export declare interface SectionInterface {
4428
3517
  */
4429
3518
  export declare function settleAgentJob(agent: AgentInterface, allowPartial: boolean): Promise<AgentResult>;
4430
3519
 
4431
- /**
4432
- * Slice the substring of `text` spanned by a {@link Range} (start INCLUSIVE, end
4433
- * EXCLUSIVE), clamping the range to the text's bounds first — the read half of the ranged
4434
- * edit surface.
4435
- *
4436
- * @remarks
4437
- * Clamps via {@link clampRange}, resolves each end to an offset via {@link offsetAt}, and
4438
- * returns `text.slice(startOffset, endOffset)`. Total — never throws.
4439
- *
4440
- * @param text - The text to slice
4441
- * @param range - The 1-based range to extract
4442
- * @returns The spanned substring (empty when the clamped span is empty)
4443
- *
4444
- * @example
4445
- * ```ts
4446
- * sliceRange('hello\nworld', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }) // 'hello'
4447
- * ```
4448
- */
4449
- export declare function sliceRange(text: string, range: Range): string;
4450
-
4451
- /**
4452
- * Replace the span of `text` covered by a {@link Range} with `replacement` (start
4453
- * INCLUSIVE, end EXCLUSIVE), clamping the range to the text's bounds first — the write
4454
- * half of the ranged edit surface.
4455
- *
4456
- * @remarks
4457
- * Clamps via {@link clampRange}, then stitches `before + replacement + after` around the
4458
- * resolved offsets. An empty span (`start === end`) becomes a pure insertion. Total —
4459
- * never throws.
4460
- *
4461
- * @param text - The original text
4462
- * @param range - The 1-based range to overwrite
4463
- * @param replacement - The text to splice in place of the spanned range
4464
- * @returns The text with the spanned range replaced by `replacement`
4465
- *
4466
- * @example
4467
- * ```ts
4468
- * spliceRange('hello', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }, 'bye') // 'bye'
4469
- * ```
4470
- */
4471
- export declare function spliceRange(text: string, range: Range, replacement: string): string;
4472
-
4473
3520
  /**
4474
3521
  * A live event stream paired with the eventual settled result and a cancel — the
4475
3522
  * generic pull/streaming handle a long-running operation hands back.
@@ -4599,299 +3646,6 @@ export declare interface ThinkSplitterInterface {
4599
3646
  flush(): string;
4600
3647
  }
4601
3648
 
4602
- /**
4603
- * A registered tool — its {@link import('../types.js').ToolDefinition} schema (the
4604
- * `name` / `description` / `parameters` the model sees) bound to the `execute`
4605
- * handler that runs a call.
4606
- *
4607
- * @remarks
4608
- * A thin value object: the constructor stores the schema fields and the handler, and
4609
- * `execute` delegates to the handler verbatim. `parameters` is kept by reference (the
4610
- * open JSON-Schema the provider forwards as-is) — not cloned. The handler's `args` is
4611
- * the model-supplied `unknown` arguments record, narrowed inside the handler (§14);
4612
- * isolating a throw is the {@link ToolManager}'s job, not this class's. Event-free —
4613
- * no Emitter, no events.
4614
- *
4615
- * @example
4616
- * ```ts
4617
- * const tool = new Tool({
4618
- * name: 'add',
4619
- * description: 'Add two numbers',
4620
- * parameters: { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } } },
4621
- * execute: (args) => Number(args.a) + Number(args.b),
4622
- * })
4623
- * ```
4624
- */
4625
- export declare class Tool implements ToolInterface {
4626
- #private;
4627
- readonly name: string;
4628
- readonly description?: string;
4629
- readonly summary?: string;
4630
- readonly parameters?: Readonly<Record<string, unknown>>;
4631
- constructor(options: ToolOptions);
4632
- execute(args: Readonly<Record<string, unknown>>): Promise<unknown> | unknown;
4633
- }
4634
-
4635
- /**
4636
- * A tool call the model emitted — the tool `name` and its parsed `arguments`.
4637
- *
4638
- * @remarks
4639
- * `id` correlates the call with its later {@link ToolResult}; when the wire omits an
4640
- * id the provider mints one (a random UUID) so every call is addressable. `arguments`
4641
- * is always a record — the provider narrows the wire value (an object, or a JSON
4642
- * string it parses) to one, defaulting to `{}` when neither.
4643
- */
4644
- export declare interface ToolCall {
4645
- readonly id: string;
4646
- readonly name: string;
4647
- readonly arguments: Readonly<Record<string, unknown>>;
4648
- }
4649
-
4650
- /**
4651
- * The MCP `CallToolResult` shape a {@link ToolResult} maps to via {@link buildToolResult} —
4652
- * `content` blocks plus an `isError` flag set ONLY on failure (never `false`).
4653
- */
4654
- export declare interface ToolCallResult {
4655
- readonly content: readonly ToolResultContent[];
4656
- readonly isError?: true;
4657
- }
4658
-
4659
- /**
4660
- * A tool the model may call — its name, an optional description, and an optional
4661
- * JSON-Schema `parameters` object describing its arguments.
4662
- *
4663
- * @remarks
4664
- * Passed to a {@link ProviderInterface}'s `generate` / `stream`; the provider maps
4665
- * each into the wire's function-tool shape. `parameters` is an open JSON-Schema
4666
- * record (the provider forwards it verbatim).
4667
- */
4668
- export declare interface ToolDefinition {
4669
- readonly name: string;
4670
- readonly description?: string;
4671
- readonly parameters?: Readonly<Record<string, unknown>>;
4672
- }
4673
-
4674
- /**
4675
- * A registered tool: its {@link ToolDefinition} (the schema the model sees) plus the
4676
- * handler that runs a call.
4677
- *
4678
- * @remarks
4679
- * The definition fields (`name` / `description` / `parameters`) are what a
4680
- * {@link ProviderInterface} advertises to the model; `execute` is the local handler a
4681
- * {@link ToolManagerInterface} invokes when the model calls the tool. The handler's
4682
- * `args` is the model-supplied `unknown` JSON object — narrow it inside the handler
4683
- * (§14), never assert; the manager isolates a throw into a {@link ToolResult} `error`.
4684
- */
4685
- export declare interface ToolInterface extends ToolDefinition {
4686
- /**
4687
- * A one-or-two-sentence concise description advertised to the model IN PLACE of the
4688
- * full `description` (which stays on the tool for on-demand retrieval, e.g. a
4689
- * describe tool); absent ⇒ the full `description` is advertised exactly as today.
4690
- */
4691
- readonly summary?: string;
4692
- /**
4693
- * Run the tool. `args` is the model-supplied `unknown` JSON object — narrow it
4694
- * inside (§14); the manager isolates a throw into a {@link ToolResult} `error`.
4695
- *
4696
- * @param args - The model-supplied arguments record (an open `unknown` object)
4697
- * @returns The tool's result (sync or async) — folded into a `ToolResult.value`
4698
- */
4699
- execute(args: Readonly<Record<string, unknown>>): Promise<unknown> | unknown;
4700
- }
4701
-
4702
- /**
4703
- * The tool registry the agent loop dispatches model tool-calls through — resolves
4704
- * names, lists {@link ToolDefinition}s for the provider, and executes calls with
4705
- * per-call error isolation.
4706
- *
4707
- * @remarks
4708
- * - **Registry.** Tools live in an insertion-ordered `Map` keyed by `tool.name`;
4709
- * `add` takes one or a batch (§9.2) and a re-`add` of the same name OVERWRITES it
4710
- * (last write wins). `count` is the map size, `tool(name)` looks one up, `tools()`
4711
- * lists them in insertion order, and `definitions()` maps each to a plain
4712
- * {@link ToolDefinition} (`name` / `description?` / `parameters?`, the `execute`
4713
- * handler stripped) — exactly what a provider advertises to the model, with
4714
- * `tool.summary ?? tool.description` advertised as `description` (a lean `summary`
4715
- * stands in for the full text when set).
4716
- * - **Per-call error isolation (the load-bearing part).** `execute` resolves a
4717
- * {@link ToolCall}'s tool by name and ALWAYS resolves a {@link ToolResult}: an
4718
- * unknown name → `{ id, name, error: 'tool not found: <name>' }`; a successful run →
4719
- * `{ id, name, value }`; a handler throw is CAUGHT into `{ id, name, error }` (an
4720
- * `Error`'s message, else the stringified throw). A tool throw never escapes — it
4721
- * becomes a result the model can react to.
4722
- * - **Batch never fails as a whole.** `execute(calls)` runs every call via
4723
- * `Promise.all(calls.map(...))` and resolves the results correlated by `id` in the
4724
- * input order — a mix of success, throw, and not-found all resolve; one bad call
4725
- * does not reject the batch.
4726
- * - **Event-free.** A purely functional registry — no Emitter, no events.
4727
- *
4728
- * @example
4729
- * ```ts
4730
- * const manager = new ToolManager()
4731
- * manager.add(new Tool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
4732
- * const result = await manager.execute({ id: '1', name: 'add', arguments: { x: 1, y: 2 } })
4733
- * result.value // 3
4734
- * ```
4735
- */
4736
- export declare class ToolManager implements ToolManagerInterface {
4737
- #private;
4738
- get count(): number;
4739
- add(tool: ToolInterface): void;
4740
- add(tools: readonly ToolInterface[]): void;
4741
- tool(name: string): ToolInterface | undefined;
4742
- tools(): readonly ToolInterface[];
4743
- definitions(): readonly ToolDefinition[];
4744
- execute(call: ToolCall): Promise<ToolResult>;
4745
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
4746
- remove(name: string): boolean;
4747
- remove(names: readonly string[]): boolean;
4748
- clear(): void;
4749
- }
4750
-
4751
- /**
4752
- * A registry of tools — resolves names, lists definitions for the provider, and
4753
- * executes calls with per-call error isolation.
4754
- *
4755
- * @remarks
4756
- * - **Registry.** `add` registers one tool or a batch (§9.2), keyed by `tool.name`
4757
- * (a re-`add` of the same name overwrites — last write wins); `count` is the number
4758
- * registered. `tool(name)` looks one up; `tools()` lists them in insertion order;
4759
- * `definitions()` strips each to a plain {@link ToolDefinition} for the provider,
4760
- * advertising `tool.summary ?? tool.description` in the definition's `description`
4761
- * field — a lean `summary` stands in for the full `description` when set.
4762
- * - **Per-call error isolation.** `execute` resolves a {@link ToolCall}'s tool by name
4763
- * and runs it, ALWAYS resolving a {@link ToolResult}: a success carries `value`, a
4764
- * handler throw is caught into `error`, and an unknown name becomes a not-found
4765
- * `error`. A tool throw NEVER escapes — it becomes a result the model can react to.
4766
- * - **Batch never fails as a whole.** The array `execute` runs every call (via
4767
- * `Promise.all`) and resolves results correlated by `id` in order — one bad call
4768
- * (throw or not-found) does not fail the batch.
4769
- * - **Event-free.** A purely functional registry — no Emitter, no events.
4770
- */
4771
- export declare interface ToolManagerInterface {
4772
- readonly count: number;
4773
- add(tool: ToolInterface): void;
4774
- add(tools: readonly ToolInterface[]): void;
4775
- tool(name: string): ToolInterface | undefined;
4776
- tools(): readonly ToolInterface[];
4777
- definitions(): readonly ToolDefinition[];
4778
- execute(call: ToolCall): Promise<ToolResult>;
4779
- execute(calls: readonly ToolCall[]): Promise<readonly ToolResult[]>;
4780
- remove(name: string): boolean;
4781
- remove(names: readonly string[]): boolean;
4782
- clear(): void;
4783
- }
4784
-
4785
- /**
4786
- * Options for `createTool` — the schema the model sees (`name` / `description` /
4787
- * `parameters`) plus the `execute` handler that runs a call.
4788
- *
4789
- * @remarks
4790
- * `name` is required (it keys the tool in a {@link ToolManagerInterface} and is what
4791
- * the model calls); `description` and `parameters` are the optional JSON-Schema the
4792
- * provider advertises (forwarded verbatim). `summary`, when set, is a lean
4793
- * one-or-two-sentence advertisement used IN PLACE of `description` in a
4794
- * {@link ToolManagerInterface.definitions} listing — `description` stays available on
4795
- * the tool for on-demand retrieval. `execute` receives the model-supplied arguments
4796
- * record and returns the tool's result (sync or async).
4797
- */
4798
- export declare interface ToolOptions {
4799
- readonly name: string;
4800
- readonly description?: string;
4801
- readonly summary?: string;
4802
- readonly parameters?: Readonly<Record<string, unknown>>;
4803
- readonly execute: (args: Readonly<Record<string, unknown>>) => Promise<unknown> | unknown;
4804
- }
4805
-
4806
- /**
4807
- * The outcome of executing a {@link ToolCall} — keyed back to the call by `id` and
4808
- * `name`, carrying either a `value` (the result) or an `error` string (the failure).
4809
- */
4810
- export declare interface ToolResult {
4811
- readonly id: string;
4812
- readonly name: string;
4813
- readonly value?: unknown;
4814
- readonly error?: string;
4815
- }
4816
-
4817
- /**
4818
- * One block of an MCP-shaped tool call response — plain text, the only content kind
4819
- * {@link buildToolResult} emits.
4820
- */
4821
- export declare interface ToolResultContent {
4822
- readonly type: 'text';
4823
- readonly text: string;
4824
- }
4825
-
4826
- /**
4827
- * A mutable, `path`-keyed working set of immutable {@link FileInterface}s — the
4828
- * in-memory editing surface over the file primitive.
4829
- *
4830
- * @remarks
4831
- * - **Registry.** Files live in an insertion-ordered `Map` keyed by `path`; `count` is the
4832
- * map size, `file(path)` looks one up, and `files()` lists them in insertion order.
4833
- * - **Write replaces the File (§11 immutability).** Every edit MINTS a replacement
4834
- * {@link FileInterface} (via {@link import('../factories.js').createFile}) rather than
4835
- * mutating in place, transitioning `state` to `'created'` for a brand-new path or
4836
- * `'modified'` for an existing one. A whole-file string write preserves an existing text
4837
- * file's `language`, else infers it from the `path` (`inferLanguage`); writing a string
4838
- * onto an existing BINARY path replaces it with a text file (a deliberate retype). A
4839
- * ranged `write` splices a `Range` of an existing text file.
4840
- * - **Modality rules.** Text-only ops on a binary file are rejected: a ranged `read` /
4841
- * `write`, `prepend`, and `append` throw `MODALITY`; a plain `read(path)` of a binary
4842
- * file returns `undefined`; `search` / `replace` skip binary files (0 matches).
4843
- * - **In-memory removal.** `remove(path)` / `remove(paths)` drop files outright (no
4844
- * tombstone — the `'loaded'` / `'deleted'` states are reserved for a future FileStore);
4845
- * `remove()` and `clear()` both empty the registry and emit the single `clear` signal.
4846
- * The disk/sync lifecycle (`load` / `revert` / `accept` / `purge` / `dirty`) is NOT part
4847
- * of this surface.
4848
- * - **Observable (§13).** The owned {@link emitter} ({@link WorkspaceEventMap}) carries
4849
- * `write` (the resulting file) / `remove` (the path) / `move` (`{ from, to }`) / `clear`.
4850
- * Every event is emitted directly, strictly AFTER the map mutation completes; the
4851
- * emitter isolates a listener throw and routes it to its `error` handler, so a buggy
4852
- * observer can never corrupt a mutation.
4853
- *
4854
- * @example
4855
- * ```ts
4856
- * const workspace = new Workspace()
4857
- * workspace.write('src/main.ts', 'const x = 1')
4858
- * workspace.file('src/main.ts')?.state // 'created'
4859
- * workspace.append('src/main.ts', '\nconst y = 2')
4860
- * workspace.file('src/main.ts')?.state // 'modified'
4861
- * workspace.read('src/main.ts') // 'const x = 1\nconst y = 2'
4862
- * ```
4863
- */
4864
- export declare class Workspace implements WorkspaceInterface {
4865
- #private;
4866
- constructor(options?: WorkspaceOptions, seed?: Iterable<readonly [string, FileInterface]>);
4867
- get id(): string;
4868
- get emitter(): EmitterInterface<WorkspaceEventMap>;
4869
- get count(): number;
4870
- file(path: string): FileInterface | undefined;
4871
- files(): readonly FileInterface[];
4872
- read(path: string): string | undefined;
4873
- read(path: string, range: Range): ReadResult | undefined;
4874
- read(paths: readonly string[]): Readonly<Record<string, string>>;
4875
- has(path: string): boolean;
4876
- has(paths: readonly string[]): boolean;
4877
- search(query: string, options?: SearchOptions): readonly SearchMatch[];
4878
- replace(query: string, replacement: string, options?: ReplaceOptions): ReplaceResult;
4879
- write(path: string, content: string): void;
4880
- write(path: string, content: string, range: Range): void;
4881
- write(files: Readonly<Record<string, string>>): void;
4882
- prepend(path: string, content: string): void;
4883
- prepend(files: Readonly<Record<string, string>>): void;
4884
- append(path: string, content: string): void;
4885
- append(files: Readonly<Record<string, string>>): void;
4886
- move(from: string, to: string): boolean;
4887
- move(mapping: Readonly<Record<string, string>>): boolean;
4888
- remove(): void;
4889
- remove(path: string): boolean;
4890
- remove(paths: readonly string[]): boolean;
4891
- clear(): void;
4892
- snapshot(): WorkspaceSnapshot;
4893
- }
4894
-
4895
3649
  /**
4896
3650
  * The section header {@link import('./AgentContext.js').AgentContext}'s `build()` renders the
4897
3651
  * ACTIVE workspace's TEXT files under — the leading line of the dedicated workspace block in the
@@ -4906,393 +3660,4 @@ export declare class Workspace implements WorkspaceInterface {
4906
3660
  */
4907
3661
  export declare const WORKSPACE_SECTION_HEADER = "## Workspace";
4908
3662
 
4909
- /**
4910
- * An error thrown by the in-memory {@link import('./workspaces/Workspace.js').Workspace} edit
4911
- * surface.
4912
- *
4913
- * @remarks
4914
- * Carries a {@link WorkspaceErrorCode} and an optional `context` bag naming the offending
4915
- * path / range. Thrown for a text-only operation aimed at an image file (`MODALITY` — a
4916
- * ranged read / write, `prepend`, or `append`), an invalid `search` / `replace` regular
4917
- * expression (`PATTERN`), and a structurally invalid ranged-write {@link import('./types.js').Range}
4918
- * (`RANGE` — inverted, or a sub-1 line / column).
4919
- */
4920
- export declare class WorkspaceError extends Error {
4921
- readonly code: WorkspaceErrorCode;
4922
- readonly context?: Readonly<Record<string, unknown>>;
4923
- constructor(code: WorkspaceErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
4924
- }
4925
-
4926
- /**
4927
- * The machine-readable code a {@link import('./errors.js').WorkspaceError} carries
4928
- * (AGENTS §12) — a `catch` branches on `error.code` instead of parsing the message.
4929
- *
4930
- * @remarks
4931
- * - `MODALITY` — a text-only op (ranged read/write, `prepend`, `append`) was aimed at a binary file.
4932
- * - `PATTERN` — a `search` / `replace` `query` was an invalid regular expression (with `regex: true`).
4933
- * - `RANGE` — a ranged `write` got a structurally invalid {@link Range} (inverted, or a sub-1 line/column).
4934
- * - `TOOL` — a handler-level validation fault raised by a workspace-editing tool boundary (see
4935
- * `@orkestrel/tool`) when the agent-supplied args were malformed / unknown. Distinct from the
4936
- * surface faults above (which the live workspace raises) — this is the tool boundary rejecting an
4937
- * un-parseable operation before any workspace call.
4938
- */
4939
- export declare type WorkspaceErrorCode = 'MODALITY' | 'PATTERN' | 'RANGE' | 'TOOL';
4940
-
4941
- /**
4942
- * The observable events a {@link import('./workspaces/Workspace.js').Workspace} emits
4943
- * (AGENTS §13) — each fired AFTER the corresponding mutation completes.
4944
- *
4945
- * @remarks
4946
- * - `write` — a file was written / edited, carrying the resulting {@link FileInterface}.
4947
- * - `remove` — a single file was dropped, carrying its `path`.
4948
- * - `move` — a file was re-keyed, carrying `{ from, to }`.
4949
- * - `clear` — the workspace was emptied (the canonical "emptied" signal, fired by both `remove()` and `clear()`).
4950
- */
4951
- export declare type WorkspaceEventMap = {
4952
- readonly write: readonly [file: FileInterface];
4953
- readonly remove: readonly [path: string];
4954
- readonly move: readonly [move: {
4955
- readonly from: string;
4956
- readonly to: string;
4957
- }];
4958
- readonly clear: readonly [];
4959
- };
4960
-
4961
- /**
4962
- * The data to author a {@link WorkspaceInterface} through a
4963
- * {@link WorkspaceManagerInterface} — the optional `id`, the reserved `on` hooks + `error`
4964
- * handler (§13), and an optional `seed` of initial files.
4965
- *
4966
- * @remarks
4967
- * `id` is the workspace's identity (minted when omitted). `on` / `error` are the §8/§13
4968
- * keys (initial {@link WorkspaceEventMap} listeners + the listener-error handler), flowing
4969
- * the manager's defaults in unless overridden. `seed` is the construction-time hydration
4970
- * seam (path → {@link FileInterface}) — the only way to seat a non-text (binary) file, and
4971
- * what a future FileStore reads a snapshot into; seeding is silent (no `write` events).
4972
- */
4973
- export declare interface WorkspaceInput {
4974
- readonly id?: string;
4975
- readonly on?: EmitterHooks<WorkspaceEventMap>;
4976
- readonly error?: EmitterErrorHandler;
4977
- /** Pre-seeded initial files (path → File), placed silently at construction. */
4978
- readonly seed?: Iterable<readonly [string, FileInterface]>;
4979
- }
4980
-
4981
- /**
4982
- * A mutable, `path`-keyed working set of immutable {@link FileInterface}s — the in-memory
4983
- * editing surface over the file primitive.
4984
- *
4985
- * @remarks
4986
- * Files live in an insertion-ordered map keyed by `path`. Every edit REPLACES the
4987
- * immutable {@link FileInterface} at a path with a new one (transitioning its `state` to
4988
- * `'created'` for a fresh path or `'modified'` for an existing one) rather than mutating in
4989
- * place. The disk/sync lifecycle (`load` / `revert` / `accept` / `purge` / `dirty`) is
4990
- * deliberately NOT part of this surface — it is deferred to a future FileStore. Text-only
4991
- * operations on a binary file are governed by the modality rules (a ranged read/write,
4992
- * `prepend`, and `append` throw `MODALITY`; a plain `read` returns `undefined`; `search` /
4993
- * `replace` skip the file). Observe mutations through the owned {@link emitter} (AGENTS §13).
4994
- */
4995
- export declare interface WorkspaceInterface {
4996
- /** The workspace's identity — its key in a {@link WorkspaceManagerInterface}; minted when not supplied. */
4997
- readonly id: string;
4998
- /** The push observation surface (AGENTS §13) — `write` / `remove` / `move` / `clear`. */
4999
- readonly emitter: EmitterInterface<WorkspaceEventMap>;
5000
- /** The number of files currently held. */
5001
- readonly count: number;
5002
- /** Look up one file by path, or `undefined` when absent. */
5003
- file(path: string): FileInterface | undefined;
5004
- /** List every file in insertion order. */
5005
- files(): readonly FileInterface[];
5006
- read(path: string): string | undefined;
5007
- read(path: string, range: Range): ReadResult | undefined;
5008
- read(paths: readonly string[]): Readonly<Record<string, string>>;
5009
- has(path: string): boolean;
5010
- has(paths: readonly string[]): boolean;
5011
- /** Scan every text file's lines for `query`, returning each hit in insertion order. */
5012
- search(query: string, options?: SearchOptions): readonly SearchMatch[];
5013
- /** Replace `query` with `replacement` across every text file, returning the tally. */
5014
- replace(query: string, replacement: string, options?: ReplaceOptions): ReplaceResult;
5015
- write(path: string, content: string): void;
5016
- write(path: string, content: string, range: Range): void;
5017
- write(files: Readonly<Record<string, string>>): void;
5018
- prepend(path: string, content: string): void;
5019
- prepend(files: Readonly<Record<string, string>>): void;
5020
- append(path: string, content: string): void;
5021
- append(files: Readonly<Record<string, string>>): void;
5022
- move(from: string, to: string): boolean;
5023
- move(mapping: Readonly<Record<string, string>>): boolean;
5024
- remove(): void;
5025
- remove(path: string): boolean;
5026
- remove(paths: readonly string[]): boolean;
5027
- /** Empty the workspace (emits `clear`). */
5028
- clear(): void;
5029
- /**
5030
- * Serialize this workspace to a plain, JSON-serializable {@link WorkspaceSnapshot} — its
5031
- * `id` plus a flat list of its {@link FileInterface}s (each already carries its `path`).
5032
- *
5033
- * @remarks
5034
- * The container serializes ITSELF (`{ id: this.id, files: this.files() }`) — the
5035
- * {@link WorkspaceStoreInterface} persistence seam's payload, paralleling the
5036
- * {@link ConversationInterface}'s own snapshot a later track adds. The snapshot is the
5037
- * durable analogue of the constructor `seed`: a {@link WorkspaceManagerInterface} HYDRATES a
5038
- * workspace from `snapshot.files` through that seam (see {@link WorkspaceManagerInterface.open}).
5039
- * Pure — the `File`s are already frozen plain records (so the snapshot `structuredClone`s /
5040
- * JSON-round-trips losslessly), and snapshotting mutates nothing.
5041
- *
5042
- * @returns The {@link WorkspaceSnapshot} (`{ id, files }`)
5043
- */
5044
- snapshot(): WorkspaceSnapshot;
5045
- }
5046
-
5047
- /**
5048
- * The registry of {@link Workspace}s keyed by `id`, in insertion order, WITH an active pointer
5049
- * — the §9 store over the workspace layer PLUS the `active` / `switch` seam the context renders.
5050
- * Event-free (a registry, like {@link ConversationManager}); the observability lives on each
5051
- * {@link Workspace}.
5052
- *
5053
- * @remarks
5054
- * - **Registry.** Workspaces live in an insertion-ordered `Map` keyed by `id`. `add(input?)`
5055
- * mints a {@link Workspace} (its `id` from `input` or `crypto.randomUUID()`), flowing the
5056
- * manager's default `on` / `error` in unless the `input` OVERRIDES them, and stores it (an
5057
- * already-present `id` OVERWRITES — last write wins). `count` is the map size, `workspace(id)`
5058
- * looks one up, `workspaces()` lists them in insertion order.
5059
- * - **Active pointer.** `active` is the active workspace (what the context renders), `undefined`
5060
- * until the FIRST `add` (which auto-activates it — a registry with workspaces always has one
5061
- * active). A subsequent `add` leaves `active` unchanged. `switch(id)` re-points `active` to the
5062
- * workspace with `id` and returns it; an unknown `id` returns `undefined` and leaves `active`
5063
- * unchanged (the lenient lookup style — never throws, no new error code).
5064
- * - **Removal.** `remove` drops one by id, or a batch (§9.2) — `true` when any was removed;
5065
- * removing the ACTIVE workspace sets `active` to `undefined`. `clear` empties the registry and
5066
- * sets `active` to `undefined`.
5067
- * - **Durable open / save (the optional `store`).** With a {@link WorkspaceStoreInterface} supplied
5068
- * (the `store` option), `open(id)` HYDRATES a workspace on a registry miss — it `store.get(id)`s
5069
- * the snapshot and rebuilds a fresh {@link Workspace} through the constructor `seed`
5070
- * (`snapshot.files` → path → File), then activates it — and `save(id)` PERSISTS a registered
5071
- * workspace's `snapshot()`. Both are LENIENT without a store (open resolves only registered ids,
5072
- * save is a no-op `false`), consistent with the lenient `switch`.
5073
- * - **Event-free.** A purely registry store — no Emitter, no events (each workspace owns its own
5074
- * observable `emitter`).
5075
- *
5076
- * @example
5077
- * ```ts
5078
- * const manager = new WorkspaceManager()
5079
- * const first = manager.add() // auto-activates — active === first
5080
- * manager.add({ id: 'scratch' }) // leaves active unchanged
5081
- * manager.switch('scratch') // re-points active to the 'scratch' workspace
5082
- * manager.count // 2
5083
- * ```
5084
- */
5085
- export declare class WorkspaceManager implements WorkspaceManagerInterface {
5086
- #private;
5087
- constructor(options?: WorkspaceManagerOptions);
5088
- get count(): number;
5089
- get active(): WorkspaceInterface | undefined;
5090
- workspace(id: string): WorkspaceInterface | undefined;
5091
- workspaces(): readonly WorkspaceInterface[];
5092
- add(input?: WorkspaceInput): WorkspaceInterface;
5093
- switch(id: string): WorkspaceInterface | undefined;
5094
- open(id: string): Promise<WorkspaceInterface | undefined>;
5095
- save(id: string): Promise<boolean>;
5096
- remove(ids: readonly string[]): boolean;
5097
- remove(id: string): boolean;
5098
- clear(): void;
5099
- }
5100
-
5101
- /**
5102
- * A registry of {@link WorkspaceInterface}s keyed by their `id`, in insertion order, WITH an
5103
- * active pointer — the §9 store over the workspace layer PLUS the `active` / `switch` seam the
5104
- * context renders. Event-free (each {@link WorkspaceInterface} owns its own `emitter`).
5105
- *
5106
- * @remarks
5107
- * - **Registry.** `count` is how many are stored. `add(input?)` mints a
5108
- * {@link WorkspaceInterface} (its `id` from `input` or a random UUID), flowing the manager's
5109
- * default `on` / `error` in unless the `input` overrides them; `add` of an already-present
5110
- * `id` OVERWRITES it (last write wins). `workspace(id)` looks one up (`undefined` when
5111
- * absent); `workspaces()` lists them in insertion order.
5112
- * - **Active pointer.** `active` is the active workspace (what the context renders), `undefined`
5113
- * until the FIRST `add` (which auto-activates it). A later `add` leaves `active` unchanged.
5114
- * `switch(id)` re-points `active` to the workspace with `id` and returns it; an unknown `id`
5115
- * returns `undefined` and leaves `active` unchanged (the lenient lookup style — never throws).
5116
- * - **Removal.** `remove` drops one by id, or a batch (§9.2, array overload FIRST) — `true`
5117
- * when any was removed; removing the ACTIVE workspace sets `active` to `undefined`. `clear`
5118
- * empties the registry and sets `active` to `undefined`.
5119
- * - **Durable open / save (the optional `store` seam).** When a {@link WorkspaceStoreInterface}
5120
- * is supplied (the `store` option), `open(id)` HYDRATES a workspace from the store on a registry
5121
- * miss (rebuilding it through the constructor `seed` from `snapshot.files`) and `save(id)`
5122
- * PERSISTS a registered workspace's {@link WorkspaceInterface.snapshot}. Both are LENIENT without
5123
- * a store — `open` resolves only registered ids, `save` is a no-op (`false`) — consistent with
5124
- * the lenient `switch`.
5125
- * - **Event-free.** A purely registry store — no Emitter, no events (each workspace owns its own).
5126
- */
5127
- export declare interface WorkspaceManagerInterface {
5128
- readonly count: number;
5129
- /** The active workspace — what the context renders; `undefined` until the first `add`. */
5130
- readonly active: WorkspaceInterface | undefined;
5131
- workspace(id: string): WorkspaceInterface | undefined;
5132
- workspaces(): readonly WorkspaceInterface[];
5133
- add(input?: WorkspaceInput): WorkspaceInterface;
5134
- switch(id: string): WorkspaceInterface | undefined;
5135
- /**
5136
- * Resolve a workspace by id, ACTIVATING it — from the registry if present, else HYDRATED from
5137
- * the optional {@link WorkspaceStoreInterface} (`store`).
5138
- *
5139
- * @remarks
5140
- * - If `id` is ALREADY registered, it is ACTIVATED (`switch`ed to) and returned — no store hit.
5141
- * - Else if a `store` is set, `store.get(id)` is awaited; on a HIT the snapshot is rehydrated
5142
- * into a fresh {@link WorkspaceInterface} through the constructor `seed`
5143
- * (`add({ id, seed: snapshot.files.map((file) => [file.path, file]) })`), which registers AND
5144
- * auto-activates it (or activates it explicitly), and it is returned.
5145
- * - Else (no store, or a store MISS) ⇒ `undefined` (lenient — no throw).
5146
- *
5147
- * @param id - The workspace id to open
5148
- * @returns The activated {@link WorkspaceInterface}, or `undefined` when neither registered nor stored
5149
- */
5150
- open(id: string): Promise<WorkspaceInterface | undefined>;
5151
- /**
5152
- * Persist a REGISTERED workspace's {@link WorkspaceInterface.snapshot} to the optional
5153
- * {@link WorkspaceStoreInterface} (`store`).
5154
- *
5155
- * @remarks
5156
- * Lenient: when a `store` is set AND `id` is registered, `store.set(workspace.snapshot())` is
5157
- * awaited and `true` is returned; otherwise (no store, OR an unknown id) it is a NO-OP returning
5158
- * `false` — never a throw, consistent with the lenient `switch`.
5159
- *
5160
- * @param id - The id of the registered workspace to persist
5161
- * @returns `true` when the snapshot was persisted; `false` when no store / unknown id
5162
- */
5163
- save(id: string): Promise<boolean>;
5164
- remove(ids: readonly string[]): boolean;
5165
- remove(id: string): boolean;
5166
- clear(): void;
5167
- }
5168
-
5169
- /**
5170
- * Options for `createWorkspaceManager` — the per-workspace `on` / `error` defaults the
5171
- * workspaces it creates inherit.
5172
- *
5173
- * @remarks
5174
- * `on` / `error` are the default {@link WorkspaceEventMap} listeners + the emitter's
5175
- * listener-error handler (§13) flowed into every workspace the manager `add`s (a per-`add`
5176
- * {@link WorkspaceInput.on} / `.error` overrides them). Kept minimal (AGENTS §21) — a
5177
- * registry needs no more.
5178
- */
5179
- export declare interface WorkspaceManagerOptions {
5180
- /** The default event listeners for workspaces this manager creates (a per-`add` override wins). */
5181
- readonly on?: EmitterHooks<WorkspaceEventMap>;
5182
- /** The default listener-error handler (a per-`add` override wins). */
5183
- readonly error?: EmitterErrorHandler;
5184
- /**
5185
- * The optional durable {@link WorkspaceStoreInterface} backing {@link WorkspaceManagerInterface.open}
5186
- * / {@link WorkspaceManagerInterface.save} — a memory / JSON / SQLite / IndexedDB store a workspace
5187
- * is HYDRATED from (`open` a registry-miss) and PERSISTED to (`save`). Omitted ⇒ the manager is
5188
- * registry-only: `open` resolves only what is already registered, and `save` is a no-op (`false`).
5189
- */
5190
- readonly store?: WorkspaceStoreInterface;
5191
- }
5192
-
5193
- /**
5194
- * Options for {@link import('./factories.js').createWorkspace} / the
5195
- * {@link import('./workspaces/Workspace.js').Workspace} constructor.
5196
- *
5197
- * @param options - The workspace options
5198
- *
5199
- * @remarks
5200
- * - `id` — the workspace's identity (its key in a {@link WorkspaceManagerInterface}); MINTED via `crypto.randomUUID()` when omitted.
5201
- * - `on` — initial {@link WorkspaceEventMap} listeners (AGENTS §8), wired at construction.
5202
- * - `error` — the emitter's listener-error handler (AGENTS §13); a throw from any listener is routed here instead of rethrown.
5203
- */
5204
- export declare interface WorkspaceOptions {
5205
- readonly id?: string;
5206
- readonly on?: EmitterHooks<WorkspaceEventMap>;
5207
- readonly error?: EmitterErrorHandler;
5208
- }
5209
-
5210
- /**
5211
- * A JSON-serializable snapshot of a workspace's state — its `id` plus a FLAT list of its
5212
- * {@link FileInterface}s — the durable payload the {@link WorkspaceStoreInterface} persists.
5213
- *
5214
- * @remarks
5215
- * Pure JSON DATA (no class instances, no functions): each {@link FileInterface} is already a
5216
- * PLAIN frozen record that `structuredClone`s / JSON-round-trips losslessly, and a `File` carries
5217
- * its OWN `path`, so a flat `files` list is enough to reconstruct the path-keyed map (no nested
5218
- * `path → File` object needed for serialization). The snapshot the container produces from itself
5219
- * ({@link WorkspaceInterface.snapshot}); the durable analogue of the constructor `seed`. A
5220
- * {@link WorkspaceManagerInterface} hydrates a workspace from `snapshot.files` through the seed
5221
- * seam (see {@link WorkspaceManagerInterface.open}). It is narrowed back from an untrusted storage
5222
- * read by {@link import('./helpers.js').isWorkspaceSnapshot} (the AGENTS §14 boundary narrow).
5223
- */
5224
- export declare interface WorkspaceSnapshot {
5225
- readonly id: string;
5226
- readonly files: readonly FileInterface[];
5227
- }
5228
-
5229
- /**
5230
- * One row of the table a
5231
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} persists
5232
- * — a workspace `id` plus its {@link WorkspaceSnapshot} held as ONE OPAQUE JSON column.
5233
- *
5234
- * @remarks
5235
- * The Database twin of {@link WorkspaceStoreInterface} stores the snapshot whole (the `snapshot`
5236
- * column is a `rawShape`, an opaque JSON blob — exactly as the analogous
5237
- * `WorkflowSnapshotRow` in `@orkestrel/workflow` stores a workflow snapshot), so the
5238
- * row type stays FLAT and the `File`-list snapshot shape never forces the contract to `Infer` it.
5239
- * The column therefore reads back as the broad `unknown`; the store narrows it to a
5240
- * {@link WorkspaceSnapshot} on `get` ({@link import('./helpers.js').isWorkspaceSnapshot}, the
5241
- * AGENTS §14 boundary narrow). `id` mirrors {@link WorkspaceSnapshot.id} (the primary key), so a
5242
- * `set` writes `{ id: snapshot.id, snapshot }`.
5243
- */
5244
- export declare interface WorkspaceSnapshotRow {
5245
- readonly id: string;
5246
- /** The whole {@link WorkspaceSnapshot} as one opaque JSON blob — read back as `unknown`, narrowed on `get`. */
5247
- readonly snapshot: unknown;
5248
- }
5249
-
5250
- /**
5251
- * The durable persistence seam for a {@link WorkspaceSnapshot} — three async primitives
5252
- * (`get` / `set` / `delete`) keyed by a workspace id, mirroring the analogous
5253
- * `WorkflowStoreInterface` in `@orkestrel/workflow` (and the driver-swap pattern a server
5254
- * surface's session store would follow).
5255
- *
5256
- * @remarks
5257
- * The store persists the {@link WorkspaceSnapshot} — the self-contained, pure-JSON workspace
5258
- * state — so a JSON / SQLite / IndexedDB backend swaps in WITHOUT touching the manager or the
5259
- * workspace: the in-memory default
5260
- * {@link import('./workspaces/stores/MemoryWorkspaceStore.js').MemoryWorkspaceStore} and its
5261
- * driver-pluggable twin
5262
- * {@link import('./workspaces/stores/DatabaseWorkspaceStore.js').DatabaseWorkspaceStore} (the
5263
- * snapshot as one opaque JSON column) share THIS one interface. Hydration is NOT a store concern
5264
- * — a {@link WorkspaceManagerInterface} reads a snapshot back and rebuilds the live workspace
5265
- * through the constructor `seed` (see {@link WorkspaceManagerInterface.open} /
5266
- * {@link WorkspaceManagerInterface.save}).
5267
- *
5268
- * Every primitive is async (a `Promise`), so a durable backend (a database round-trip) fits the
5269
- * same shape as the memory one. The snapshot carries its OWN id, so `set` takes no separate id
5270
- * param (mirroring the analogous `WorkflowStoreInterface.set` in `@orkestrel/workflow`). UNLIKE a
5271
- * session store there is NO idle-TTL / eviction — a persisted workspace lives until an explicit
5272
- * `delete`. It is concrete over {@link WorkspaceSnapshot} — no generic parameter (AGENTS §21
5273
- * minimal-interface), since the snapshot is the ONE payload a workspace store persists.
5274
- */
5275
- export declare interface WorkspaceStoreInterface {
5276
- /**
5277
- * Resolve the persisted snapshot for `id`, or `undefined` if none is stored.
5278
- *
5279
- * @param id - The workspace id to resolve (a {@link WorkspaceSnapshot.id})
5280
- * @returns The persisted snapshot, or `undefined` if absent
5281
- */
5282
- get(id: string): Promise<WorkspaceSnapshot | undefined>;
5283
- /**
5284
- * Insert or replace a snapshot under its own `snapshot.id` (no separate id param —
5285
- * mirroring the analogous `WorkflowStoreInterface.set` in `@orkestrel/workflow`).
5286
- *
5287
- * @param snapshot - The snapshot to store (keyed by its `id`)
5288
- */
5289
- set(snapshot: WorkspaceSnapshot): Promise<void>;
5290
- /**
5291
- * Drop a snapshot by id; an absent id is a no-op (no throw).
5292
- *
5293
- * @param id - The workspace id to drop
5294
- */
5295
- delete(id: string): Promise<void>;
5296
- }
5297
-
5298
3663
  export { }