@orkestrel/toolbox 0.0.1 → 0.0.2

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.
@@ -1,10 +1,11 @@
1
- import { arrayShape, booleanShape, createContract, integerShape, isNonEmptyString, isRecord, isString, jsonShape, literalShape, numberShape, objectShape, optionalShape, rawShape, recordShape, samplesToSchema, schemaToObject, schemaToParameters, schemaToShape, stringShape, unionShape } from "@orkestrel/contract";
1
+ import { arrayShape, attempt, booleanShape, cloneJSONRecord, createContract, integerShape, isFiniteNumber, isNonEmptyString, isRecord, isString, jsonShape, literalShape, numberShape, objectShape, optionalShape, parseJSONValue, rawShape, recordShape, samplesToSchema, schemaToObject, schemaToParameters, schemaToShape, stringShape, unionShape } from "@orkestrel/contract";
2
+ import { MAX_TIMER_MS, createWorkflowContract } from "@orkestrel/workflow";
2
3
  import { isTerminalError } from "@orkestrel/terminal";
3
- import { createDatabase, createMemoryDriver, generateUUID, isDatabaseError, shapeToColumnType } from "@orkestrel/database";
4
+ import { createDatabase, createMemoryDriver, isDatabaseError } from "@orkestrel/database";
4
5
  import { isRelationError } from "@orkestrel/relation";
5
- import { WorkspaceError, createWorkspaceManager, isText, rangeOf } from "@orkestrel/agent";
6
+ import { agentResultToJSON } from "@orkestrel/agent";
7
+ import { createWorkspaceManager, isText, rangeOf } from "@orkestrel/workspace";
6
8
  import { createTool } from "@orkestrel/tool";
7
- import { WorkflowError, createWorkflowContract } from "@orkestrel/workflow";
8
9
  //#region src/core/constants.ts
9
10
  /**
10
11
  * The name {@link import('./factories.js').createAgentTool} advertises by default — the key a
@@ -25,15 +26,6 @@ var AGENT_TOOL_NAME = "agent";
25
26
  */
26
27
  var AGENT_TOOL_DEPTH = 8;
27
28
  /**
28
- * The DESCRIPTION {@link import('./factories.js').createAgentTool} advertises — a short guide
29
- * that teaches a model how to delegate a task to a sub-agent.
30
- *
31
- * @remarks
32
- * Mirrors {@link WORKFLOW_TOOL_DESCRIPTION} / `WORKSPACE_TOOL_DESCRIPTION`'s teaching style: names
33
- * the required `task` field, and documents the optional per-call `provider` / `tools` /
34
- * `system` overrides.
35
- */
36
- /**
37
29
  * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createAgentTool}
38
30
  * advertises in place of {@link AGENT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
39
31
  * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
@@ -41,6 +33,10 @@ var AGENT_TOOL_DEPTH = 8;
41
33
  * {@link import('./factories.js').createDescribeTool}.
42
34
  */
43
35
  var AGENT_TOOL_SUMMARY = "Delegate a task to a sub-agent and return its result; each call runs one sub-agent turn to completion. Call describe('agent') for the optional provider/tools/system overrides.";
36
+ /**
37
+ * The description {@link import('./factories.js').createAgentTool} advertises — a short guide
38
+ * covering the required task and optional provider, tools, and system overrides.
39
+ */
44
40
  var AGENT_TOOL_DESCRIPTION = [
45
41
  "Delegate a task to a sub-agent and return its result. Every call runs ONE sub-agent turn to completion.",
46
42
  "",
@@ -59,11 +55,9 @@ var AGENT_TOOL_DESCRIPTION = [
59
55
  * {@link import('./factories.js').createWorkflowTool}'s depth/cycle guards enforce.
60
56
  *
61
57
  * @remarks
62
- * OWNED here now (ported from `@orkestrel/workflow`, whose engine no longer uses it — only the
63
- * tool-authoring guards this package now owns consume it). The limit lives in ONE place: an
64
- * agent-function-wrapped agent running at this depth can no longer author + run a NESTED
65
- * workflow through its bound workflow tool (that would be depth `MAX_WORKFLOW_DEPTH + 1`), so
66
- * the over-deep invocation is REJECTED (a typed `DEPTH` `WorkflowError` throw, `@orkestrel/workflow`).
58
+ * Depth is zero-based and counts workflow tags after the root: the root plus eight nested
59
+ * workflows is allowed, while a ninth nested workflow is rejected with this package's typed
60
+ * `DEPTH` `ToolboxError`.
67
61
  */
68
62
  var MAX_WORKFLOW_DEPTH = 8;
69
63
  /**
@@ -73,7 +67,7 @@ var MAX_WORKFLOW_DEPTH = 8;
73
67
  * under onto a wrapped agent's `context.tools`.
74
68
  *
75
69
  * @remarks
76
- * OWNED here now (ported from `@orkestrel/workflow`). The propagation seam's well-known key: when
70
+ * The propagation seam's well-known key: when
77
71
  * `createAgentFunction`'s `runner` option is supplied, it adds a `createWorkflowTool`-built tool
78
72
  * under this name to the agent's `context.tools`, so it can author + run a NESTED workflow
79
73
  * (bounded by {@link MAX_WORKFLOW_DEPTH}).
@@ -84,7 +78,7 @@ var WORKFLOW_TOOL_NAME = "workflow";
84
78
  * {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
85
79
  *
86
80
  * @remarks
87
- * OWNED here now (ported from `@orkestrel/workflow`). Each step becomes a one-task phase, in
81
+ * Each step becomes a one-task phase, in
88
82
  * order; a step's `name` is a REGISTERED behavior name (not a label) — the registry key its
89
83
  * task's `run` resolves against. The tool expands this
90
84
  * ({@link import('./helpers.js').expandSteps}) into a valid `WorkflowDefinition`
@@ -99,7 +93,7 @@ var WORKFLOW_TOOL_FLAT_EXAMPLE = Object.freeze({
99
93
  * the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
100
94
  *
101
95
  * @remarks
102
- * OWNED here now (ported from `@orkestrel/workflow`). The full four-level form, documented in
96
+ * The full four-level form, documented in
103
97
  * {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced alternative. It is embedded VERBATIM.
104
98
  */
105
99
  var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
@@ -116,19 +110,6 @@ var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
116
110
  })])
117
111
  });
118
112
  /**
119
- * The DESCRIPTION {@link import('./factories.js').createWorkflowTool} advertises — a multi-line
120
- * guide that teaches a small model how to author a complete workflow tree.
121
- *
122
- * @remarks
123
- * OWNED here now (ported from `@orkestrel/workflow`). Presents the SIMPLE flat shape
124
- * (`{ name, steps: [{ name }] }`) as the PRIMARY way with one complete worked example
125
- * ({@link WORKFLOW_TOOL_FLAT_EXAMPLE}), names that a step's `name` is a REGISTERED name (not a
126
- * human label), and documents the full nested `WorkflowDefinition` as the ADVANCED form with a
127
- * minimal example ({@link WORKFLOW_TOOL_NESTED_EXAMPLE}). The `parameters` the tool advertises
128
- * are the FLAT shape's schema; the nested form is the documented escape-hatch (the tool accepts
129
- * both).
130
- */
131
- /**
132
113
  * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkflowTool}
133
114
  * advertises in place of {@link WORKFLOW_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
134
115
  * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
@@ -136,26 +117,28 @@ var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
136
117
  * {@link import('./factories.js').createDescribeTool}.
137
118
  */
138
119
  var WORKFLOW_TOOL_SUMMARY = "Author and run a multi-phase workflow in one call — phases run in sequence, tasks within a phase run concurrently. Call describe('workflow') for the full authoring schema and examples.";
120
+ /**
121
+ * The description {@link import('./factories.js').createWorkflowTool} advertises — the flat
122
+ * authoring form, its worked example, and the advanced nested definition form.
123
+ */
139
124
  var WORKFLOW_TOOL_DESCRIPTION = [
140
125
  "Author and run a workflow (phases run sequentially, the tasks within a phase run concurrently) in one call.",
141
126
  "",
142
127
  "SIMPLEST way — a flat list of steps. Each step runs one registered behavior; steps run one after another:",
143
128
  " { \"name\": \"<workflow name>\", \"steps\": [ { \"name\": \"<registered name>\" }, ... ] }",
144
129
  "- a step's \"name\" is a REGISTERED behavior name (a registry key), NOT a human label.",
145
- "- the top-level \"name\" (the workflow name) is optional. Ids are filled in for you.",
130
+ "- every step name must exist in the host-supplied functions registry; an unknown name is rejected.",
131
+ "- the top-level \"name\" is optional; when present it is also the workflow persistence id. Other ids are filled in for you.",
146
132
  "Example:",
147
133
  JSON.stringify(WORKFLOW_TOOL_FLAT_EXAMPLE),
148
134
  "",
149
- "ADVANCED — the full nested form, for multi-task phases or explicit ids. A workflow has phases; a phase has tasks; a task has a \"run\" (a registered behavior name):",
135
+ "ADVANCED — the full nested form, for multi-task phases or explicit ids. A workflow has phases; a phase has tasks; a task may have a \"run\" (a registered behavior name); omitting it creates a JSON-null no-op:",
150
136
  JSON.stringify(WORKFLOW_TOOL_NESTED_EXAMPLE),
151
137
  "In the nested form you may omit any \"id\"/\"name\" and they are filled in positionally; a provided one is kept."
152
138
  ].join("\n");
153
139
  /**
154
140
  * The name {@link import('./factories.js').createWorkspaceTool} advertises by default — the key a
155
141
  * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
156
- *
157
- * @remarks
158
- * OWNED here now (ported from `@orkestrel/agent`).
159
142
  */
160
143
  var WORKSPACE_TOOL_NAME = "workspace";
161
144
  /**
@@ -163,7 +146,7 @@ var WORKSPACE_TOOL_NAME = "workspace";
163
146
  * VERBATIM in {@link WORKSPACE_TOOL_DESCRIPTION}.
164
147
  *
165
148
  * @remarks
166
- * OWNED here now (ported from `@orkestrel/agent`). A `'write'` op (the most common authoring
149
+ * A `'write'` op (the most common authoring
167
150
  * action): create or overwrite `notes.txt` with `hello`. Frozen so it cannot be mutated in
168
151
  * place.
169
152
  */
@@ -173,17 +156,6 @@ var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
173
156
  content: "hello"
174
157
  });
175
158
  /**
176
- * The DESCRIPTION {@link import('./factories.js').createWorkspaceTool} advertises — a multi-line
177
- * guide that teaches a small model how to drive a workspace through the single
178
- * `operation`-keyed tool.
179
- *
180
- * @remarks
181
- * OWNED here now (ported from `@orkestrel/agent`). Mirrors {@link WORKFLOW_TOOL_DESCRIPTION}'s
182
- * teaching style: names the `operation` discriminant field, enumerates all 13 operations with
183
- * their FLAT fields, gives a worked example for the common ones, and embeds
184
- * {@link WORKSPACE_TOOL_EXAMPLE} verbatim.
185
- */
186
- /**
187
159
  * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkspaceTool}
188
160
  * advertises in place of {@link WORKSPACE_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
189
161
  * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
@@ -191,6 +163,14 @@ var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
191
163
  * {@link import('./factories.js').createDescribeTool}.
192
164
  */
193
165
  var WORKSPACE_TOOL_SUMMARY = "Read and edit files in a workspace — one operation per call (read, write, list, search, replace, splice, move, remove, plus workspace switching), chosen by the 'operation' field. Call describe('workspace') for the full operation list and fields.";
