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