166
+ /**
167
+ * The description {@link import('./factories.js').createWorkspaceTool} advertises — the
168
+ * operation-keyed workspace protocol, all supported operations, and worked examples.
169
+ *
170
+ * @remarks
171
+ * Mirrors {@link WORKFLOW_TOOL_DESCRIPTION}'s teaching style and embeds
172
+ * {@link WORKSPACE_TOOL_EXAMPLE} verbatim.
173
+ */
194
174
  var WORKSPACE_TOOL_DESCRIPTION = [
195
175
  "Read and edit files in a workspace. Every call is ONE operation, chosen by the \"operation\" field.",
196
176
  "All file operations act on the ACTIVE workspace; use \"workspaces\" then \"switch\" to move between workspaces.",
@@ -199,8 +179,8 @@ var WORKSPACE_TOOL_DESCRIPTION = [
199
179
  "- read { \"operation\": \"read\", \"path\": \"<file>\" } — return the file's text.",
200
180
  "- list { \"operation\": \"list\" } — list every file in the active workspace (path, state, size, lines, kind).",
201
181
  "- has { \"operation\": \"has\", \"path\": \"<file>\" } — whether the file exists.",
202
- "- search { \"operation\": \"search\", \"query\": \"<text>\", \"regex\"?: bool, \"exact\"?: bool, \"limit\"?: int } — find lines matching the query across all files.",
203
- "- replace { \"operation\": \"replace\", \"query\": \"<text>\", \"replacement\": \"<text>\", \"regex\"?: bool, \"exact\"?: bool, \"limit\"?: int } — replace matches across all files.",
182
+ "- search { \"operation\": \"search\", \"query\": \"<text>\", \"regex\"?: bool, \"sensitive\"?: bool, \"limit\"?: int } — find lines matching the query across all files.",
183
+ "- replace { \"operation\": \"replace\", \"query\": \"<text>\", \"replacement\": \"<text>\", \"regex\"?: bool, \"sensitive\"?: bool, \"limit\"?: int } — replace matches across all files.",
204
184
  "- write { \"operation\": \"write\", \"path\": \"<file>\", \"content\": \"<text>\" } — create or overwrite the whole file.",
205
185
  "- splice { \"operation\": \"splice\", \"path\": \"<file>\", \"content\": \"<text>\", \"fromLine\": int, \"fromColumn\": int, \"toLine\": int, \"toColumn\": int } — replace a 1-based range (from inclusive, to exclusive) with content.",
206
186
  "- prepend { \"operation\": \"prepend\", \"path\": \"<file>\", \"content\": \"<text>\" } — add content to the start of the file.",
@@ -210,7 +190,7 @@ var WORKSPACE_TOOL_DESCRIPTION = [
210
190
  "- workspaces { \"operation\": \"workspaces\" } — list the workspaces you can switch between (each id, file count, active).",
211
191
  "- switch { \"operation\": \"switch\", \"id\": \"<id>\" } — make the workspace with that id active (ids come from \"workspaces\").",
212
192
  "",
213
- "Notes: lines and columns are 1-based (column 1 is the first character). \"regex\" defaults to false (a literal substring), \"exact\" defaults to true (case-sensitive). \"search\"/\"replace\"/\"splice\" act only on text files. Editing with no active workspace auto-creates one.",
193
+ "Notes: lines and columns are 1-based (column 1 is the first character). \"regex\" defaults to false (a literal substring), \"sensitive\" defaults to true (case-sensitive). \"search\"/\"replace\"/\"splice\" act only on text files. Editing with no active workspace auto-creates one.",
214
194
  "",
215
195
  "Example — write a file:",
216
196
  JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
@@ -253,6 +233,7 @@ var PROMPT_TOOL_NAME = "ask";
253
233
  * {@link import('./factories.js').createDescribeTool}.
254
234
  */
255
235
  var PROMPT_TOOL_SUMMARY = "Ask another terminal a question and BLOCK until it answers; the call resolves with the answered value. Call describe('ask') for the required fields.";
236
+ /** The full prompt protocol {@link import('./factories.js').createPromptTool} advertises. */
256
237
  var PROMPT_TOOL_DESCRIPTION = [
257
238
  "Ask another terminal a question and block until it answers. This call does not return until the addressed terminal answers, or the prompt fails.",
258
239
  "",
@@ -283,6 +264,7 @@ var ANSWER_TOOL_NAME = "answer";
283
264
  * {@link import('./factories.js').createDescribeTool}.
284
265
  */
285
266
  var ANSWER_TOOL_SUMMARY = "List prompts addressed to this terminal, or answer one by id. Call describe('answer') for the required fields.";
267
+ /** The pending/answer protocol {@link import('./factories.js').createAnswerTool} advertises. */
286
268
  var ANSWER_TOOL_DESCRIPTION = [
287
269
  "List the prompts currently addressed to this terminal, or answer one of them by id. Every call is ONE operation, chosen by the \"operation\" field.",
288
270
  "",
@@ -312,14 +294,14 @@ var DATABASE_TOOL_NAME = "database";
312
294
  * The lean {@link import('@orkestrel/tool').ToolInterface.summary} the upcoming database tool
313
295
  * will advertise in place of {@link DATABASE_TOOL_DESCRIPTION}.
314
296
  */
315
- var DATABASE_TOOL_SUMMARY = "Create and query a database — one operation per call (create, tables, get, records, count, aggregate, add, set, update, remove, migrate, destroy), chosen by the 'operation' field. Call describe('database') for the full operation list, the criteria form, and the column DSL.";
297
+ var DATABASE_TOOL_SUMMARY = "Create and query a database — one operation per call (create, tables, get, records, count, aggregate, add, set, update, remove, destroy), chosen by the 'operation' field. Call describe('database') for the full operation list, the query form, and the column DSL.";
316
298
  /**
317
299
  * The DESCRIPTION the upcoming database tool will advertise — a multi-line guide that teaches a
318
- * small model the operation list, the SERIALIZED criteria form, and the {@link import('./types.js').TableSpec}
300
+ * small model the operation list, the SERIALIZED query form, and the {@link import('./types.js').TableSpec}
319
301
  * column DSL.
320
302
  *
321
303
  * @remarks
322
- * The criteria form is deliberately SERIALIZED (never fluent) — every condition is a flat object
304
+ * The query form is deliberately SERIALIZED (never fluent) — every condition is a flat object
323
305
  * `{ column, operator, values, connector? }` where `values` is ALWAYS an array, even for a
324
306
  * single-value operator (`{ column: 'age', operator: 'from', values: [18] }`), so a small model
325
307
  * never has to chain method calls or guess whether a value is scalar or a list.
@@ -328,25 +310,25 @@ var DATABASE_TOOL_DESCRIPTION = [
328
310
  "Create and query a database. Every call is ONE operation, chosen by the \"operation\" field.",
329
311
  "",
330
312
  "Operations (each takes the fields listed):",
331
- "- create { \"operation\": \"create\", \"id\": \"<database id>\", \"tables\": { \"<table>\": { \"columns\": { \"<column>\": \"string\" | \"integer\" | \"number\" | \"boolean\" | { \"type\": \"string\", \"optional\": true } } } } } — define a new database.",
313
+ "- create { \"operation\": \"create\", \"id\": \"<database id>\", \"tables\": { \"<table>\": { \"columns\": { \"<column>\": \"string\" | \"integer\" | \"number\" | \"boolean\" | { \"type\": \"string\", \"optional\": true } } } }, \"primary\"?: { \"<table>\": \"<column>\" }, \"indexes\"?: { \"<table>\": [[\"<column>\"]] }, \"version\"?: <number> } — define a new database.",
332
314
  "- tables { \"operation\": \"tables\", \"id\": \"<database id>\" } — list a database's table names.",
333
315
  "- get { \"operation\": \"get\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\" } — fetch one row by its primary key.",
334
- "- records { \"operation\": \"records\", \"id\": \"<database id>\", \"table\": \"<table>\", \"criteria\"?: <Criteria> } — list rows matching criteria.",
335
- "- count { \"operation\": \"count\", \"id\": \"<database id>\", \"table\": \"<table>\", \"criteria\"?: <Criteria> } — count rows matching criteria.",
336
- "- aggregate { \"operation\": \"aggregate\", \"id\": \"<database id>\", \"table\": \"<table>\", \"column\": \"<column>\", \"function\": \"count\" | \"sum\" | \"average\" | \"minimum\" | \"maximum\", \"criteria\"?: <Criteria> } — compute an aggregate.",
316
+ "- records { \"operation\": \"records\", \"id\": \"<database id>\", \"table\": \"<table>\", \"query\"?: <Query> } — list rows matching a query.",
317
+ "- count { \"operation\": \"count\", \"id\": \"<database id>\", \"table\": \"<table>\", \"query\"?: <Query> } — count rows matching a query.",
318
+ "- aggregate { \"operation\": \"aggregate\", \"id\": \"<database id>\", \"table\": \"<table>\", \"column\": \"<column>\", \"function\": \"count\" | \"sum\" | \"average\" | \"minimum\" | \"maximum\", \"query\"?: <Query> } — compute an aggregate.",
337
319
  "- add { \"operation\": \"add\", \"id\": \"<database id>\", \"table\": \"<table>\", \"row\": { ... } } — insert a row (fails on a duplicate key).",
338
320
  "- set { \"operation\": \"set\", \"id\": \"<database id>\", \"table\": \"<table>\", \"row\": { ... } } — upsert a row.",
339
- "- update { \"operation\": \"update\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\", \"row\": { ... } } — patch an existing row.",
321
+ "- update { \"operation\": \"update\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\", \"changes\": { ... } } — patch an existing row.",
340
322
  "- remove { \"operation\": \"remove\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\" } — delete a row by key.",
341
- "- migrate { \"operation\": \"migrate\", \"id\": \"<database id>\", \"tables\": { ... } } — replace the table layout in place.",
342
323
  "- destroy { \"operation\": \"destroy\", \"id\": \"<database id>\" } — drop a database entirely.",
343
324
  "",
344
- "Criteria form — SERIALIZED, never fluent. A condition is a flat object; \"values\" is ALWAYS an array, even for one value:",
325
+ "Query form — SERIALIZED, never fluent. A condition is a flat object; \"values\" is ALWAYS an array, even for one value:",
345
326
  " { \"conditions\": [ { \"column\": \"age\", \"operator\": \"from\", \"values\": [18], \"connector\": \"and\" } ], \"order\"?: [...], \"offset\"?: 0, \"limit\"?: 100 }",
346
327
  " operators: equals, not, above, below, from, to, between, like, glob, starts, ends, any, none, absent, present.",
347
328
  " \"connector\" joins this condition to the next (\"and\" | \"or\"); omit on the last condition.",
348
329
  "",
349
- "Column DSL (used by \"create\"/\"migrate\" \"tables\"): a column is either a bare type string (\"string\" | \"integer\" | \"number\" | \"boolean\"), or { \"type\": \"<type>\", \"optional\": true } when the column may be absent from a row.",
330
+ "Column DSL (used by \"create\" \"tables\"): a column is either a bare type string (\"string\" | \"integer\" | \"number\" | \"boolean\"), or { \"type\": \"<type>\", \"optional\": true } when the column may be absent from a row.",
331
+ "Schema configuration is creation-time only. Evolve a schema by opening a newly configured target-version database over a versioned persistent driver; reconciliation requires paired metadata/stamp capabilities.",
350
332
  "Example — create a database:",
351
333
  JSON.stringify({
352
334
  operation: "create",
@@ -360,28 +342,27 @@ var DATABASE_TOOL_DESCRIPTION = [
360
342
  }
361
343
  } } }
362
344
  }),
363
- "Example — query with criteria:",
345
+ "Example — query records:",
364
346
  JSON.stringify({
365
347
  operation: "records",
366
348
  id: "shop",
367
349
  table: "products",
368
- criteria: { conditions: [{
350
+ query: { conditions: [{
369
351
  column: "price",
370
352
  operator: "below",
371
353
  values: [50]
372
354
  }] }
373
355
  })
374
356
  ].join("\n");
375
- /** The default cap on rows a `records` / `remove` call returns (or acts on) when the caller omits `criteria.limit` — the upcoming database tool's default row ceiling. */
357
+ /** The default cap on rows a `records` call returns when the caller omits `query.limit` — the upcoming database tool's default row ceiling. */
376
358
  var DATABASE_TOOL_LIMIT = 1e3;
377
- /** The database tool's mutating operations — disabled by `DatabaseToolOptions.readonly`. */
378
- var DATABASE_TOOL_MUTATIONS = /* @__PURE__ */ new Set([
359
+ /** The runtime-frozen database-tool mutation names disabled by `DatabaseToolOptions.readonly`. */
360
+ var DATABASE_TOOL_MUTATIONS = Object.freeze([
379
361
  "create",
380
362
  "add",
381
363
  "set",
382
364
  "update",
383
365
  "remove",
384
- "migrate",
385
366
  "destroy"
386
367
  ]);
387
368
  /**
@@ -438,6 +419,7 @@ var INFER_TOOL_NAME = "infer";
438
419
  * {@link import('./factories.js').createDescribeTool}.
439
420
  */
440
421
  var INFER_TOOL_SUMMARY = "Infer a JSON Schema (as advertised tool parameters) from one or more example values. Call describe('infer') for the required fields.";
422
+ /** The schema-inference protocol {@link import('./factories.js').createInferTool} advertises. */
441
423
  var INFER_TOOL_DESCRIPTION = [
442
424
  "Infer a JSON Schema from example values, returned in the same shape a tool advertises its parameters.",
443
425
  "",
@@ -530,13 +512,9 @@ var INFER_TOOL_DESCRIPTION = [
530
512
  //#endregion
531
513
  //#region src/core/errors.ts
532
514
  /**
533
- * Thrown by {@link import('./factories.js').createAgentTool}'s and
534
- * {@link import('./factories.js').createDescribeTool}'s handlers on every failure path — a
535
- * malformed / unresolvable call or an unknown tool name (`TOOL`), a delegation that would
536
- * exceed the configured depth bound or re-enter an ancestor (`DEPTH`), a prompt cycle
537
- * (`DEADLOCK`), a prompt that expired before it was answered (`EXPIRE`), or an answer that
538
- * failed to apply (`ANSWER`) — the last three thrown by
539
- * {@link import('./factories.js').createPromptTool} / {@link import('./factories.js').createAnswerTool}.
515
+ * A package-owned tool-call failure: malformed input or unresolved configuration (`TOOL`),
516
+ * delegation depth/cycle rejection (`DEPTH`), prompt failure (`DEADLOCK` / `EXPIRE` / `ANSWER`),
517
+ * or a translated upstream database/relation failure (`DATABASE` / `RELATION`).
540
518
  *
541
519
  * @remarks
542
520
  * Carries a machine-readable `code` (see {@link import('./types.js').ToolboxErrorCode}) and
@@ -734,7 +712,7 @@ var taskDraftShape = objectShape({
734
712
  description: optionalShape(stringShape({ description: "Optional task description." })),
735
713
  run: optionalShape(stringShape({
736
714
  min: 1,
737
- description: "The registered behavior name to invoke (a registry key, not a label); omitted has no handler."
715
+ description: "The registered behavior name to invoke (a registry key, not a label); omitted completes with JSON null."
738
716
  })),
739
717
  retries: optionalShape(integerShape({
740
718
  min: 0,
@@ -742,7 +720,8 @@ var taskDraftShape = objectShape({
742
720
  })),
743
721
  timeout: optionalShape(integerShape({
744
722
  min: 0,
745
- description: "Per-attempt deadline in milliseconds; overrides the phase default. Omitted means no deadline."
723
+ max: MAX_TIMER_MS,
724
+ description: "Persisted per-attempt deadline in milliseconds (0 disables it); omitted means no deadline."
746
725
  }))
747
726
  });
748
727
  /**
@@ -773,7 +752,8 @@ var phaseDraftShape = objectShape({
773
752
  *
774
753
  * @remarks
775
754
  * The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract} compiles.
776
- * `run` stays required on the strict form; a provided `id` / `name` still has `minLength: 1` (so
755
+ * `run` stays optional like the strict form; omission is the deliberate JSON `null` no-op. A
756
+ * provided `id` / `name` still has `minLength: 1` (so
777
757
  * an explicitly-empty `id: ''` is REJECTED, not auto-filled). After
778
758
  * {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
779
759
  * validated against the STRICT `createWorkflowContract` (`@orkestrel/workflow`) gate before
@@ -834,10 +814,10 @@ var workflowStepsShape = objectShape({
834
814
  * The union compiles to an `anyOf` JSON Schema + a `unionOf` guard + a first-match parser
835
815
  * automatically ({@link import('./factories.js').createWorkspaceTool} types the result to the
836
816
  * hand-written {@link import('./types.js').WorkspaceOperation}). `limit` and the four `'splice'`
837
- * caret components are POSITIVE integers (`integerShape({ min: 1 })`); `regex` / `exact` are
838
- * `optionalShape(booleanShape(...))`. The two REGISTRY arms — `workspaces` (list the workspaces
839
- * the model can move between) and `switch` (re-point the active one by `id`) — let a model
840
- * DISCOVER then CHOOSE which workspace the edit / read arms target.
817
+ * caret components are POSITIVE integers (`integerShape({ min: 1 })`); `regex` / `sensitive`
818
+ * are `optionalShape(booleanShape(...))`. The two REGISTRY arms — `workspaces` (list the
819
+ * workspaces the model can move between) and `switch` (re-point the active one by `id`) — let a
820
+ * model DISCOVER then CHOOSE which workspace the edit / read arms target.
841
821
  */
842
822
  var workspaceToolShape = unionShape(objectShape({
843
823
  operation: literalShape(["read"], { description: "Read a whole text file's text by path." }),
@@ -849,7 +829,7 @@ var workspaceToolShape = unionShape(objectShape({
849
829
  operation: literalShape(["search"], { description: "Search every text file for a query, returning each hit." }),
850
830
  query: stringShape({ description: "The text (or regular-expression source) to search for." }),
851
831
  regex: optionalShape(booleanShape({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
852
- exact: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
832
+ sensitive: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
853
833
  limit: optionalShape(integerShape({
854
834
  min: 1,
855
835
  description: "Stop after this many matches across all files. Omitted means unlimited."
@@ -859,7 +839,7 @@ var workspaceToolShape = unionShape(objectShape({
859
839
  query: stringShape({ description: "The text (or regular-expression source) to replace." }),
860
840
  replacement: stringShape({ description: "The text to substitute for each match." }),
861
841
  regex: optionalShape(booleanShape({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
862
- exact: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
842
+ sensitive: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
863
843
  limit: optionalShape(integerShape({
864
844
  min: 1,
865
845
  description: "Stop after this many replacements across all files. Omitted means unlimited."
@@ -955,8 +935,8 @@ var orderShape = objectShape({
955
935
  column: stringShape({ description: "The column to sort by." }),
956
936
  direction: literalShape(["ascending", "descending"], { description: "The sort direction." })
957
937
  });
958
- /** The SERIALIZED criteria form — conditions, order, and pagination. */
959
- var criteriaShape = objectShape({
938
+ /** The SERIALIZED query form — conditions, order, and pagination. */
939
+ var queryShape = objectShape({
960
940
  conditions: optionalShape(arrayShape(conditionShape, { description: "The WHERE conditions, folded left to right." })),
961
941
  order: optionalShape(arrayShape(orderShape, { description: "The sort terms, applied in order." })),
962
942
  limit: optionalShape(integerShape({
@@ -970,17 +950,17 @@ var criteriaShape = objectShape({
970
950
  });
971
951
  /**
972
952
  * The shape of {@link import('./factories.js').createDatabaseTool}'s call arguments —
973
- * discriminated by `operation` into the 12 database operations (`'create'` / `'tables'` /
953
+ * discriminated by `operation` into the 11 database operations (`'create'` / `'tables'` /
974
954
  * `'get'` / `'records'` / `'count'` / `'aggregate'` / `'add'` / `'set'` / `'update'` /
975
- * `'remove'` / `'migrate'` / `'destroy'`).
955
+ * `'remove'` / `'destroy'`).
976
956
  *
977
957
  * @remarks
978
- * Every arm carries `id` (the database id). `'create'` / `'migrate'` carry `tables` (the
958
+ * Every arm carries `id` (the database id). `'create'` carries `tables` (the
979
959
  * {@link import('./types.js').TableSpec} column DSL, compiled via
980
960
  * {@link import('./helpers.js').expandTables}); `'get'` / `'update'` / `'remove'` carry `key`
981
961
  * (one key or an array of keys, positional); `'add'` / `'set'` carry `row` (one row or an array of
982
962
  * rows); `'update'` also carries `changes` (a loose partial row); `'records'` / `'count'` /
983
- * `'aggregate'` carry an optional `criteria` (the SERIALIZED form — `values` is ALWAYS an array,
963
+ * `'aggregate'` carry an optional `query` (the SERIALIZED form — `values` is ALWAYS an array,
984
964
  * even for a single-value operator, so a caller never chains method calls or guesses arity).
985
965
  */
986
966
  var databaseToolShape = unionShape(objectShape({
@@ -994,7 +974,12 @@ var databaseToolShape = unionShape(objectShape({
994
974
  min: 1,
995
975
  description: "The registered driver key. Defaults to \"memory\"."
996
976
  })),
997
- keys: optionalShape(recordShape(stringShape(), { description: "Table name to its primary-key column." }))
977
+ primary: optionalShape(recordShape(stringShape({ min: 1 }), { description: "Table name to its primary-key column." })),
978
+ indexes: optionalShape(recordShape(arrayShape(arrayShape(stringShape({ min: 1 }), {
979
+ min: 1,
980
+ description: "One nonempty index group of column names."
981
+ }), { description: "Zero or more index groups declared for the table." }), { description: "Table name to its index column groups." })),
982
+ version: optionalShape(numberShape({ description: "The schema version for open-time reconciliation." }))
998
983
  }), objectShape({
999
984
  operation: literalShape(["tables"], { description: "List a database's table names." }),
1000
985
  id: stringShape({
@@ -1013,7 +998,7 @@ var databaseToolShape = unionShape(objectShape({
1013
998
  }),
1014
999
  key: keyShape
1015
1000
  }), objectShape({
1016
- operation: literalShape(["records"], { description: "List rows matching criteria." }),
1001
+ operation: literalShape(["records"], { description: "List rows matching a query." }),
1017
1002
  id: stringShape({
1018
1003
  min: 1,
1019
1004
  description: "The database id."
@@ -1022,9 +1007,9 @@ var databaseToolShape = unionShape(objectShape({
1022
1007
  min: 1,
1023
1008
  description: "The table name."
1024
1009
  }),
1025
- criteria: optionalShape(criteriaShape)
1010
+ query: optionalShape(queryShape)
1026
1011
  }), objectShape({
1027
- operation: literalShape(["count"], { description: "Count rows matching criteria." }),
1012
+ operation: literalShape(["count"], { description: "Count rows matching a query." }),
1028
1013
  id: stringShape({
1029
1014
  min: 1,
1030
1015
  description: "The database id."
@@ -1033,7 +1018,7 @@ var databaseToolShape = unionShape(objectShape({
1033
1018
  min: 1,
1034
1019
  description: "The table name."
1035
1020
  }),
1036
- criteria: optionalShape(criteriaShape)
1021
+ query: optionalShape(queryShape)
1037
1022
  }), objectShape({
1038
1023
  operation: literalShape(["aggregate"], { description: "Compute an aggregate over a column." }),
1039
1024
  id: stringShape({
@@ -1055,7 +1040,7 @@ var databaseToolShape = unionShape(objectShape({
1055
1040
  min: 1,
1056
1041
  description: "The column to aggregate."
1057
1042
  }),
1058
- criteria: optionalShape(criteriaShape)
1043
+ query: optionalShape(queryShape)
1059
1044
  }), objectShape({
1060
1045
  operation: literalShape(["add"], { description: "Insert one or more rows (fails on a duplicate key)." }),
1061
1046
  id: stringShape({
@@ -1101,13 +1086,6 @@ var databaseToolShape = unionShape(objectShape({
1101
1086
  description: "The table name."
1102
1087
  }),
1103
1088
  key: keyShape
1104
- }), objectShape({
1105
- operation: literalShape(["migrate"], { description: "Replace the table layout in place." }),
1106
- id: stringShape({
1107
- min: 1,
1108
- description: "The database id."
1109
- }),
1110
- tables: tableSpecShape
1111
1089
  }), objectShape({
1112
1090
  operation: literalShape(["destroy"], { description: "Drop a database entirely." }),
1113
1091
  id: stringShape({
@@ -1249,7 +1227,7 @@ function workflowTag(id) {
1249
1227
  * @remarks
1250
1228
  * The agent counterpart of {@link workflowTag}: {@link import('./factories.js').createAgentFunction}
1251
1229
  * / {@link import('./factories.js').createWorkflowTool} guard against re-entering an agent or
1252
- * workflow already in the chain (a typed `DEPTH` `WorkflowError`, `@orkestrel/workflow`). The
1230
+ * workflow already in the chain (this package's typed `DEPTH` `ToolboxError`). The
1253
1231
  * `agent:` namespace keeps it distinct from a same-string workflow id.
1254
1232
  *
1255
1233
  * @param name - The agent's identifier / registry name
@@ -1259,23 +1237,89 @@ function agentTag(name) {
1259
1237
  return `agent:${name}`;
1260
1238
  }
1261
1239
  /**
1240
+ * Narrow an unknown value to a valid alternating workflow lineage.
1241
+ *
1242
+ * @param value - The value to inspect
1243
+ * @returns Whether `value` is a unique, nonempty-tagged workflow/agent chain
1244
+ */
1245
+ function isWorkflowLineage(value) {
1246
+ const inspected = attempt(() => {
1247
+ if (!Array.isArray(value)) return false;
1248
+ const seen = /* @__PURE__ */ new Set();
1249
+ for (const [index, tag] of value.entries()) {
1250
+ if (!isString(tag)) return false;
1251
+ const prefix = index % 2 === 0 ? "workflow:" : "agent:";
1252
+ if (!tag.startsWith(prefix) || tag.length === prefix.length || seen.has(tag)) return false;
1253
+ seen.add(tag);
1254
+ }
1255
+ return true;
1256
+ });
1257
+ return inspected.success && inspected.value;
1258
+ }
1259
+ /**
1260
+ * Build a validated, copied, and frozen workflow lineage value.
1261
+ *
1262
+ * @param lineage - The configured chain; omitted means a direct root
1263
+ * @returns An immutable caller-isolated lineage
1264
+ */
1265
+ function lineageOf(lineage = []) {
1266
+ if (!isWorkflowLineage(lineage)) throw new ToolboxError("TOOL", "workflow lineage must alternate unique workflow and agent tags");
1267
+ return Object.freeze([...lineage]);
1268
+ }
1269
+ /**
1270
+ * Append one tag to a workflow lineage and return a frozen copy.
1271
+ *
1272
+ * @param lineage - The valid chain to extend
1273
+ * @param tag - The workflow or agent tag to append
1274
+ * @returns The validated immutable extension
1275
+ */
1276
+ function extendLineage(lineage, tag) {
1277
+ return lineageOf([...lineage, tag]);
1278
+ }
1279
+ /**
1280
+ * Derive the zero-based workflow nesting depth from a valid lineage.
1281
+ *
1282
+ * @param lineage - The workflow/agent chain
1283
+ * @returns Zero for an empty/root lineage, then one per nested workflow
1284
+ */
1285
+ function deriveWorkflowDepth(lineage) {
1286
+ const workflows = lineageOf(lineage).filter((tag) => tag.startsWith("workflow:")).length;
1287
+ return Math.max(0, workflows - 1);
1288
+ }
1289
+ /**
1290
+ * Narrow an unknown callable to Toolbox's frozen contextual agent adapter metadata.
1291
+ *
1292
+ * @param value - The value to inspect
1293
+ * @returns Whether it is a frozen {@link AgentFunction} with a frozen valid lineage
1294
+ */
1295
+ function isAgentFunction(value) {
1296
+ const inspected = attempt(() => {
1297
+ if (typeof value !== "function" || !Object.isFrozen(value)) return false;
1298
+ const category = Reflect.getOwnPropertyDescriptor(value, "category");
1299
+ const lineage = Reflect.getOwnPropertyDescriptor(value, "lineage");
1300
+ return category?.value === "agent" && lineage !== void 0 && "value" in lineage && Object.isFrozen(lineage.value) && isWorkflowLineage(lineage.value);
1301
+ });
1302
+ return inspected.success && inspected.value;
1303
+ }
1304
+ /**
1262
1305
  * Build the plain success summary {@link import('./factories.js').createWorkflowTool} returns on
1263
1306
  * a completed run — the universal tool-handler contract (AGENTS §14): return a plain value on
1264
1307
  * success, appearing identically over BOTH the agent loop and MCP.
1265
1308
  *
1266
1309
  * @remarks
1267
- * The summary is LEAN: the workflow's terminal `status` and the COUNT of settled task results —
1268
- * enough for a caller / model to react without serializing the whole live tree. (It carries no
1269
- * synthetic `id` / `name`: a tool handler has no call id; the `ToolManagerInterface`
1270
- * (`@orkestrel/tool`) supplies the canonical envelope's identity.)
1310
+ * The summary is LEAN: the workflow's terminal `status`, settled-task count, and exact optional
1311
+ * native persistence outcome. It carries no synthetic `id` / `name`: a tool handler has no call
1312
+ * id; the `ToolManagerInterface` (`@orkestrel/tool`) supplies the canonical envelope's identity.
1271
1313
  *
1272
1314
  * @param result - The terminal `WorkflowResult` (`@orkestrel/workflow`) the run produced
1273
- * @returns The plain success summary — `{ status, count }`
1315
+ * @returns The plain success summary — `{ status, count, durable?, fault? }`
1274
1316
  */
1275
1317
  function workflowToolSummary(result) {
1276
1318
  return {
1277
1319
  status: result.status,
1278
- count: result.results.length
1320
+ count: result.results.length,
1321
+ ...result.durable === void 0 ? {} : { durable: result.durable },
1322
+ ...result.fault === void 0 ? {} : { fault: result.fault }
1279
1323
  };
1280
1324
  }
1281
1325
  /**
@@ -1356,16 +1400,20 @@ function completeTaskDraft(task, phaseId, index) {
1356
1400
  * `name` becomes the task's `run` (the behavior-registry key). Ids/names are auto-filled
1357
1401
  * positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates to
1358
1402
  * {@link completeDraft}, so the two lenient surfaces share ONE synthesis path (step `i` → phase
1359
- * `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes the workflow's `name`.
1360
- * The result is a complete definition the caller validates against the STRICT contract before
1361
- * running.
1403
+ * `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes both the workflow's
1404
+ * deterministic id and its name, so named flat workflows retain distinct persistence keys;
1405
+ * omission keeps the shared draft fallback `wf`. The result is a complete definition the caller
1406
+ * validates against the STRICT contract before running.
1362
1407
  *
1363
1408
  * @param flat - The flat steps blob (`{ name?, steps: [{ name }] }`)
1364
1409
  * @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
1365
1410
  */
1366
1411
  function expandSteps(flat) {
1367
1412
  return completeDraft({
1368
- ...flat.name === void 0 ? {} : { name: flat.name },
1413
+ ...flat.name === void 0 ? {} : {
1414
+ id: flat.name,
1415
+ name: flat.name
1416
+ },
1369
1417
  phases: flat.steps.map((step) => ({ tasks: [{ run: step.name }] }))
1370
1418
  });
1371
1419
  }
@@ -1443,13 +1491,13 @@ function isColumnKind(value) {
1443
1491
  return value === "string" || value === "integer" || value === "number" || value === "boolean";
1444
1492
  }
1445
1493
  /**
1446
- * Compile a {@link TableSpec} into the `@orkestrel/database` {@link TablesShape} it configures —
1494
+ * Compile a {@link TableSpec} into the `@orkestrel/database` {@link TableMap} it configures —
1447
1495
  * each {@link ColumnSpec} maps to the matching primitive shaper (`'string'` → `stringShape()`,
1448
1496
  * `'integer'` → `integerShape()`, `'number'` → `numberShape()`, `'boolean'` → `booleanShape()`),
1449
1497
  * wrapped in `optionalShape` when the column declares `optional: true`. Total, pure.
1450
1498
  *
1451
1499
  * @param spec - The small-model-facing table layout
1452
- * @returns The compiled `TablesShape` a `@orkestrel/database` `createDatabase` call accepts
1500
+ * @returns The compiled `TableMap` a `@orkestrel/database` `createDatabase` call accepts
1453
1501
  */
1454
1502
  function expandTables(spec) {
1455
1503
  const tables = {};
@@ -1476,22 +1524,35 @@ function kindShape(kind) {
1476
1524
  }
1477
1525
  /**
1478
1526
  * Narrow an unknown value to a {@link DatabaseDefinition} — a non-empty `id` + `driver`, a
1479
- * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, and an optional
1480
- * `keys` record of strings. The boundary guard a {@link import('./types.js').DefinitionStoreInterface}
1481
- * applies to an untrusted persisted blob before trusting it as a definition (never an `as`).
1527
+ * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
1528
+ * `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
1529
+ * {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
1530
+ * before trusting it as a definition (never an `as`).
1482
1531
  */
1483
1532
  function isDatabaseDefinition(value) {
1484
1533
  if (!isRecord(value)) return false;
1534
+ if ("keys" in value) return false;
1485
1535
  if (!isNonEmptyString(value.id) || !isNonEmptyString(value.driver)) return false;
1486
1536
  if (!isRecord(value.tables)) return false;
1487
1537
  for (const table of Object.values(value.tables)) {
1488
1538
  if (!isRecord(table) || !isRecord(table.columns)) return false;
1489
1539
  for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
1490
1540
  }
1491
- if (value.keys !== void 0) {
1492
- if (!isRecord(value.keys)) return false;
1493
- for (const key of Object.values(value.keys)) if (!isString(key)) return false;
1541
+ if (value.primary !== void 0) {
1542
+ if (!isRecord(value.primary)) return false;
1543
+ for (const key of Object.values(value.primary)) if (!isNonEmptyString(key)) return false;
1494
1544
  }
1545
+ if (value.indexes !== void 0) {
1546
+ if (!isRecord(value.indexes)) return false;
1547
+ for (const groups of Object.values(value.indexes)) {
1548
+ if (!Array.isArray(groups)) return false;
1549
+ for (const group of groups) {
1550
+ if (!Array.isArray(group) || group.length === 0) return false;
1551
+ for (const column of group) if (!isNonEmptyString(column)) return false;
1552
+ }
1553
+ }
1554
+ }
1555
+ if (value.version !== void 0 && !isFiniteNumber(value.version)) return false;
1495
1556
  return true;
1496
1557
  }
1497
1558
  /**
@@ -1635,8 +1696,8 @@ function relationModelOf(manager, name) {
1635
1696
  return manager.model(name);
1636
1697
  }
1637
1698
  /**
1638
- * Normalize the database tool's parsed SERIALIZED criteria into a live `@orkestrel/database`
1639
- * {@link Criteria} — default each condition's OMITTED `connector` to `'and'`.
1699
+ * Normalize the database tool's parsed SERIALIZED query into a live `@orkestrel/database`
1700
+ * {@link QueryInput} — default each condition's OMITTED `connector` to `'and'`.
1640
1701
  *
1641
1702
  * @remarks
1642
1703
  * The wire form ({@link import('./shapers.js').databaseToolShape}) lets a caller drop `connector`
@@ -1644,84 +1705,58 @@ function relationModelOf(manager, name) {
1644
1705
  * `@orkestrel/database` table call accepts always carries one, so this fills the gap. `order` /
1645
1706
  * `limit` / `offset` pass through unchanged. Pure and total.
1646
1707
  *
1647
- * @param criteria - The parsed criteria (or `undefined`)
1648
- * @returns The equivalent live `Criteria`, or `undefined` when `criteria` is `undefined`
1708
+ * @param query - The parsed query (or `undefined`)
1709
+ * @returns The equivalent live `QueryInput`, or `undefined` when `query` is `undefined`
1649
1710
  */
1650
- function criteriaOf(criteria) {
1651
- if (criteria === void 0) return void 0;
1652
- const conditions = criteria.conditions?.map((condition) => ({
1711
+ function queryOf(query) {
1712
+ if (query === void 0) return void 0;
1713
+ const conditions = query.conditions?.map((condition) => ({
1653
1714
  ...condition,
1654
1715
  connector: condition.connector ?? "and"
1655
1716
  }));
1656
1717
  return {
1657
1718
  ...conditions === void 0 ? {} : { conditions },
1658
- ...criteria.order === void 0 ? {} : { order: criteria.order },
1659
- ...criteria.limit === void 0 ? {} : { limit: criteria.limit },
1660
- ...criteria.offset === void 0 ? {} : { offset: criteria.offset }
1719
+ ...query.order === void 0 ? {} : { order: query.order },
1720
+ ...query.limit === void 0 ? {} : { limit: query.limit },
1721
+ ...query.offset === void 0 ? {} : { offset: query.offset }
1661
1722
  };
1662
1723
  }
1663
1724
  /**
1664
- * Clamp a `'records'` call's criteria to a row cap, and build the PROBE criteria the caller reads
1725
+ * Clamp a `'records'` call's query to a row cap, and build the PROBE query the caller reads
1665
1726
  * with — the pure leaf {@link import('./factories.js').createDatabaseTool}'s `'records'` operation
1666
1727
  * uses to detect truncation without a separate `count` round trip.
1667
1728
  *
1668
1729
  * @remarks
1669
- * The effective limit is `min(criteria?.limit ?? cap, cap)`, floored at `0` (so a caller can never
1670
- * exceed the configured cap by supplying a larger `criteria.limit`). The returned probe criteria
1730
+ * The effective limit is `min(query?.limit ?? cap, cap)`, floored at `0` (so a caller can never
1731
+ * exceed the configured cap by supplying a larger `query.limit`). The returned probe query
1671
1732
  * requests ONE MORE row than the effective limit (`limit: effective + 1`) — if storage returns
1672
1733
  * that many, the caller knows the true result was truncated (`rows.length > effective`) and slices
1673
1734
  * back down to `effective` before returning.
1674
1735
  *
1675
1736
  * @example
1676
1737
  * ```ts
1677
- * import { clampCriteria } from '@src/core'
1738
+ * import { clampQuery } from '@src/core'
1678
1739
  *
1679
- * const { criteria, limit } = clampCriteria(undefined, 100)
1680
- * // limit === 100, criteria.limit === 101 — a probe fetching one extra row
1681
- * const rows = await table.records(criteria)
1740
+ * const { query, limit } = clampQuery(undefined, 100)
1741
+ * // limit === 100, query.limit === 101 — a probe fetching one extra row
1742
+ * const rows = await table.records(query)
1682
1743
  * const truncated = rows.length > limit // true when storage had more than `limit` rows
1683
1744
  * ```
1684
1745
  *
1685
- * @param criteria - The live criteria to clamp (or `undefined`)
1746
+ * @param query - The live query to clamp (or `undefined`)
1686
1747
  * @param cap - The row-count ceiling
1687
- * @returns The PROBE criteria (`limit` bumped by one) and the effective `limit`
1748
+ * @returns The PROBE query (`limit` bumped by one) and the effective `limit`
1688
1749
  */
1689
- function clampCriteria(criteria, cap) {
1690
- const limit = Math.max(0, Math.min(criteria?.limit ?? cap, cap));
1750
+ function clampQuery(query, cap) {
1751
+ const limit = Math.max(0, Math.min(query?.limit ?? cap, cap));
1691
1752
  return {
1692
- criteria: {
1693
- ...criteria,
1753
+ query: {
1754
+ ...query,
1694
1755
  limit: limit + 1
1695
1756
  },
1696
1757
  limit
1697
1758
  };
1698
1759
  }
1699
- /** Map a column NAME + its live `@orkestrel/database` `ContractShape` to a {@link ColumnSchema} — the leaf {@link tableSchema} maps over. */
1700
- function columnSchema(name, shape) {
1701
- return {
1702
- name,
1703
- type: shapeToColumnType(shape),
1704
- nullable: shape.type === "optional" || shape.type === "nullable"
1705
- };
1706
- }
1707
- /**
1708
- * Build one {@link TableSchema} from a table NAME and its `@orkestrel/database` `TableExport` —
1709
- * the "deployed" schema shape `DatabaseInterface.migrate` diffs against, derived from a LIVE
1710
- * handle's `export()` rather than a re-declared {@link TableSpec}, so it works for ANY handle
1711
- * (config-tracked or caller-supplied).
1712
- *
1713
- * @param name - The table name
1714
- * @param table - The table's `TableExport` (`{ key, columns }`, `@orkestrel/database`)
1715
- * @returns The equivalent {@link TableSchema} (`indexes` empty — this package declares none)
1716
- */
1717
- function tableSchema(name, table) {
1718
- return {
1719
- name,
1720
- primary: table.key,
1721
- columns: Object.entries(table.columns).map(([column, shape]) => columnSchema(column, shape)),
1722
- indexes: []
1723
- };
1724
- }
1725
1760
  //#endregion
1726
1761
  //#region src/core/stores/MemoryDefinitionStore.ts
1727
1762
  /**
@@ -1732,7 +1767,8 @@ function tableSchema(name, table) {
1732
1767
  *
1733
1768
  * @remarks
1734
1769
  * A plain `Map<string, DatabaseDefinition>` (AGENTS §21 — the definition is already pure,
1735
- * self-contained CONFIG-only JSON, so no encoding is needed for the memory tier). There is NO
1770
+ * self-contained CONFIG-only JSON, so no encoding is needed for the memory tier). Values are
1771
+ * structured-cloned on both write and read, keeping caller mutation outside the store. There is NO
1736
1772
  * idle-TTL and NO eviction: a persisted definition lives until an explicit `delete`. A durable
1737
1773
  * backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching a
1738
1774
  * consumer — its driver-pluggable twin is
@@ -1759,10 +1795,11 @@ function tableSchema(name, table) {
1759
1795
  var MemoryDefinitionStore = class {
1760
1796
  #definitions = /* @__PURE__ */ new Map();
1761
1797
  get(id) {
1762
- return Promise.resolve(this.#definitions.get(id));
1798
+ const definition = this.#definitions.get(id);
1799
+ return Promise.resolve(definition === void 0 ? void 0 : structuredClone(definition));
1763
1800
  }
1764
1801
  set(definition) {
1765
- this.#definitions.set(definition.id, definition);
1802
+ this.#definitions.set(definition.id, structuredClone(definition));
1766
1803
  return Promise.resolve();
1767
1804
  }
1768
1805
  delete(id) {
@@ -1851,27 +1888,27 @@ var DatabaseDefinitionStore = class {
1851
1888
  * ```ts
1852
1889
  * import { DatabaseResolver } from '@orkestrel/toolbox'
1853
1890
  *
1854
- * const resolver = new DatabaseResolver(handles, drivers, key, store)
1891
+ * const resolver = new DatabaseResolver(handles, drivers, generator, store)
1855
1892
  * const database = await resolver.resolve('shop')
1856
1893
  * ```
1857
1894
  */
1858
1895
  var DatabaseResolver = class {
1859
1896
  #handles;
1860
1897
  #drivers;
1861
- #key;
1898
+ #generator;
1862
1899
  #store;
1863
1900
  /**
1864
1901
  * Create a database resolver over the tool's live state and optional definition store.
1865
1902
  *
1866
1903
  * @param handles - Initial live database handles cached by id
1867
1904
  * @param drivers - Driver factories keyed by definition driver name
1868
- * @param key - Key generator supplied to newly created databases
1905
+ * @param generator - Optional key generator supplied to newly created databases
1869
1906
  * @param store - Optional persistent definition store
1870
1907
  */
1871
- constructor(handles, drivers, key, store) {
1908
+ constructor(handles, drivers, generator, store) {
1872
1909
  this.#handles = new Map(handles);
1873
1910
  this.#drivers = drivers;
1874
- this.#key = key;
1911
+ this.#generator = generator;
1875
1912
  this.#store = store;
1876
1913
  }
1877
1914
  /**
@@ -1931,8 +1968,11 @@ var DatabaseResolver = class {
1931
1968
  const handle = createDatabase({
1932
1969
  driver: factory(),
1933
1970
  tables: expandTables(definition.tables),
1934
- ...definition.keys === void 0 ? {} : { keys: definition.keys },
1935
- key: this.#key
1971
+ name: id,
1972
+ ...definition.primary === void 0 ? {} : { primary: definition.primary },
1973
+ ...definition.indexes === void 0 ? {} : { indexes: definition.indexes },
1974
+ ...definition.version === void 0 ? {} : { version: definition.version },
1975
+ ...this.#generator === void 0 ? {} : { generator: this.#generator }
1936
1976
  });
1937
1977
  this.set(id, handle);
1938
1978
  return handle;
@@ -1948,17 +1988,13 @@ var DatabaseResolver = class {
1948
1988
  * adapter that lets a `function`-form task run a `@orkestrel/tool` tool BY NAME.
1949
1989
  *
1950
1990
  * @remarks
1951
- * OWNED here now (ported from `@orkestrel/workflow`). Composes into a caller's
1952
- * `WorkflowOptions.functions` registry like any other behavior
1991
+ * Composes into a caller's `WorkflowOptions.functions` registry like any other behavior
1953
1992
  * (`{ publish: createToolFunction(tools, 'publish') }`); the pure workflow runner has no
1954
1993
  * knowledge of tools itself. The returned function executes `name` against `tools` with the
1955
- * task's `controller.input` as the call arguments, id-correlated to the task's own id. A
1956
- * `ToolManagerInterface.execute` (`@orkestrel/tool`) NEVER throws (a handler throw is isolated
1957
- * into `result.error`), so a failing tool is surfaced here as a THROWN `Error` carrying the
1958
- * original message as `cause` — the leaf `fail`s, honouring `bail`. An UNREGISTERED tool name is
1959
- * a programmer error (an explicit binding to a name that doesn't exist) — unlike the engine's
1960
- * own silent auto-complete of an unresolved task handler, this THROWS a typed `TOOL`
1961
- * `WorkflowError` (`@orkestrel/workflow`).
1994
+ * task's `controller.input` as the call arguments. The adapter resolves the live tool, calls its
1995
+ * `execute` method directly, and deep-gates the returned unknown through `parseJSONValue`.
1996
+ * Genuine execution throws retain identity. An unregistered name or non-JSON return throws this
1997
+ * package's typed `TOOL` `ToolboxError`.
1962
1998
  *
1963
1999
  * @param tools - The `ToolManagerInterface` (`@orkestrel/tool`) the named tool is registered on
1964
2000
  * @param name - The registered tool's name
@@ -1977,47 +2013,40 @@ var DatabaseResolver = class {
1977
2013
  * ```
1978
2014
  */
1979
2015
  function createToolFunction(tools, name) {
2016
+ if (name === "workflow") throw new ToolboxError("TOOL", `tool '${WORKFLOW_TOOL_NAME}' is reserved for live workflows`, { tool: name });
1980
2017
  return async (controller) => {
1981
- if (tools.tool(name) === void 0) throw new WorkflowError("TOOL", `tool '${name}' is not registered`, { tool: name });
1982
- const result = await tools.execute({
1983
- id: controller.task.id,
1984
- name,
1985
- arguments: controller.input
1986
- });
1987
- if (result.error !== void 0) throw new Error(result.error, { cause: result.error });
1988
- return result.value;
2018
+ const tool = tools.tool(name);
2019
+ if (tool === void 0) throw new ToolboxError("TOOL", `tool '${name}' is not registered`, { tool: name });
2020
+ const result = parseJSONValue(await tool.execute(controller.input));
2021
+ if (result === void 0) throw new ToolboxError("TOOL", `tool '${name}' returned a non-JSON value`, { tool: name });
2022
+ return result;
1989
2023
  };
1990
2024
  }
1991
2025
  /**
1992
2026
  * Wrap a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction}
1993
- * (`@orkestrel/workflow`) — the OPT-IN adapter that runs the agent to a settled result, folding
1994
- * a nested workflow-authoring depth / cycle guard into its own closure.
2027
+ * (`@orkestrel/workflow`) — the opt-in adapter that runs the agent to a settled result and carries
2028
+ * immutable lineage metadata for contextual Toolbox composition.
1995
2029
  *
1996
2030
  * @remarks
1997
- * OWNED here now (ported from `@orkestrel/workflow`). Composes into a caller's
1998
- * `WorkflowOptions.functions` registry like any other behavior; the pure workflow runner has no
1999
- * knowledge of agents itself. Before running the agent, the depth/cycle guard REJECTS the call
2000
- * (a THROWN typed `DEPTH` `WorkflowError`, which the leaf `fail`s) when running it would push a
2001
- * nested chain past {@link import('./constants.js').MAX_WORKFLOW_DEPTH}, OR when this agent is
2002
- * already an ancestor (a cycle). When {@link import('./types.js').AgentFunctionOptions.runner}
2003
- * is supplied, the adapter BINDS a depth/cycle-aware {@link createWorkflowTool} onto the agent's
2004
- * `context.tools` (the propagation seam) — closed over `depth` and the extended ancestry (the
2005
- * tool itself computes `depth + 1` internally) — so the agent can author + run a NESTED workflow
2006
- * through it; the wrapped default is the CURRENT task's own workflow id (used only on a no-args
2007
- * tool call). The task's cancellation folds into the agent run: an already-aborted
2008
- * `controller.signal` cancels the agent up front; otherwise a one-shot listener fires
2009
- * `agent.abort(reason)` when the task cancels, removed in `finally`. `agent.generate()` resolves
2010
- * a partial `AgentResult` on a cancel (never rejects), returned as the task's completed value.
2011
- *
2012
- * A bound agent is effectively SINGLE-RUN: `context.tools.add` binds one `ToolInterface` under
2013
- * the fixed {@link import('./constants.js').WORKFLOW_TOOL_NAME}, and `agent.generate()` /
2014
- * `agent.abort()` are per-agent state. Two CONCURRENT tasks sharing the SAME `agent` instance
2015
- * race on that one tool binding (last-write-wins) and on generate/abort — give each concurrent
2016
- * task its OWN agent instance.
2031
+ * Composes into a caller's registry like any other behavior; the pure workflow runner has no
2032
+ * knowledge of agents itself. A root adapter derives its workflow lineage from the controller,
2033
+ * while a contextual adapter requires an exact match. Repeated agents, repeated workflows, and
2034
+ * targets deeper than {@link import('./constants.js').MAX_WORKFLOW_DEPTH} are rejected before
2035
+ * agent or runner activity. When {@link import('./types.js').AgentFunctionOptions.runner} is
2036
+ * supplied, the adapter binds a recursion-safe {@link createWorkflowTool} onto the agent's
2037
+ * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The
2038
+ * task's cancellation propagates through the Agent's native per-run signal seam:
2039
+ * `agent.generate({ signal: controller.signal })` resolves a partial `AgentResult` on a cancel
2040
+ * (never rejects), projected onto its exact JSON fields.
2041
+ *
2042
+ * A bound agent is single-run. A concurrent branch sharing the same real agent is rejected before
2043
+ * its fixed {@link import('./constants.js').WORKFLOW_TOOL_NAME} binding can be replaced. Toolbox
2044
+ * cannot serialize opaque external wrappers that hide or bypass the raw agent registry; hosts
2045
+ * must give such concurrent work distinct agents.
2017
2046
  *
2018
2047
  * @param agent - The live `AgentInterface` to run
2019
- * @param options - The nested-workflow binding + depth/cycle bookkeeping (see {@link import('./types.js').AgentFunctionOptions})
2020
- * @returns A {@link WorkflowFunction} that runs `agent` to its settled result
2048
+ * @param options - The nested-workflow composition and lineage options
2049
+ * @returns A frozen {@link AgentFunction} that runs `agent` to its settled result
2021
2050
  *
2022
2051
  * @example
2023
2052
  * ```ts
@@ -2030,21 +2059,32 @@ function createToolFunction(tools, name) {
2030
2059
  * ```
2031
2060
  */
2032
2061
  function createAgentFunction(agent, options) {
2033
- return async (controller) => {
2034
- const depth = options?.depth ?? 0;
2035
- const ancestry = options?.ancestry ?? [];
2036
- if (depth + 1 > 8) throw new WorkflowError("DEPTH", `agent '${agent.id}' exceeds max workflow depth`, {
2062
+ const lineage = lineageOf(options?.lineage);
2063
+ if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "agent function lineage must end with a workflow tag");
2064
+ const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
2065
+ const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
2066
+ return Object.freeze(Object.assign(async (controller) => {
2067
+ const workflow = workflowTag(controller.task.phase.workflow.id);
2068
+ const current = lineage.length === 0 ? extendLineage(lineage, workflow) : lineage;
2069
+ if (current.at(-1) !== workflow) throw new ToolboxError("DEPTH", "agent function workflow does not match its lineage", {
2070
+ workflow,
2071
+ lineage: [...current]
2072
+ });
2073
+ const depth = deriveWorkflowDepth(current);
2074
+ if (depth > 8) throw new ToolboxError("DEPTH", `agent '${agent.id}' exceeds max workflow depth`, {
2037
2075
  agent: agent.id,
2038
2076
  depth,
2039
2077
  max: 8
2040
2078
  });
2041
2079
  const tag = agentTag(agent.id);
2042
- if (ancestry.includes(tag)) throw new WorkflowError("DEPTH", `agent '${agent.id}' is already an ancestor (cycle)`, {
2080
+ if (current.includes(tag)) throw new ToolboxError("DEPTH", `agent '${agent.id}' is already an ancestor (cycle)`, {
2043
2081
  agent: agent.id,
2044
- ancestry: [...ancestry]
2082
+ lineage: [...current]
2045
2083
  });
2084
+ const nested = extendLineage(current, tag);
2046
2085
  const runner = options?.runner;
2047
2086
  if (runner !== void 0) {
2087
+ if (agent.status === "running") throw new ToolboxError("TOOL", `agent '${agent.id}' is already running`, { agent: agent.id });
2048
2088
  const workflowId = controller.task.phase.workflow.id;
2049
2089
  const wrapped = {
2050
2090
  id: workflowId,
@@ -2052,50 +2092,77 @@ function createAgentFunction(agent, options) {
2052
2092
  phases: []
2053
2093
  };
2054
2094
  agent.context.tools.add(createWorkflowTool(wrapped, runner, {
2055
- depth,
2056
- ancestry: [...ancestry, tag]
2095
+ lineage: nested,
2096
+ ...functions === void 0 ? {} : { functions },
2097
+ ...agents === void 0 ? {} : { agents },
2098
+ ...options?.store === void 0 ? {} : { store: options.store }
2057
2099
  }));
2058
2100
  }
2059
- const signal = controller.signal;
2060
- const onAbort = { handleEvent() {
2061
- agent.abort(signal.reason);
2062
- } };
2063
- if (signal.aborted) agent.abort(signal.reason);
2064
- else signal.addEventListener("abort", onAbort, { once: true });
2065
- try {
2066
- return await agent.generate();
2067
- } finally {
2068
- signal.removeEventListener("abort", onAbort);
2069
- }
2070
- };
2101
+ const generated = agent.generate({ signal: controller.signal });
2102
+ const result = agentResultToJSON(await generated);
2103
+ if (result === void 0) throw new ToolboxError("TOOL", `agent '${agent.id}' returned a non-JSON result`, { agent: agent.id });
2104
+ return result;
2105
+ }, {
2106
+ category: "agent",
2107
+ lineage
2108
+ }));
2071
2109
  }
2072
2110
  /**
2073
- * Compile the LENIENT workflow DRAFT contract — identical to `createWorkflowContract`
2074
- * (`@orkestrel/workflow`) EXCEPT `id` and `name` are OPTIONAL at all three levels (workflow /
2075
- * phase / task), so a small model can omit the six identity strings.
2111
+ * Compose opaque host functions and raw agents into one immutable workflow registry.
2076
2112
  *
2077
2113
  * @remarks
2078
- * The widened authoring surface {@link createWorkflowTool} parses an authored blob through
2079
- * before {@link import('./helpers.js').completeDraft} fills the missing ids/names. It does NOT
2080
- * relax the canonical contract — `createWorkflowContract` (`@orkestrel/workflow`) stays
2081
- * byte-for-byte unchanged and STRICT, and the completed draft is re-validated against THAT
2082
- * strict gate before running (soundness preserved). A PROVIDED `id` / `name` still carries
2083
- * `minLength: 1`, so an explicitly-empty `id: ''` is REJECTED (parses to `undefined`), never
2084
- * auto-filled — keeping "garbage" distinct from "omitted". `run` stays optional (a plain name
2085
- * string).
2114
+ * This is the target-aware composition boundary used by direct runner consumers and
2115
+ * {@link createWorkflowTool}. Opaque leaves are copied unchanged, marked agent adapters are
2116
+ * rejected from that channel, raw agents are rebound to the supplied lineage, collisions fail
2117
+ * closed, and the returned null-prototype record prevents inherited names from becoming phantom
2118
+ * Workflow handlers.
2086
2119
  *
2087
- * @returns The compiled {@link import('./types.js').WorkflowDraft} contract
2120
+ * @param runner - The native runner propagated into contextual agent adapters
2121
+ * @param options - Target lineage, opaque functions, raw agents, and native store
2122
+ * @returns A frozen null-prototype snapshot of the target workflow's callable registry
2088
2123
  *
2089
2124
  * @example
2090
2125
  * ```ts
2091
- * import { createWorkflowDraftContract, completeDraft } from '@src/core'
2126
+ * import { createWorkflowFunctions } from '@src/core'
2127
+ * import { createWorkflowRunner } from '@orkestrel/workflow'
2092
2128
  *
2093
- * const draft = createWorkflowDraftContract()
2094
- * const parsed = draft.parse({ phases: [{ tasks: [{ run: 'compile' }] }] })
2095
- * const definition = parsed && completeDraft(parsed) // ids/names filled positionally
2096
- * draft.parse({ id: '', phases: [] }) // undefined — an explicit empty id is rejected
2129
+ * const runner = createWorkflowRunner()
2130
+ * const functions = createWorkflowFunctions(runner, {
2131
+ * functions: { publish: async () => 'published' },
2132
+ * agents: { review: reviewAgent },
2133
+ * })
2134
+ * await runner.execute(definition, { functions })
2097
2135
  * ```
2098
2136
  */
2137
+ function createWorkflowFunctions(runner, options) {
2138
+ const lineage = lineageOf(options?.lineage);
2139
+ if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "workflow functions lineage must end with a workflow tag");
2140
+ const functions = options?.functions === void 0 ? Object.freeze({}) : Object.freeze({ ...options.functions });
2141
+ const agents = options?.agents === void 0 ? Object.freeze({}) : Object.freeze({ ...options.agents });
2142
+ const composed = {};
2143
+ Object.setPrototypeOf(composed, null);
2144
+ for (const [name, fn] of Object.entries(functions)) {
2145
+ if (typeof fn !== "function") throw new ToolboxError("TOOL", `workflow function '${name}' is not callable`, { function: name });
2146
+ if (isAgentFunction(fn)) throw new ToolboxError("TOOL", `agent function '${name}' must be supplied through agents`, { function: name });
2147
+ composed[name] = fn;
2148
+ }
2149
+ for (const [name, agent] of Object.entries(agents)) {
2150
+ if (Object.hasOwn(composed, name)) throw new ToolboxError("TOOL", `workflow function '${name}' collides with an agent`, { function: name });
2151
+ composed[name] = createAgentFunction(agent, {
2152
+ runner,
2153
+ lineage,
2154
+ functions,
2155
+ agents,
2156
+ ...options?.store === void 0 ? {} : { store: options.store }
2157
+ });
2158
+ }
2159
+ return Object.freeze(composed);
2160
+ }
2161
+ /**
2162
+ * Compile the lenient workflow draft contract used by {@link createWorkflowTool}.
2163
+ *
2164
+ * @returns The compiled {@link import('./types.js').WorkflowDraft} contract
2165
+ */
2099
2166
  function createWorkflowDraftContract() {
2100
2167
  return createContract(workflowDraftShape);
2101
2168
  }
@@ -2103,19 +2170,16 @@ function createWorkflowDraftContract() {
2103
2170
  * Wrap a {@link WorkflowDefinition} as an LLM-callable tool — it ADVERTISES the SIMPLE flat
2104
2171
  * authoring shape (`{ name?, steps: [{ name }] }`) as its `parameters` so even a small model can
2105
2172
  * author a complete tree, and its handler EXPANDS / COMPLETES the authored blob, validates it
2106
- * against the STRICT contract, runs it through `runner`, and, when
2107
- * {@link import('./types.js').WorkflowToolOptions.store} is supplied, PERSISTS each executed
2108
- * workflow's final snapshot after the run settles.
2173
+ * against the STRICT contract, and runs it through `runner`, forwarding the caller's optional
2174
+ * named functions and native checkpoint store.
2109
2175
  *
2110
2176
  * @remarks
2111
2177
  * A plain `ToolManagerInterface`-compatible tool (`@orkestrel/tool`), reproducing
2112
- * `@orkestrel/workflow`'s former call contract exactly (flat / draft / full authoring forms, the
2113
- * strict soundness gate, the depth/cycle guard). It is ALSO the propagation carrier
2178
+ * Toolbox's flat / draft / full authoring contract, strict soundness gate, and lineage guard.
2179
+ * It is ALSO the propagation carrier
2114
2180
  * {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
2115
- * handler receives ONLY the model-supplied `args` (no ambient context, no signal), the run's
2116
- * depth + ancestry are CLOSED OVER at bind time via {@link import('./types.js').WorkflowToolOptions},
2117
- * and the handler enforces the SAME depth / cycle guard itself before running the nested
2118
- * workflow at `depth + 1` with the extended ancestry.
2181
+ * handler receives only the model-supplied `args`, the immutable lineage is closed over at bind
2182
+ * time. The authored target is appended and its zero-based depth derived from workflow tags.
2119
2183
  *
2120
2184
  * **Widened authoring surface (additive — the canonical contract + runner stay STRICT and
2121
2185
  * unchanged).** A 2B model reliably CALLS the tool but cannot reliably emit the full four-level
@@ -2130,17 +2194,17 @@ function createWorkflowDraftContract() {
2130
2194
  * super-set.
2131
2195
  *
2132
2196
  * The universal tool-handler contract (AGENTS §14): returns the plain run summary
2133
- * (`{ status, count }`) on success, THROWS a typed `WorkflowError` (`@orkestrel/workflow`) on
2134
- * every failure path — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
2135
- * (`DEPTH`). The `ToolManagerInterface` isolates every throw into the canonical tool result's
2136
- * top-level `error`, so nothing escapes the run. `options.depth` / `options.ancestry` are the
2137
- * propagation carrier across a workflow → agent → workflow chain; `options.store` is this
2138
- * package's ADDITION — the persisted snapshot is retrievable via the store afterwards (a caller
2139
- * restores it through `@orkestrel/workflow`'s own `Workflow.restore` / store-backed factories).
2197
+ * (`{ status, count, durable?, fault? }`) on success, THROWS a typed `ToolboxError` on Toolbox
2198
+ * boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
2199
+ * (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
2200
+ * top-level `error`, so nothing escapes the run. `options.lineage` is the propagation carrier
2201
+ * across a workflow → agent → workflow chain. Opaque `functions` remain host leaves; raw `agents`
2202
+ * are contextually adapted through {@link createWorkflowFunctions}; native checkpoint persistence
2203
+ * makes the final snapshot retrievable without a second Toolbox write.
2140
2204
  *
2141
2205
  * @param definition - The workflow the tool runs when called with no authored args
2142
2206
  * @param runner - The `WorkflowRunnerInterface` (`@orkestrel/workflow`) that executes the (nested) workflow
2143
- * @param options - Depth/ancestry bookkeeping plus the optional durable store (see {@link import('./types.js').WorkflowToolOptions})
2207
+ * @param options - Lineage-aware functions, agents, and optional native store
2144
2208
  * @returns A `ToolInterface` (named {@link import('./constants.js').WORKFLOW_TOOL_NAME}) whose
2145
2209
  * `parameters` advertise the flat authoring schema
2146
2210
  *
@@ -2154,15 +2218,17 @@ function createWorkflowDraftContract() {
2154
2218
  * const store = createMemoryWorkflowStore()
2155
2219
  * const tool = createWorkflowTool(definition, runner, { store })
2156
2220
  * const tools = createToolManager()
2157
- * tools.add(tool) // authored runs are now persisted to `store` on settle
2221
+ * tools.add(tool) // authored runs now use native checkpoints through `store`
2158
2222
  * ```
2159
2223
  */
2160
2224
  function createWorkflowTool(definition, runner, options) {
2161
2225
  const strict = createWorkflowContract();
2162
2226
  const draft = createWorkflowDraftContract();
2163
2227
  const steps = createContract(workflowStepsShape);
2164
- const depth = options?.depth ?? 0;
2165
- const ancestry = options?.ancestry ?? [];
2228
+ const lineage = lineageOf(options?.lineage);
2229
+ if (lineage.length > 0 && !lineage.at(-1)?.startsWith("agent:")) throw new ToolboxError("TOOL", "workflow tool lineage must end with an agent tag");
2230
+ const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
2231
+ const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
2166
2232
  const store = options?.store;
2167
2233
  const parameters = schemaToParameters(steps.schema);
2168
2234
  return createTool({
@@ -2171,29 +2237,41 @@ function createWorkflowTool(definition, runner, options) {
2171
2237
  summary: WORKFLOW_TOOL_SUMMARY,
2172
2238
  ...parameters === void 0 ? {} : { parameters },
2173
2239
  async execute(args) {
2240
+ const cloned = attempt(() => cloneJSONRecord(args));
2241
+ if (!cloned.success) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
2242
+ const owned = cloned.value;
2174
2243
  let target;
2175
- if (Object.keys(args).length === 0) target = definition;
2176
- else if (Array.isArray(args.steps)) {
2177
- const flat = steps.parse(args);
2244
+ if (Object.keys(owned).length === 0) target = definition;
2245
+ else if (Array.isArray(owned.steps)) {
2246
+ const flat = steps.parse(owned);
2178
2247
  target = flat === void 0 ? void 0 : expandSteps(flat);
2179
2248
  } else {
2180
- const parsed = draft.parse(args);
2249
+ const parsed = draft.parse(owned);
2181
2250
  target = parsed === void 0 ? void 0 : completeDraft(parsed);
2182
2251
  }
2183
- if (target === void 0 || !strict.is(target)) throw new WorkflowError("TOOL", "malformed workflow definition", { workflow: definition.id });
2184
- if (depth + 1 > 8) throw new WorkflowError("DEPTH", `nested workflow exceeds max depth 8`, {
2252
+ if (target === void 0 || !strict.is(target)) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
2253
+ const tag = workflowTag(target.id);
2254
+ if (lineage.includes(tag)) throw new ToolboxError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
2255
+ workflow: target.id,
2256
+ lineage: [...lineage]
2257
+ });
2258
+ const targetLineage = extendLineage(lineage, tag);
2259
+ const depth = deriveWorkflowDepth(targetLineage);
2260
+ if (depth > 8) throw new ToolboxError("DEPTH", `nested workflow exceeds max depth 8`, {
2185
2261
  workflow: target.id,
2186
2262
  depth,
2187
2263
  max: 8
2188
2264
  });
2189
- const tag = workflowTag(target.id);
2190
- if (ancestry.includes(tag)) throw new WorkflowError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
2191
- workflow: target.id,
2192
- ancestry: [...ancestry]
2265
+ const registry = functions === void 0 && agents === void 0 ? void 0 : createWorkflowFunctions(runner, {
2266
+ lineage: targetLineage,
2267
+ ...functions === void 0 ? {} : { functions },
2268
+ ...agents === void 0 ? {} : { agents },
2269
+ ...store === void 0 ? {} : { store }
2193
2270
  });
2194
- const result = await runner.execute(target);
2195
- if (store !== void 0) await store.set(result.workflow.snapshot());
2196
- return workflowToolSummary(result);
2271
+ return workflowToolSummary(await runner.execute(target, {
2272
+ ...registry === void 0 ? {} : { functions: registry },
2273
+ ...store === void 0 ? {} : { store }
2274
+ }));
2197
2275
  }
2198
2276
  });
2199
2277
  }
@@ -2202,12 +2280,13 @@ function createWorkflowTool(definition, runner, options) {
2202
2280
  * 13-op union ({@link import('./shapers.js').workspaceToolShape}) as its `parameters`, and its
2203
2281
  * handler PARSES the model-supplied args against that contract and DISPATCHES the matched
2204
2282
  * operation against the manager's ACTIVE workspace (the registry ops drive the manager itself),
2205
- * returning the plain result (throwing a typed `WorkspaceError`, `@orkestrel/agent`, on
2206
- * failure). EITHER drives a caller-supplied {@link WorkspaceToolOptions.manager} directly, OR
2207
- * constructs a fresh `WorkspaceManagerInterface` (`@orkestrel/agent`) over
2208
- * {@link import('./types.js').WorkspaceToolOptions.store} (via `@orkestrel/agent`'s
2209
- * `createWorkspaceManager`); neither given constructs a manager backed by `@orkestrel/agent`'s
2210
- * in-memory store default.
2283
+ * returning the plain result. A malformed operation throws this package's `ToolboxError`; a
2284
+ * genuine workspace-domain failure propagates `@orkestrel/workspace`'s typed `WorkspaceError`.
2285
+ * EITHER drives a caller-supplied {@link WorkspaceToolOptions.manager} directly, OR
2286
+ * constructs a fresh `WorkspaceManagerInterface` (`@orkestrel/workspace`) over
2287
+ * {@link import('./types.js').WorkspaceToolOptions.store} (via `@orkestrel/workspace`'s
2288
+ * `createWorkspaceManager`); neither given constructs a manager backed by
2289
+ * `@orkestrel/workspace`'s in-memory store default.
2211
2290
  *
2212
2291
  * @remarks
2213
2292
  * MANAGER-DRIVEN: every edit / read op (read / list / has / search / replace / write / splice /
@@ -2225,12 +2304,12 @@ function createWorkflowTool(definition, runner, options) {
2225
2304
  * `false`), never creating one and never throwing.
2226
2305
  *
2227
2306
  * The handler conforms to the universal tool-handler contract (AGENTS §14): it `contract.parse`s
2228
- * the args, THROWS a `TOOL` `WorkspaceError` when no operation arm matched (a malformed / unknown
2307
+ * the args, THROWS a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
2229
2308
  * operation), else `switch`es on `op.operation` and RETURNS the plain result — letting a
2230
2309
  * `WorkspaceError` raised by the live workspace (`MODALITY` / `PATTERN` / `RANGE`) PROPAGATE
2231
2310
  * uncaught. The range edit is the FLAT `'splice'` op: its four flat caret integers are
2232
- * reassembled into a `Range` (`@orkestrel/agent`) by `rangeOf` and fed to the workspace's ranged
2233
- * `write`.
2311
+ * reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
2312
+ * ranged `write`.
2234
2313
  *
2235
2314
  * @param options - `manager` (drive directly) OR `store` (build a manager over it); neither ⇒
2236
2315
  * an in-memory-backed manager (see {@link import('./types.js').WorkspaceToolOptions})
@@ -2257,7 +2336,7 @@ function createWorkspaceTool(options) {
2257
2336
  ...parameters === void 0 ? {} : { parameters },
2258
2337
  execute(args) {
2259
2338
  const op = contract.parse(args);
2260
- if (op === void 0) throw new WorkspaceError("TOOL", `unknown or malformed operation`, { args });
2339
+ if (op === void 0) throw new ToolboxError("TOOL", "unknown or malformed operation", { args });
2261
2340
  if (op.operation === "workspaces") {
2262
2341
  const activeId = manager.active?.id;
2263
2342
  return manager.workspaces().map((workspace) => ({
@@ -2290,12 +2369,12 @@ function createWorkspaceTool(options) {
2290
2369
  case "has": return active?.has(op.path) ?? false;
2291
2370
  case "search": return active?.search(op.query, {
2292
2371
  ...op.regex === void 0 ? {} : { regex: op.regex },
2293
- ...op.exact === void 0 ? {} : { exact: op.exact },
2372
+ ...op.sensitive === void 0 ? {} : { sensitive: op.sensitive },
2294
2373
  ...op.limit === void 0 ? {} : { limit: op.limit }
2295
2374
  }) ?? [];
2296
2375
  case "replace": return (active ?? manager.add()).replace(op.query, op.replacement, {
2297
2376
  ...op.regex === void 0 ? {} : { regex: op.regex },
2298
- ...op.exact === void 0 ? {} : { exact: op.exact },
2377
+ ...op.sensitive === void 0 ? {} : { sensitive: op.sensitive },
2299
2378
  ...op.limit === void 0 ? {} : { limit: op.limit }
2300
2379
  });
2301
2380
  case "write": {
@@ -2712,20 +2791,18 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2712
2791
  * `TOOL` {@link import('./errors.js').ToolboxError}). When a `store` is configured, `'create'`
2713
2792
  * persists the new {@link import('./types.js').DatabaseDefinition} and `'destroy'` deletes it.
2714
2793
  *
2715
- * `'migrate'` re-declares a LIVE handle's tables via `DatabaseInterface.import` (the SAME driver
2716
- * and storage, a NEW typed view) and calls its `migrate` against the OLD deployed schema —
2717
- * derived from the handle's OWN `export()` (via {@link import('./helpers.js').tableSchema}), so it
2718
- * works for any handle, config-tracked or caller-supplied via
2719
- * {@link import('./types.js').DatabaseToolOptions.databases}. `'records'` clamps its `criteria` to
2794
+ * `'records'` clamps its `query` to
2720
2795
  * {@link import('./types.js').DatabaseToolOptions.limit} (default
2721
2796
  * {@link import('./constants.js').DATABASE_TOOL_LIMIT}) via
2722
- * {@link import('./helpers.js').clampCriteria}, reporting `truncated` when storage held more rows
2723
- * than the cap. Every operation's `criteria` is normalized via
2724
- * {@link import('./helpers.js').criteriaOf} (defaults an omitted condition `connector` to `'and'`).
2797
+ * {@link import('./helpers.js').clampQuery}, reporting `truncated` when storage held more rows
2798
+ * than the cap. Every operation's `query` is normalized via
2799
+ * {@link import('./helpers.js').queryOf} (defaults an omitted condition `connector` to `'and'`).
2725
2800
  * When {@link import('./types.js').DatabaseToolOptions.readonly} is `true`, every mutating
2726
- * operation throws a typed `TOOL` `ToolboxError` before doing anything. When
2727
- * {@link import('./types.js').DatabaseToolOptions.timeout} is set, every `@orkestrel/database` call
2728
- * this tool makes is given a fresh `AbortSignal.timeout(timeout)`. A typed `@orkestrel/database`
2801
+ * operation throws a typed `TOOL` `ToolboxError` before doing anything. A configured
2802
+ * {@link import('./types.js').DatabaseToolOptions.timeout} is passed as a fresh
2803
+ * `AbortSignal.timeout(timeout)` only to table operations whose current database API accepts
2804
+ * operation options (`records`, `count`, `aggregate`, `add`, `set`, `update`, and `remove`). It is
2805
+ * not an outer deadline for resolution, construction, schema inspection, `get`, or `close`. A typed `@orkestrel/database`
2729
2806
  * failure (`DatabaseError`) re-surfaces as a typed `DATABASE` `ToolboxError` carrying the
2730
2807
  * original {@link import('@orkestrel/database').DatabaseErrorCode} in `context.code`
2731
2808
  * ({@link import('./helpers.js').databaseToolCode}); an `ToolboxError` thrown by this tool's own
@@ -2759,15 +2836,15 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2759
2836
  * ```
2760
2837
  */
2761
2838
  function createDatabaseTool(options = {}) {
2839
+ if (options.timeout !== void 0 && (options.timeout < 0 || !Number.isSafeInteger(options.timeout))) throw new ToolboxError("TOOL", "database timeout must be a nonnegative safe integer", { timeout: options.timeout });
2762
2840
  const contract = createContract(databaseToolShape);
2763
2841
  const parameters = schemaToParameters(contract.schema);
2764
2842
  const handles = new Map(Object.entries(options.databases ?? {}));
2765
- const definitions = /* @__PURE__ */ new Map();
2766
2843
  const drivers = options.drivers ?? { memory: createMemoryDriver };
2767
- const key = options.key ?? generateUUID;
2844
+ const generator = options.generator;
2768
2845
  const cap = options.limit ?? 1e3;
2769
2846
  const store = options.store;
2770
- const resolver = store === void 0 ? new DatabaseResolver(handles, drivers, key) : new DatabaseResolver(handles, drivers, key, store);
2847
+ const resolver = store === void 0 ? new DatabaseResolver(handles, drivers, generator) : new DatabaseResolver(handles, drivers, generator, store);
2771
2848
  return createTool({
2772
2849
  name: options.name ?? "database",
2773
2850
  description: options.description ?? DATABASE_TOOL_DESCRIPTION,
@@ -2776,7 +2853,7 @@ function createDatabaseTool(options = {}) {
2776
2853
  async execute(args) {
2777
2854
  const call = contract.parse(args);
2778
2855
  if (call === void 0) throw new ToolboxError("TOOL", "malformed database call", { args });
2779
- if (options.readonly === true && DATABASE_TOOL_MUTATIONS.has(call.operation)) throw new ToolboxError("TOOL", `operation '${call.operation}' is disabled in readonly mode`, { operation: call.operation });
2856
+ if (options.readonly === true && DATABASE_TOOL_MUTATIONS.includes(call.operation)) throw new ToolboxError("TOOL", `operation '${call.operation}' is disabled in readonly mode`, { operation: call.operation });
2780
2857
  const read = options.timeout === void 0 ? void 0 : { signal: AbortSignal.timeout(options.timeout) };
2781
2858
  try {
2782
2859
  switch (call.operation) {
@@ -2789,22 +2866,28 @@ function createDatabaseTool(options = {}) {
2789
2866
  driver: name
2790
2867
  });
2791
2868
  const tables = call.tables;
2792
- const keys = call.keys;
2869
+ const primary = call.primary;
2870
+ const indexes = call.indexes;
2871
+ const version = call.version;
2793
2872
  const handle = createDatabase({
2794
2873
  driver: factory(),
2795
2874
  tables: expandTables(tables),
2796
- ...keys === void 0 ? {} : { keys },
2797
- key
2875
+ name: call.id,
2876
+ ...primary === void 0 ? {} : { primary },
2877
+ ...indexes === void 0 ? {} : { indexes },
2878
+ ...version === void 0 ? {} : { version },
2879
+ ...generator === void 0 ? {} : { generator }
2798
2880
  });
2799
- resolver.set(call.id, handle);
2800
2881
  const definition = {
2801
2882
  id: call.id,
2802
2883
  driver: name,
2803
2884
  tables,
2804
- ...keys === void 0 ? {} : { keys }
2885
+ ...primary === void 0 ? {} : { primary },
2886
+ ...indexes === void 0 ? {} : { indexes },
2887
+ ...version === void 0 ? {} : { version }
2805
2888
  };
2806
- definitions.set(call.id, definition);
2807
2889
  if (store !== void 0) await store.set(definition);
2890
+ resolver.set(call.id, handle);
2808
2891
  return {
2809
2892
  id: call.id,
2810
2893
  tables: Object.keys(tables)
@@ -2830,7 +2913,7 @@ function createDatabaseTool(options = {}) {
2830
2913
  }
2831
2914
  case "records": {
2832
2915
  const table = (await resolver.resolve(call.id)).table(call.table);
2833
- const { criteria: probe, limit } = clampCriteria(criteriaOf(call.criteria), cap);
2916
+ const { query: probe, limit } = clampQuery(queryOf(call.query), cap);
2834
2917
  const rows = await table.records(probe, read);
2835
2918
  const truncated = rows.length > limit;
2836
2919
  const sliced = rows.slice(0, limit);
@@ -2841,8 +2924,8 @@ function createDatabaseTool(options = {}) {
2841
2924
  limit
2842
2925
  };
2843
2926
  }
2844
- case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(criteriaOf(call.criteria), read) };
2845
- case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, criteriaOf(call.criteria), read) };
2927
+ case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(queryOf(call.query), read) };
2928
+ case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, queryOf(call.query), read) };
2846
2929
  case "add": {
2847
2930
  const table = (await resolver.resolve(call.id)).table(call.table);
2848
2931
  const many = Array.isArray(call.row);
@@ -2872,33 +2955,6 @@ function createDatabaseTool(options = {}) {
2872
2955
  const removed = await table.remove(keys, read);
2873
2956
  return many ? { removed } : { removed: removed[0] };
2874
2957
  }
2875
- case "migrate": {
2876
- const handle = await resolver.resolve(call.id);
2877
- const previous = handle.export();
2878
- const deployed = Object.entries(previous).map(([name, table]) => tableSchema(name, table));
2879
- const tables = call.tables;
2880
- const keys = {};
2881
- for (const name of Object.keys(tables)) {
2882
- const existing = previous[name];
2883
- if (existing !== void 0) keys[name] = existing.key;
2884
- }
2885
- const declared = expandTables(tables);
2886
- const migrated = handle.import(declared, Object.keys(keys).length > 0 ? keys : void 0);
2887
- const migration = await migrated.migrate(deployed, read);
2888
- resolver.set(call.id, migrated);
2889
- const tracked = definitions.get(call.id) ?? (store === void 0 ? void 0 : await store.get(call.id));
2890
- if (tracked !== void 0) {
2891
- const updated = {
2892
- id: call.id,
2893
- driver: tracked.driver,
2894
- tables,
2895
- ...Object.keys(keys).length > 0 ? { keys } : {}
2896
- };
2897
- definitions.set(call.id, updated);
2898
- if (store !== void 0) await store.set(updated);
2899
- }
2900
- return { migration };
2901
- }
2902
2958
  case "destroy": {
2903
2959
  const cached = resolver.get(call.id);
2904
2960
  const persisted = store !== void 0 && cached === void 0 ? await store.get(call.id) !== void 0 : false;
@@ -2906,7 +2962,6 @@ function createDatabaseTool(options = {}) {
2906
2962
  await cached.close();
2907
2963
  resolver.delete(call.id);
2908
2964
  }
2909
- definitions.delete(call.id);
2910
2965
  if (store !== void 0) await store.delete(call.id);
2911
2966
  return {
2912
2967
  id: call.id,
@@ -2952,7 +3007,7 @@ function createDatabaseTool(options = {}) {
2952
3007
  * (positional many-key form, AGENTS §9.2) or a single key. `'find'` and `'links'` clamp their
2953
3008
  * result to {@link import('./types.js').RelationToolOptions.limit} (default
2954
3009
  * {@link import('./constants.js').RELATION_TOOL_LIMIT}) — `'find'` probes one row past the
2955
- * effective limit (mirroring {@link import('./helpers.js').clampCriteria}'s idiom) to report
3010
+ * effective limit (mirroring {@link import('./helpers.js').clampQuery}'s idiom) to report
2956
3011
  * `truncated`; `'links'` (which has no upstream pagination) fetches the FULL linked-key list and
2957
3012
  * slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
2958
3013
  * row.
@@ -3264,6 +3319,6 @@ function createEndpointTool(definition, options) {
3264
3319
  });
3265
3320
  }
3266
3321
  //#endregion
3267
- export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, clampCriteria, coerceAnswer, columnKindShape, columnSchema, columnShape, columnSpecShape, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowTool, createWorkspaceTool, criteriaOf, criteriaShape, databaseToolCode, databaseToolShape, describeToolShape, expandInclude, expandSteps, expandTables, includeShape, inferToolShape, isColumnKind, isColumnSpec, isDatabaseDefinition, isToolboxError, keyShape, kindShape, managerShape, orderShape, phaseDraftShape, promptToolShape, relationKeyShape, relationManagerOf, relationModelOf, relationToolCode, relationToolShape, rowShape, rowsShape, singleKeyShape, stepShape, tableSchema, tableSpecShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
3322
+ export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, clampQuery, coerceAnswer, columnKindShape, columnShape, columnSpecShape, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowFunctions, createWorkflowTool, createWorkspaceTool, databaseToolCode, databaseToolShape, deriveWorkflowDepth, describeToolShape, expandInclude, expandSteps, expandTables, extendLineage, includeShape, inferToolShape, isAgentFunction, isColumnKind, isColumnSpec, isDatabaseDefinition, isToolboxError, isWorkflowLineage, keyShape, kindShape, lineageOf, managerShape, orderShape, phaseDraftShape, promptToolShape, queryOf, queryShape, relationKeyShape, relationManagerOf, relationModelOf, relationToolCode, relationToolShape, rowShape, rowsShape, singleKeyShape, stepShape, tableSpecShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
3268
3323
 
3269
3324
  //# sourceMappingURL=index.js.map