@orkestrel/toolbox 0.0.11 → 0.0.13

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.
@@ -9,40 +9,44 @@ import { createTool } from "@orkestrel/tool";
9
9
  import { createForm, isFormError, isFormValues, parseForm } from "@orkestrel/form";
10
10
  //#region src/core/constants.ts
11
11
  /**
12
- * The name {@link import('./factories.js').createAgentTool} advertises by default — the key a
13
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
12
+ * Holds the name {@link import('./factories.js').createAgentTool} advertises by default, `'agent'`
13
+ * — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
14
14
  */
15
15
  var AGENT_TOOL_NAME = "agent";
16
16
  /**
17
- * The maximum nesting depth a delegation chain (agent tool → sub-agent → agent tool → …) may
18
- * reach — the bound {@link import('./factories.js').createAgentTool}'s depth/cycle guard
17
+ * Holds the maximum nesting depth a delegation chain (agent tool → sub-agent → agent tool → …) may
18
+ * reach, `8` — the bound {@link import('./factories.js').createAgentTool}'s depth/cycle guard
19
19
  * enforces.
20
20
  *
21
21
  * @remarks
22
- * Deliberately a SEPARATE constant from {@link MAX_WORKFLOW_DEPTH} (rather than the two guards
23
- * sharing one reference): the two guards bound DIFFERENT chains (workflow nesting vs. agent
22
+ * Deliberately a separate constant from {@link MAX_WORKFLOW_CHAIN} (rather than the guards
23
+ * sharing one reference): they bound different chains (workflow nesting against agent
24
24
  * delegation) that happen to share a value today, and keeping this bound decoupled means a
25
25
  * future change to one never silently shifts the other. Same numeric value by convention, not
26
26
  * by shared reference.
27
27
  */
28
28
  var AGENT_TOOL_DEPTH = 8;
29
29
  /**
30
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createAgentTool}
31
- * advertises in place of {@link AGENT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
32
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
33
- * for the full teaching description; the full text stays retrievable via
34
- * {@link import('./factories.js').createDescribeTool}.
30
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
31
+ * {@link import('./factories.js').createAgentTool} advertises in place of
32
+ * {@link AGENT_TOOL_DESCRIPTION} — one sentence offering sub-agent delegation and pointing at
33
+ * `describe` for the optional overrides.
34
+ *
35
+ * @remarks
36
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
37
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
38
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
35
39
  */
36
40
  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
41
  /**
38
- * The description {@link import('./factories.js').createAgentTool} advertises — a short guide
42
+ * Holds the description {@link import('./factories.js').createAgentTool} advertises — a short guide
39
43
  * covering the required task and optional provider, tools, and system overrides.
40
44
  */
41
45
  var AGENT_TOOL_DESCRIPTION = [
42
46
  "Delegate a task to a sub-agent and return its result. Every call runs ONE sub-agent turn to completion.",
43
47
  "",
44
48
  "Required:",
45
- " task - the instructions the sub-agent should carry out.",
49
+ " task - the instructions the sub-agent carries out.",
46
50
  "Optional overrides (default to the values this tool was configured with):",
47
51
  " provider - the registry key of the model/provider the sub-agent runs against.",
48
52
  " tools - registry keys of the tools loaded into the sub-agent (replaces the default list, not merged).",
@@ -51,7 +55,7 @@ var AGENT_TOOL_DESCRIPTION = [
51
55
  JSON.stringify({ task: "Summarize the attached notes in three bullet points." })
52
56
  ].join("\n");
53
57
  /**
54
- * The maximum nesting depth a workflow → agent → workflow chain may reach — the bound
58
+ * Holds the maximum nesting depth a workflow → agent → workflow chain may reach, `8` — the bound
55
59
  * {@link import('./factories.js').createAgentFunction} and
56
60
  * {@link import('./factories.js').createWorkflowTool}'s depth/cycle guards enforce.
57
61
  *
@@ -60,42 +64,42 @@ var AGENT_TOOL_DESCRIPTION = [
60
64
  * workflows is allowed, while a ninth nested workflow is rejected with this package's typed
61
65
  * `DEPTH` `ToolboxError`.
62
66
  */
63
- var MAX_WORKFLOW_DEPTH = 8;
67
+ var MAX_WORKFLOW_CHAIN = 8;
64
68
  /**
65
- * The name {@link import('./factories.js').createWorkflowTool} advertises by default — the key a
66
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under, and the name
67
- * {@link import('./factories.js').createAgentFunction} binds the depth/cycle-aware workflow tool
68
- * under onto a wrapped agent's `context.tools`.
69
+ * Holds the name {@link import('./factories.js').createWorkflowTool} advertises by default,
70
+ * `'workflow'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
71
+ * under, and the name {@link import('./factories.js').createAgentFunction} binds the
72
+ * depth/cycle-aware workflow tool under onto a wrapped agent's `context.tools`.
69
73
  *
70
74
  * @remarks
71
75
  * The propagation seam's well-known key: when
72
76
  * `createAgentFunction`'s `runner` option is supplied, it adds a `createWorkflowTool`-built tool
73
- * under this name to the agent's `context.tools`, so it can author + run a NESTED workflow
74
- * (bounded by {@link MAX_WORKFLOW_DEPTH}).
77
+ * under this name to the agent's `context.tools`, so it can author and run a nested workflow
78
+ * (bounded by {@link MAX_WORKFLOW_CHAIN}).
75
79
  */
76
80
  var WORKFLOW_TOOL_NAME = "workflow";
77
81
  /**
78
- * A complete FLAT authoring example — the PRIMARY way a small model authors a workflow through
79
- * {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
82
+ * Holds a complete flat authoring example — the primary way a small model authors a workflow
83
+ * through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
80
84
  *
81
85
  * @remarks
82
86
  * Each step becomes a one-task phase, in
83
- * order; a step's `name` is a REGISTERED behavior name (not a label) — the registry key its
84
- * task's `run` resolves against. The tool expands this
87
+ * order; a step's `name` is a registered behavior name (not a label) — the registry key its
88
+ * task's `behavior` resolves against. The tool expands this
85
89
  * ({@link import('./helpers.js').expandSteps}) into a valid `WorkflowDefinition`
86
- * (`@orkestrel/workflow`). It is embedded VERBATIM in {@link WORKFLOW_TOOL_DESCRIPTION}.
90
+ * (`@orkestrel/workflow`). It is embedded verbatim in {@link WORKFLOW_TOOL_DESCRIPTION}.
87
91
  */
88
92
  var WORKFLOW_TOOL_FLAT_EXAMPLE = Object.freeze({
89
93
  name: "release",
90
94
  steps: Object.freeze([Object.freeze({ name: "compile" }), Object.freeze({ name: "publish" })])
91
95
  });
92
96
  /**
93
- * A minimal NESTED authoring example — the ADVANCED escape-hatch form a model may use instead of
94
- * the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
97
+ * Holds a minimal nested authoring example — the advanced escape-hatch form a model can use instead
98
+ * of the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
95
99
  *
96
100
  * @remarks
97
- * The full four-level form, documented in
98
- * {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced alternative. It is embedded VERBATIM.
101
+ * The full nested form, documented in
102
+ * {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced alternative. It is embedded verbatim.
99
103
  */
100
104
  var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
101
105
  id: "release",
@@ -106,20 +110,24 @@ var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
106
110
  tasks: Object.freeze([Object.freeze({
107
111
  id: "compile",
108
112
  name: "Compile",
109
- run: "compile"
113
+ behavior: "compile"
110
114
  })])
111
115
  })])
112
116
  });
113
117
  /**
114
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkflowTool}
115
- * advertises in place of {@link WORKFLOW_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
116
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
117
- * for the full teaching description; the full text stays retrievable via
118
- * {@link import('./factories.js').createDescribeTool}.
118
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
119
+ * {@link import('./factories.js').createWorkflowTool} advertises in place of
120
+ * {@link WORKFLOW_TOOL_DESCRIPTION} — one sentence offering multi-phase authoring and pointing at
121
+ * `describe` for the authoring schema and its examples.
122
+ *
123
+ * @remarks
124
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
125
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
126
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
119
127
  */
120
128
  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
129
  /**
122
- * The description {@link import('./factories.js').createWorkflowTool} advertises — the flat
130
+ * Holds the description {@link import('./factories.js').createWorkflowTool} advertises — the flat
123
131
  * authoring form, its worked example, and the advanced nested definition form.
124
132
  */
125
133
  var WORKFLOW_TOOL_DESCRIPTION = [
@@ -133,18 +141,19 @@ var WORKFLOW_TOOL_DESCRIPTION = [
133
141
  "Example:",
134
142
  JSON.stringify(WORKFLOW_TOOL_FLAT_EXAMPLE),
135
143
  "",
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:",
144
+ "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 \"behavior\" (a registered behavior name); omitting it creates a JSON-null no-op:",
137
145
  JSON.stringify(WORKFLOW_TOOL_NESTED_EXAMPLE),
138
146
  "In the nested form you may omit any \"id\"/\"name\" and they are filled in positionally; a provided one is kept."
139
147
  ].join("\n");
140
148
  /**
141
- * The name {@link import('./factories.js').createWorkspaceTool} advertises by default — the key a
142
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
149
+ * Holds the name {@link import('./factories.js').createWorkspaceTool} advertises by default,
150
+ * `'workspace'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`)
151
+ * registers under.
143
152
  */
144
153
  var WORKSPACE_TOOL_NAME = "workspace";
145
154
  /**
146
- * A valid {@link import('./types.js').WorkspaceOperation} object — the canonical example embedded
147
- * VERBATIM in {@link WORKSPACE_TOOL_DESCRIPTION}.
155
+ * Holds a valid {@link import('./types.js').WorkspaceOperation} object — the canonical example
156
+ * embedded verbatim in {@link WORKSPACE_TOOL_DESCRIPTION}.
148
157
  *
149
158
  * @remarks
150
159
  * A `'write'` op (the most common authoring
@@ -157,15 +166,19 @@ var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
157
166
  content: "hello"
158
167
  });
159
168
  /**
160
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkspaceTool}
161
- * advertises in place of {@link WORKSPACE_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
162
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
163
- * for the full teaching description; the full text stays retrievable via
164
- * {@link import('./factories.js').createDescribeTool}.
169
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
170
+ * {@link import('./factories.js').createWorkspaceTool} advertises in place of
171
+ * {@link WORKSPACE_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed file editing and
172
+ * pointing at `describe` for the operation list and its fields.
173
+ *
174
+ * @remarks
175
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
176
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
177
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
165
178
  */
166
179
  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
180
  /**
168
- * The description {@link import('./factories.js').createWorkspaceTool} advertises — the
181
+ * Holds the description {@link import('./factories.js').createWorkspaceTool} advertises — the
169
182
  * operation-keyed workspace protocol, all supported operations, and worked examples.
170
183
  *
171
184
  * @remarks
@@ -197,24 +210,26 @@ var WORKSPACE_TOOL_DESCRIPTION = [
197
210
  JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
198
211
  ].join("\n");
199
212
  /**
200
- * The name {@link import('./factories.js').createDescribeTool} advertises by default — the key a
201
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
213
+ * Holds the name {@link import('./factories.js').createDescribeTool} advertises by default,
214
+ * `'describe'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
215
+ * under.
202
216
  *
203
217
  * @remarks
204
- * Net-new: pairs with the other three tools' lean {@link AGENT_TOOL_SUMMARY} /
205
- * {@link WORKFLOW_TOOL_SUMMARY} / {@link WORKSPACE_TOOL_SUMMARY} — a model that reads only the
218
+ * Pairs with the lean {@link AGENT_TOOL_SUMMARY} / {@link WORKFLOW_TOOL_SUMMARY} /
219
+ * {@link WORKSPACE_TOOL_SUMMARY} — a model that reads only the
206
220
  * advertised summary can call `describe` with that tool's registered name to get its full
207
221
  * teaching description back.
208
222
  */
209
223
  var DESCRIBE_TOOL_NAME = "describe";
210
224
  /**
211
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createDescribeTool}
225
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createDescribeTool}
212
226
  * advertises — this tool needs no teaching of its own, so its summary and description are both
213
227
  * short.
214
228
  */
215
229
  var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered tool.";
216
230
  /**
217
- * The DESCRIPTION {@link import('./factories.js').createDescribeTool} advertises.
231
+ * Holds the description {@link import('./factories.js').createDescribeTool} advertises — the
232
+ * registered tool `name` it requires, and the full description of that tool it returns.
218
233
  *
219
234
  * @remarks
220
235
  * Deliberately short — unlike the workflow / workspace / agent tools, this one has no authoring
@@ -222,19 +237,27 @@ var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered t
222
237
  */
223
238
  var DESCRIBE_TOOL_DESCRIPTION = "Return the full description of a registered tool by its name. Required: name - the registered tool name (see another tool listing for available names).";
224
239
  /**
225
- * The name {@link import('./factories.js').createPromptTool} advertises by default — the key a
226
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
240
+ * Holds the name {@link import('./factories.js').createPromptTool} advertises by default, `'ask'` —
241
+ * the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
227
242
  */
228
243
  var PROMPT_TOOL_NAME = "ask";
229
244
  /**
230
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createPromptTool}
231
- * advertises in place of {@link PROMPT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
232
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
233
- * for the full teaching description; the full text stays retrievable via
234
- * {@link import('./factories.js').createDescribeTool}.
245
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
246
+ * {@link import('./factories.js').createPromptTool} advertises in place of
247
+ * {@link PROMPT_TOOL_DESCRIPTION} — one sentence offering the blocking multi-field ask and pointing
248
+ * at `describe` for the schema.
249
+ *
250
+ * @remarks
251
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
252
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
253
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
235
254
  */
236
255
  var PROMPT_TOOL_SUMMARY = "Ask another terminal a multi-field form and BLOCK until it answers; the call resolves with the values record. Call describe('ask') for the schema.";
237
- /** The full form protocol {@link import('./factories.js').createPromptTool} advertises. */
256
+ /**
257
+ * Holds the form protocol {@link import('./factories.js').createPromptTool} advertises — the
258
+ * required `to` and `schema`, how a field declares its control and its rules, and a worked
259
+ * example.
260
+ */
238
261
  var PROMPT_TOOL_DESCRIPTION = [
239
262
  "Ask another terminal a multi-field form and block until it answers. This call does not return until the addressed terminal answers, or the form expires.",
240
263
  "",
@@ -260,24 +283,32 @@ var PROMPT_TOOL_DESCRIPTION = [
260
283
  })
261
284
  ].join("\n");
262
285
  /**
263
- * The name {@link import('./factories.js').createAnswerTool} advertises by default — the key a
264
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
286
+ * Holds the name {@link import('./factories.js').createAnswerTool} advertises by default,
287
+ * `'answer'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
288
+ * under.
265
289
  */
266
290
  var ANSWER_TOOL_NAME = "answer";
267
291
  /**
268
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createAnswerTool}
269
- * advertises in place of {@link ANSWER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
270
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
271
- * for the full teaching description; the full text stays retrievable via
272
- * {@link import('./factories.js').createDescribeTool}.
292
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
293
+ * {@link import('./factories.js').createAnswerTool} advertises in place of
294
+ * {@link ANSWER_TOOL_DESCRIPTION} — one sentence offering the pending listing and the answer call,
295
+ * and pointing at `describe` for the fields each takes.
296
+ *
297
+ * @remarks
298
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
299
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
300
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
273
301
  */
274
302
  var ANSWER_TOOL_SUMMARY = "List forms addressed to this terminal, or answer one by id with a values record. Call describe('answer') for the required fields.";
275
- /** The pending/answer protocol {@link import('./factories.js').createAnswerTool} advertises. */
303
+ /**
304
+ * Holds the protocol {@link import('./factories.js').createAnswerTool} advertises — the `pending`
305
+ * and `answer` operations and the `values` record an answer supplies, each with a worked example.
306
+ */
276
307
  var ANSWER_TOOL_DESCRIPTION = [
277
- "List the forms currently addressed to this terminal, or answer one of them by id. Every call is ONE operation, chosen by the \"operation\" field.",
308
+ "List the forms addressed to this terminal, or answer one of them by id. Every call is ONE operation, chosen by the \"operation\" field.",
278
309
  "",
279
310
  "Operations:",
280
- "- pending { \"operation\": \"pending\" } — list every form currently addressed to this terminal (id, from, schema).",
311
+ "- pending { \"operation\": \"pending\" } — list every form addressed to this terminal (id, from, schema).",
281
312
  "- answer { \"operation\": \"answer\", \"id\": \"<form id>\", \"values\": { \"<field name>\": <field value> } } — answer every required field with the value its control accepts.",
282
313
  "Example — list pending forms:",
283
314
  JSON.stringify({ operation: "pending" }),
@@ -292,28 +323,31 @@ var ANSWER_TOOL_DESCRIPTION = [
292
323
  })
293
324
  ].join("\n");
294
325
  /**
295
- * The name the upcoming `createDatabaseTool` factory will advertise by default — the key a model
296
- * calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
297
- *
298
- * @remarks
299
- * SRC-1 of a 3-unit spine: this unit lands the persistence + schema foundation
300
- * ({@link import('./types.js').DatabaseDefinition}, {@link import('./types.js').DefinitionStoreInterface},
301
- * {@link import('./helpers.js').expandTables}); `createDatabaseTool` itself is built in a later unit.
326
+ * Holds the name {@link import('./factories.js').createDatabaseTool} advertises by default,
327
+ * `'database'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
328
+ * under.
302
329
  */
303
330
  var DATABASE_TOOL_NAME = "database";
304
331
  /**
305
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} the upcoming database tool
306
- * will advertise in place of {@link DATABASE_TOOL_DESCRIPTION}.
332
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
333
+ * {@link import('./factories.js').createDatabaseTool} advertises in place of
334
+ * {@link DATABASE_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed database call and
335
+ * pointing at `describe` for the operation list, the query form, and the column DSL.
336
+ *
337
+ * @remarks
338
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
339
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
340
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
307
341
  */
308
342
  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.";
309
343
  /**
310
- * The DESCRIPTION the upcoming database tool will advertise — a multi-line guide that teaches a
311
- * small model the operation list, the SERIALIZED query form, and the {@link import('./types.js').TableSpec}
312
- * column DSL.
344
+ * Holds the description {@link import('./factories.js').createDatabaseTool} advertises — a
345
+ * multi-line guide that teaches a small model the operation list, the serialized query form, and
346
+ * the {@link import('./types.js').TableSpec} column DSL.
313
347
  *
314
348
  * @remarks
315
- * The query form is deliberately SERIALIZED (never fluent) — every condition is a flat object
316
- * `{ column, operator, values, connector? }` where `values` is ALWAYS an array, even for a
349
+ * The query form is deliberately serialized (never fluent) — every condition is a flat object
350
+ * `{ column, operator, values, connector? }` where `values` is always an array, even for a
317
351
  * single-value operator (`{ column: 'age', operator: 'from', values: [18] }`), so a small model
318
352
  * never has to chain method calls or guess whether a value is scalar or a list.
319
353
  */
@@ -321,7 +355,7 @@ var DATABASE_TOOL_DESCRIPTION = [
321
355
  "Create and query a database. Every call is ONE operation, chosen by the \"operation\" field.",
322
356
  "",
323
357
  "Operations (each takes the fields listed):",
324
- "- 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.",
358
+ "- create { \"operation\": \"create\", \"id\": \"<database id>\", \"tables\": { \"<table>\": { \"columns\": { \"<column>\": \"string\" | \"integer\" | \"number\" | \"boolean\" | { \"primitive\": \"string\", \"optional\": true } } } }, \"primary\"?: { \"<table>\": \"<column>\" }, \"indexes\"?: { \"<table>\": [[\"<column>\"]] }, \"version\"?: <number> } — define a new database.",
325
359
  "- tables { \"operation\": \"tables\", \"id\": \"<database id>\" } — list a database's table names.",
326
360
  "- get { \"operation\": \"get\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\" } — fetch one row by its primary key.",
327
361
  "- records { \"operation\": \"records\", \"id\": \"<database id>\", \"table\": \"<table>\", \"query\"?: <Query> } — list rows matching a query.",
@@ -338,7 +372,7 @@ var DATABASE_TOOL_DESCRIPTION = [
338
372
  " operators: equals, not, above, below, from, to, between, like, glob, starts, ends, any, none, absent, present.",
339
373
  " \"connector\" joins this condition to the next (\"and\" | \"or\"); omit on the last condition.",
340
374
  "",
341
- "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.",
375
+ "Column DSL (used by \"create\" \"tables\"): a column is either a bare primitive string (\"string\" | \"integer\" | \"number\" | \"boolean\"), or { \"primitive\": \"<primitive>\", \"optional\": true } when the column may be absent from a row.",
342
376
  "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.",
343
377
  "Example — create a database:",
344
378
  JSON.stringify({
@@ -348,7 +382,7 @@ var DATABASE_TOOL_DESCRIPTION = [
348
382
  name: "string",
349
383
  price: "number",
350
384
  notes: {
351
- type: "string",
385
+ primitive: "string",
352
386
  optional: true
353
387
  }
354
388
  } } }
@@ -365,9 +399,12 @@ var DATABASE_TOOL_DESCRIPTION = [
365
399
  }] }
366
400
  })
367
401
  ].join("\n");
368
- /** The default cap on rows a `records` call returns when the caller omits `query.limit` — the upcoming database tool's default row ceiling. */
402
+ /**
403
+ * Holds the default cap on rows a `records` call returns when the caller omits `query.limit`,
404
+ * `1000` — the database tool's default row ceiling.
405
+ */
369
406
  var DATABASE_TOOL_LIMIT = 1e3;
370
- /** The runtime-frozen database-tool mutation names disabled by `DatabaseToolOptions.readonly`. */
407
+ /** Lists the runtime-frozen database-tool mutation names disabled by `DatabaseToolOptions.readonly`. */
371
408
  var DATABASE_TOOL_MUTATIONS = Object.freeze([
372
409
  "create",
373
410
  "add",
@@ -377,21 +414,30 @@ var DATABASE_TOOL_MUTATIONS = Object.freeze([
377
414
  "destroy"
378
415
  ]);
379
416
  /**
380
- * The name `createRelationTool` advertises by default — the key a model calls and the
381
- * `ToolManagerInterface` (`@orkestrel/tool`) registers under.
417
+ * Holds the name {@link import('./factories.js').createRelationTool} advertises by default,
418
+ * `'relation'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
419
+ * under.
382
420
  */
383
421
  var RELATION_TOOL_NAME = "relation";
384
422
  /**
385
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} the relation tool advertises
386
- * in place of {@link RELATION_TOOL_DESCRIPTION}.
423
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
424
+ * {@link import('./factories.js').createRelationTool} advertises in place of
425
+ * {@link RELATION_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed relationship
426
+ * traversal and pointing at `describe` for the include-path syntax.
427
+ *
428
+ * @remarks
429
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
430
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
431
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
387
432
  */
388
433
  var RELATION_TOOL_SUMMARY = "Traverse and edit relationships between database rows — one operation per call (load, find, link, unlink, links), chosen by the 'operation' field. Call describe('relation') for the include-path syntax.";
389
434
  /**
390
- * The DESCRIPTION the relation tool advertises — a multi-line guide that teaches a small model
391
- * the operation list and the flat dot-path `include` syntax.
435
+ * Holds the description {@link import('./factories.js').createRelationTool} advertises — a
436
+ * multi-line guide that teaches a small model the operation list and the flat dot-path `include`
437
+ * syntax.
392
438
  *
393
439
  * @remarks
394
- * An include path is a FLAT dot-separated string (`'contacts.account'`), never a nested object —
440
+ * An include path is a flat dot-separated string (`'contacts.account'`), never a nested object —
395
441
  * the same small-model ergonomic lever the other tools in this package use for flat args.
396
442
  */
397
443
  var RELATION_TOOL_DESCRIPTION = [
@@ -413,24 +459,38 @@ var RELATION_TOOL_DESCRIPTION = [
413
459
  include: ["contacts.account"]
414
460
  })
415
461
  ].join("\n");
416
- /** The default cap on rows a `find` / `links` call returns when the caller omits `limit` — the relation tool's default row ceiling. */
462
+ /**
463
+ * Holds the default cap on rows a `find` or `links` call returns when the caller omits `limit`,
464
+ * `1000` — the relation tool's default row ceiling.
465
+ */
417
466
  var RELATION_TOOL_LIMIT = 1e3;
418
- /** The default cap on how many `include` path segments deep a `load` / `find` call may traverse — the relation tool's default include-depth ceiling. */
467
+ /**
468
+ * Holds the default cap on how many `include` path segments deep a `load` or `find` call may
469
+ * traverse, `3` — the relation tool's default include-depth ceiling.
470
+ */
419
471
  var RELATION_TOOL_DEPTH = 3;
420
472
  /**
421
- * The name {@link import('./factories.js').createInferTool} advertises by default — the key a
422
- * model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
473
+ * Holds the name {@link import('./factories.js').createInferTool} advertises by default, `'infer'`
474
+ * — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
423
475
  */
424
476
  var INFER_TOOL_NAME = "infer";
425
477
  /**
426
- * The lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createInferTool}
427
- * advertises in place of {@link INFER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
428
- * (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
429
- * for the full teaching description; the full text stays retrievable via
430
- * {@link import('./factories.js').createDescribeTool}.
478
+ * Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
479
+ * {@link import('./factories.js').createInferTool} advertises in place of
480
+ * {@link INFER_TOOL_DESCRIPTION} — one sentence offering schema inference from example values and
481
+ * pointing at `describe` for the fields it takes.
482
+ *
483
+ * @remarks
484
+ * A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
485
+ * `summary ?? description`, so this one-sentence text stands in for the full teaching description;
486
+ * the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
431
487
  */
432
488
  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.";
433
- /** The schema-inference protocol {@link import('./factories.js').createInferTool} advertises. */
489
+ /**
490
+ * Holds the schema-inference protocol {@link import('./factories.js').createInferTool} advertises —
491
+ * the required `samples`, the optional `format`, `enum`, and `candidates` arguments, and a worked
492
+ * example of the bare return and of the `candidates`-wrapped return.
493
+ */
434
494
  var INFER_TOOL_DESCRIPTION = [
435
495
  "Infer a JSON Schema from example values, returned in the same shape a tool advertises its parameters.",
436
496
  "",
@@ -443,7 +503,7 @@ var INFER_TOOL_DESCRIPTION = [
443
503
  " is wrapped as { parameters, checks } instead of the bare parameters record, one",
444
504
  " check per candidate (same index). Every check has the uniform shape",
445
505
  " { index, valid, coercible, faults? }. `valid` is a STRICT verdict (no coercion)",
446
- " — e.g. the number 7 is NOT valid against a string slot. `coercible` answers a",
506
+ " — for example the number 7 is NOT valid against a string slot. `coercible` answers a",
447
507
  " separate question: would the SAME value be accepted by an endpoint tool call,",
448
508
  " whose enforcement NORMALIZES args (7 coerces to '7')? So 7 against a string slot",
449
509
  " yields { valid: false, coercible: true, faults: [] } — a strict mismatch that",
@@ -523,7 +583,7 @@ var INFER_TOOL_DESCRIPTION = [
523
583
  //#endregion
524
584
  //#region src/core/errors.ts
525
585
  /**
526
- * A package-owned tool-call failure: malformed input or unresolved configuration (`TOOL`),
586
+ * Represents a package-owned tool-call failure: malformed input or unresolved configuration (`TOOL`),
527
587
  * delegation depth/cycle rejection (`DEPTH`), prompt failure (`DEADLOCK` / `EXPIRE` / `ANSWER`),
528
588
  * or a translated upstream database/relation failure (`DATABASE` / `RELATION`).
529
589
  *
@@ -555,10 +615,10 @@ var ToolboxError = class extends Error {
555
615
  }
556
616
  };
557
617
  /**
558
- * Type guard narrowing an unknown caught value to an {@link ToolboxError}.
618
+ * Narrows an unknown caught value to a {@link ToolboxError}.
559
619
  *
560
620
  * @param value - The value to test (typically a `catch` binding)
561
- * @returns `true` when `value` is an {@link ToolboxError}
621
+ * @returns True if `value` is an {@link ToolboxError}; false otherwise
562
622
  *
563
623
  * @example
564
624
  * ```ts
@@ -575,9 +635,102 @@ function isToolboxError(value) {
575
635
  return value instanceof ToolboxError;
576
636
  }
577
637
  //#endregion
638
+ //#region src/core/validators.ts
639
+ /**
640
+ * Narrows an unknown value to a valid alternating workflow lineage.
641
+ *
642
+ * @param value - The value to inspect
643
+ * @returns True if `value` is a unique, nonempty-tagged workflow/agent chain; false otherwise
644
+ */
645
+ function isWorkflowLineage(value) {
646
+ const inspected = attempt(() => {
647
+ if (!Array.isArray(value)) return false;
648
+ const seen = /* @__PURE__ */ new Set();
649
+ for (const [index, tag] of value.entries()) {
650
+ if (!isString(tag)) return false;
651
+ const prefix = index % 2 === 0 ? "workflow:" : "agent:";
652
+ if (!tag.startsWith(prefix) || tag.length === prefix.length || seen.has(tag)) return false;
653
+ seen.add(tag);
654
+ }
655
+ return true;
656
+ });
657
+ return inspected.success && inspected.value;
658
+ }
659
+ /**
660
+ * Narrows an unknown callable to Toolbox's frozen contextual agent adapter metadata.
661
+ *
662
+ * @param value - The value to inspect
663
+ * @returns True if it is a frozen {@link AgentFunction} with a frozen valid lineage; false otherwise
664
+ */
665
+ function isAgentFunction(value) {
666
+ const inspected = attempt(() => {
667
+ if (typeof value !== "function" || !Object.isFrozen(value)) return false;
668
+ const category = Reflect.getOwnPropertyDescriptor(value, "category");
669
+ const lineage = Reflect.getOwnPropertyDescriptor(value, "lineage");
670
+ return category?.value === "agent" && lineage !== void 0 && "value" in lineage && Object.isFrozen(lineage.value) && isWorkflowLineage(lineage.value);
671
+ });
672
+ return inspected.success && inspected.value;
673
+ }
674
+ /**
675
+ * Narrows an unknown value to a {@link ColumnSpec}.
676
+ *
677
+ * @param value - The value to inspect
678
+ * @returns True if `value` is a valid {@link ColumnPrimitive} shorthand or a `{ primitive, optional }` record with a valid `primitive`; false otherwise
679
+ */
680
+ function isColumnSpec(value) {
681
+ if (isColumnPrimitive(value)) return true;
682
+ if (!isRecord(value)) return false;
683
+ return isColumnPrimitive(value.primitive) && (value.optional === void 0 || typeof value.optional === "boolean");
684
+ }
685
+ /**
686
+ * Narrows an unknown value to a {@link ColumnPrimitive}.
687
+ *
688
+ * @param value - The value to inspect
689
+ * @returns True if `value` is `'string'`, `'integer'`, `'number'`, or `'boolean'`; false otherwise
690
+ */
691
+ function isColumnPrimitive(value) {
692
+ return value === "string" || value === "integer" || value === "number" || value === "boolean";
693
+ }
694
+ /**
695
+ * Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id` and `driver`, a
696
+ * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
697
+ * `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
698
+ * {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
699
+ * before trusting it as a definition (never an `as`).
700
+ *
701
+ * @param value - The value to inspect
702
+ * @returns True if `value` is a complete {@link DatabaseDefinition}; false otherwise
703
+ */
704
+ function isDatabaseDefinition(value) {
705
+ if (!isRecord(value)) return false;
706
+ if ("keys" in value) return false;
707
+ if (!isNonEmptyString(value.id) || !isNonEmptyString(value.driver)) return false;
708
+ if (!isRecord(value.tables)) return false;
709
+ for (const table of Object.values(value.tables)) {
710
+ if (!isRecord(table) || !isRecord(table.columns)) return false;
711
+ for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
712
+ }
713
+ if (value.primary !== void 0) {
714
+ if (!isRecord(value.primary)) return false;
715
+ for (const key of Object.values(value.primary)) if (!isNonEmptyString(key)) return false;
716
+ }
717
+ if (value.indexes !== void 0) {
718
+ if (!isRecord(value.indexes)) return false;
719
+ for (const groups of Object.values(value.indexes)) {
720
+ if (!Array.isArray(groups)) return false;
721
+ for (const group of groups) {
722
+ if (!Array.isArray(group) || group.length === 0) return false;
723
+ for (const column of group) if (!isNonEmptyString(column)) return false;
724
+ }
725
+ }
726
+ }
727
+ if (value.version !== void 0 && !isFiniteNumber(value.version)) return false;
728
+ return true;
729
+ }
730
+ //#endregion
578
731
  //#region src/core/shapers.ts
579
732
  /**
580
- * The shape of {@link import('./factories.js').createPromptTool}'s call arguments — `to` names the
733
+ * Describes the shape of {@link import('./factories.js').createPromptTool}'s call arguments — `to` names the
581
734
  * terminal identity and `schema` carries the complete multi-field form document.
582
735
  *
583
736
  * @remarks
@@ -592,7 +745,7 @@ var promptToolShape = objectShape({
592
745
  schema: jsonShape({ description: "The @orkestrel/form schema to ask, with form metadata and one or more typed fields." })
593
746
  });
594
747
  /**
595
- * The shape of {@link import('./factories.js').createAnswerTool}'s call arguments — discriminated
748
+ * Describes the shape of {@link import('./factories.js').createAnswerTool}'s call arguments — discriminated
596
749
  * by `operation`: `'pending'` lists the forms addressed to this tool's terminal, while `'answer'`
597
750
  * resolves one by `id` with a complete `values` record.
598
751
  *
@@ -600,7 +753,7 @@ var promptToolShape = objectShape({
600
753
  * `values` is first bounded to exact JSON. The handler then narrows it with the originating Form
601
754
  * package's `isFormValues` guard before the terminal manager applies it to the parked live form.
602
755
  */
603
- var answerToolShape = unionShape(objectShape({ operation: literalShape(["pending"], { description: "List the forms currently addressed to this terminal." }) }), objectShape({
756
+ var answerToolShape = unionShape(objectShape({ operation: literalShape(["pending"], { description: "List the forms addressed to this terminal." }) }), objectShape({
604
757
  operation: literalShape(["answer"], { description: "Answer one pending form by id." }),
605
758
  id: stringShape({
606
759
  min: 1,
@@ -609,7 +762,7 @@ var answerToolShape = unionShape(objectShape({ operation: literalShape(["pending
609
762
  values: jsonShape({ description: "The form answers keyed by field name." })
610
763
  }));
611
764
  /**
612
- * The shape of {@link import('./types.js').AgentToolArguments} —
765
+ * Describes the shape of {@link import('./types.js').AgentToolArguments} —
613
766
  * {@link import('./factories.js').createAgentTool}'s advertised `parameters`.
614
767
  *
615
768
  * @remarks
@@ -619,7 +772,7 @@ var answerToolShape = unionShape(objectShape({ operation: literalShape(["pending
619
772
  var agentToolShape = objectShape({
620
773
  task: stringShape({
621
774
  min: 1,
622
- description: "The instructions the sub-agent should carry out."
775
+ description: "The instructions the sub-agent carries out."
623
776
  }),
624
777
  provider: optionalShape(stringShape({
625
778
  min: 1,
@@ -629,7 +782,7 @@ var agentToolShape = objectShape({
629
782
  system: optionalShape(stringShape({ description: "A system prompt seeding the sub-agent's context (overrides the default)." }))
630
783
  });
631
784
  /**
632
- * The shape of {@link import('./types.js').DescribeToolArguments} —
785
+ * Describes the shape of {@link import('./types.js').DescribeToolArguments} —
633
786
  * {@link import('./factories.js').createDescribeTool}'s advertised `parameters`.
634
787
  *
635
788
  * @remarks
@@ -640,8 +793,8 @@ var describeToolShape = objectShape({ name: stringShape({
640
793
  description: "The registered name of the tool whose full description to return."
641
794
  }) });
642
795
  /**
643
- * The shape of a {@link import('./types.js').TaskDraft} — identical to a strict task shape
644
- * EXCEPT `id` and `name` are OPTIONAL.
796
+ * Describes the shape of a {@link import('./types.js').TaskDraft} — identical to a strict task
797
+ * shape except that `id` and `name` are optional.
645
798
  */
646
799
  var taskDraftShape = objectShape({
647
800
  id: optionalShape(stringShape({
@@ -653,7 +806,7 @@ var taskDraftShape = objectShape({
653
806
  description: "Task name; defaults to the id when omitted."
654
807
  })),
655
808
  description: optionalShape(stringShape({ description: "Optional task description." })),
656
- run: optionalShape(stringShape({
809
+ behavior: optionalShape(stringShape({
657
810
  min: 1,
658
811
  description: "The registered behavior name to invoke (a registry key, not a label); omitted completes with JSON null."
659
812
  })),
@@ -668,8 +821,8 @@ var taskDraftShape = objectShape({
668
821
  }))
669
822
  });
670
823
  /**
671
- * The shape of a PHASE in a draft workflow — identical to a strict phase shape EXCEPT `id` and
672
- * `name` are OPTIONAL, and its tasks are {@link taskDraftShape}s.
824
+ * Describes the shape of a phase in a draft workflow — identical to a strict phase shape except
825
+ * that `id` and `name` are optional and each task takes {@link taskDraftShape}.
673
826
  */
674
827
  var phaseDraftShape = objectShape({
675
828
  id: optionalShape(stringShape({
@@ -689,17 +842,17 @@ var phaseDraftShape = objectShape({
689
842
  bail: optionalShape(literalShape([true, false], { description: "Per-phase failure-policy override; omitted inherits the workflow bail." }))
690
843
  });
691
844
  /**
692
- * The shape of a DRAFT workflow — identical to a strict workflow shape EXCEPT `id` and `name`
693
- * are OPTIONAL at all three levels (workflow / phase / task), so a small model can omit the six
694
- * identity strings and let the tool synthesize them positionally.
845
+ * Describes the shape of a draft workflow — identical to a strict workflow shape except that `id`
846
+ * and `name` are optional at the workflow, phase, and task levels, so a small model can omit every
847
+ * identity string and let the tool synthesize them positionally.
695
848
  *
696
849
  * @remarks
697
850
  * The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract} compiles.
698
- * `run` stays optional like the strict form; omission is the deliberate JSON `null` no-op. A
851
+ * `behavior` stays optional like the strict form; omission is the deliberate JSON `null` no-op. A
699
852
  * provided `id` / `name` still has `minLength: 1` (so
700
- * an explicitly-empty `id: ''` is REJECTED, not auto-filled). After
853
+ * an explicitly-empty `id: ''` is rejected, not auto-filled). After
701
854
  * {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
702
- * validated against the STRICT `createWorkflowContract` (`@orkestrel/workflow`) gate before
855
+ * validated against the strict `createWorkflowContract` (`@orkestrel/workflow`) gate before
703
856
  * running.
704
857
  */
705
858
  var workflowDraftShape = objectShape({
@@ -716,27 +869,29 @@ var workflowDraftShape = objectShape({
716
869
  bail: optionalShape(literalShape([true, false], { description: "Failure policy: false (default) continues gracefully, true halts on the first failure." }))
717
870
  });
718
871
  /**
719
- * The shape of ONE flat step — `{ name }` — the building block of {@link workflowStepsShape}.
872
+ * Describes the shape of one flat step — `{ name }` — the building block of
873
+ * {@link workflowStepsShape}.
720
874
  *
721
875
  * @remarks
722
- * `name` is the REGISTERED behavior name the step runs (it becomes the task's `run`). The tool
876
+ * `name` is the registered behavior name the step runs (it becomes the task's `behavior`). The tool
723
877
  * expands each step into a one-task phase, in order ({@link import('./helpers.js').expandSteps}).
724
878
  */
725
879
  var stepShape = objectShape({ name: stringShape({
726
880
  min: 1,
727
- description: "The registered behavior name this step runs (becomes the task run)."
881
+ description: "The registered behavior name this step runs (becomes the task behavior)."
728
882
  }) });
729
883
  /**
730
- * The FLAT authoring shape {@link import('./factories.js').createWorkflowTool} advertises as its
731
- * `parameters` — the simplest surface a small model can fill: `{ name?, steps: [{ name }] }`.
884
+ * Describes the flat authoring shape {@link import('./factories.js').createWorkflowTool} advertises
885
+ * as its `parameters` — the simplest surface a small model can fill:
886
+ * `{ name?, steps: [{ name }] }`.
732
887
  *
733
888
  * @remarks
734
889
  * A deliberately-reduced surface: a flat ordered list of steps, each a `{ name }`. The tool
735
- * EXPANDS it ({@link import('./helpers.js').expandSteps}) into a full
890
+ * expands it ({@link import('./helpers.js').expandSteps}) into a full
736
891
  * {@link import('./types.js').WorkflowDefinition} — one one-task phase per step, in order —
737
- * then validates against the STRICT `createWorkflowContract` (`@orkestrel/workflow`) gate. The
738
- * full nested form is STILL accepted by the tool (it branches on the args' shape) and is
739
- * documented as the advanced escape-hatch in the tool's description — but THIS is what
892
+ * then validates against the strict `createWorkflowContract` (`@orkestrel/workflow`) gate. The
893
+ * full nested form is still accepted by the tool (it branches on the args' shape) and is
894
+ * documented as the advanced escape-hatch in the tool's description — but this is what
740
895
  * `parameters` advertises.
741
896
  */
742
897
  var workflowStepsShape = objectShape({
@@ -747,20 +902,20 @@ var workflowStepsShape = objectShape({
747
902
  steps: arrayShape(stepShape, { description: "The ordered steps to run, one after another (each becomes a one-task phase)." })
748
903
  });
749
904
  /**
750
- * The shape of a {@link import('./types.js').WorkspaceOperation} — a descriptive tagged union
751
- * over the 13 workspace edit / read / navigation operations, discriminated by the `operation`
752
- * literal (never a bare `kind`; AGENTS §4.4). Each variant leads with its `operation`
753
- * discriminant then its FLAT fields, every field via `stringShape` / `optionalShape` /
905
+ * Describes the shape of a {@link import('./types.js').WorkspaceOperation} — a descriptive tagged union
906
+ * over the workspace edit, read, and navigation operations, discriminated by the `operation`
907
+ * literal (never a bare `kind`). Each variant leads with its `operation`
908
+ * discriminant then its flat fields, every field through `stringShape` / `optionalShape` /
754
909
  * `integerShape({ min: 1 })` / `booleanShape`, each carrying a strong field-level `description`.
755
910
  *
756
911
  * @remarks
757
- * The union compiles to an `anyOf` JSON Schema + a `unionOf` guard + a first-match parser
912
+ * The union compiles to an `anyOf` JSON Schema, a `unionOf` guard, and a first-match parser
758
913
  * automatically ({@link import('./factories.js').createWorkspaceTool} types the result to the
759
- * hand-written {@link import('./types.js').WorkspaceOperation}). `limit` and the four `'splice'`
760
- * caret components are POSITIVE integers (`integerShape({ min: 1 })`); `regex` / `sensitive`
761
- * are `optionalShape(booleanShape(...))`. The two REGISTRY arms — `workspaces` (list the
914
+ * hand-written {@link import('./types.js').WorkspaceOperation}). `limit` and the `'splice'`
915
+ * caret components are positive integers (`integerShape({ min: 1 })`); `regex` / `sensitive`
916
+ * are `optionalShape(booleanShape(...))`. The registry arms — `workspaces` (list the
762
917
  * workspaces the model can move between) and `switch` (re-point the active one by `id`) — let a
763
- * model DISCOVER then CHOOSE which workspace the edit / read arms target.
918
+ * model discover then choose which workspace the edit / read arms target.
764
919
  */
765
920
  var workspaceToolShape = unionShape(objectShape({
766
921
  operation: literalShape(["read"], { description: "Read a whole text file's text by path." }),
@@ -830,27 +985,37 @@ var workspaceToolShape = unionShape(objectShape({
830
985
  operation: literalShape(["switch"], { description: "Switch the active workspace to the one with this id (get ids from the \"workspaces\" operation). Edit and read operations then target it." }),
831
986
  id: stringShape({ description: "The id of the workspace to make active (from the \"workspaces\" listing)." })
832
987
  }));
833
- /** A {@link import('./types.js').ColumnKind} literal — the leaf {@link columnSpecShape} wraps. */
834
- var columnKindShape = literalShape([
988
+ /** Describes a {@link import('./types.js').ColumnPrimitive} literal — the leaf {@link columnSpecShape} wraps. */
989
+ var columnPrimitiveShape = literalShape([
835
990
  "string",
836
991
  "integer",
837
992
  "number",
838
993
  "boolean"
839
- ], { description: "A column type: \"string\" | \"integer\" | \"number\" | \"boolean\"." });
840
- /** A {@link import('./types.js').ColumnSpec} — a bare {@link columnKindShape}, or `{ type, optional }`. */
841
- var columnSpecShape = unionShape(columnKindShape, objectShape({
842
- type: columnKindShape,
994
+ ], { description: "A column primitive: \"string\" | \"integer\" | \"number\" | \"boolean\"." });
995
+ /** Describes a {@link import('./types.js').ColumnSpec} — a bare {@link columnPrimitiveShape}, or `{ primitive, optional }`. */
996
+ var columnSpecShape = unionShape(columnPrimitiveShape, objectShape({
997
+ primitive: columnPrimitiveShape,
843
998
  optional: optionalShape(booleanShape({ description: "Whether the column may be absent from a row." }))
844
999
  }));
845
- /** A {@link import('./types.js').TableSpec} — table name to `{ columns }`, each column a {@link columnSpecShape}. */
846
- var tableSpecShape = recordShape(objectShape({ columns: recordShape(columnSpecShape, { description: "Column name to its type." }) }), { description: "Table name to its column layout." });
847
- /** One key value — a string or number; the array form (multiple keys, positional) resolves FIRST per AGENTS §9.2. */
1000
+ /** Describes a {@link import('./types.js').TableSpec} — table name to `{ columns }`, each column a {@link columnSpecShape}. */
1001
+ var tableSpecShape = recordShape(objectShape({ columns: recordShape(columnSpecShape, { description: "Column name to its primitive or its { primitive, optional } spec." }) }), { description: "Table name to its column layout." });
1002
+ /**
1003
+ * Describes one key value for the database tool and the relation tool — a string or number; the
1004
+ * array form (multiple keys, positional) resolves first, so an array argument is read as many keys
1005
+ * rather than one.
1006
+ */
848
1007
  var keyShape = unionShape(arrayShape(unionShape(stringShape(), numberShape()), { description: "Multiple row keys, positional — a miss at an index is undefined there." }), stringShape({ description: "One row key." }), numberShape({ description: "One row key." }));
849
- /** A loose row — a flat object of column name to JSON value; the array form (multiple rows) resolves FIRST per AGENTS §9.2. */
1008
+ /** Describes a loose row — a flat object of column name to JSON value. */
850
1009
  var rowShape = recordShape(jsonShape(), { description: "A row as a flat object of column name to value." });
851
- /** One or many loose rows — the array form resolves FIRST per AGENTS §9.2. */
1010
+ /**
1011
+ * Describes one or many loose rows — the array form resolves first, so an array argument is read as
1012
+ * many rows rather than one.
1013
+ */
852
1014
  var rowsShape = unionShape(arrayShape(rowShape, { description: "Multiple rows." }), rowShape);
853
- /** One SERIALIZED WHERE condition — `values` is ALWAYS an array, even for a single-value operator. */
1015
+ /**
1016
+ * Describes one serialized where condition — `values` is always an array, even for a single-value
1017
+ * operator.
1018
+ */
854
1019
  var conditionShape = objectShape({
855
1020
  column: stringShape({ description: "The column this condition applies to." }),
856
1021
  operator: literalShape([
@@ -873,12 +1038,12 @@ var conditionShape = objectShape({
873
1038
  values: arrayShape(jsonShape(), { description: "The operand values the operator needs (always an array, even for one value)." }),
874
1039
  connector: optionalShape(literalShape(["and", "or"], { description: "Joins this condition to the next; omit on the last condition." }))
875
1040
  });
876
- /** One sort term. */
1041
+ /** Describes one sort term — a `column` to sort by and the `direction` to sort it in. */
877
1042
  var orderShape = objectShape({
878
1043
  column: stringShape({ description: "The column to sort by." }),
879
1044
  direction: literalShape(["ascending", "descending"], { description: "The sort direction." })
880
1045
  });
881
- /** The SERIALIZED query form — conditions, order, and pagination. */
1046
+ /** Describes the serialized query form — conditions, order, and pagination. */
882
1047
  var queryShape = objectShape({
883
1048
  conditions: optionalShape(arrayShape(conditionShape, { description: "The WHERE conditions, folded left to right." })),
884
1049
  order: optionalShape(arrayShape(orderShape, { description: "The sort terms, applied in order." })),
@@ -892,18 +1057,17 @@ var queryShape = objectShape({
892
1057
  }))
893
1058
  });
894
1059
  /**
895
- * The shape of {@link import('./factories.js').createDatabaseTool}'s call arguments —
896
- * discriminated by `operation` into the 11 database operations (`'create'` / `'tables'` /
897
- * `'get'` / `'records'` / `'count'` / `'aggregate'` / `'add'` / `'set'` / `'update'` /
898
- * `'remove'` / `'destroy'`).
1060
+ * Describes the shape of {@link import('./factories.js').createDatabaseTool}'s call arguments —
1061
+ * discriminated by `operation` into the database operations `'create'`, `'tables'`, `'get'`,
1062
+ * `'records'`, `'count'`, `'aggregate'`, `'add'`, `'set'`, `'update'`, `'remove'`, and `'destroy'`.
899
1063
  *
900
1064
  * @remarks
901
1065
  * Every arm carries `id` (the database id). `'create'` carries `tables` (the
902
- * {@link import('./types.js').TableSpec} column DSL, compiled via
903
- * {@link import('./helpers.js').expandTables}); `'get'` / `'update'` / `'remove'` carry `key`
1066
+ * {@link import('./types.js').TableSpec} column DSL, compiled through
1067
+ * {@link import('./compilers.js').expandTables}); `'get'` / `'update'` / `'remove'` carry `key`
904
1068
  * (one key or an array of keys, positional); `'add'` / `'set'` carry `row` (one row or an array of
905
1069
  * rows); `'update'` also carries `changes` (a loose partial row); `'records'` / `'count'` /
906
- * `'aggregate'` carry an optional `query` (the SERIALIZED form — `values` is ALWAYS an array,
1070
+ * `'aggregate'` carry an optional `query` (the serialized form — `values` is always an array,
907
1071
  * even for a single-value operator, so a caller never chains method calls or guesses arity).
908
1072
  */
909
1073
  var databaseToolShape = unionShape(objectShape({
@@ -1036,21 +1200,19 @@ var databaseToolShape = unionShape(objectShape({
1036
1200
  description: "The database id."
1037
1201
  })
1038
1202
  }));
1039
- /** One key value — a string or number; the array form (multiple keys, positional) resolves FIRST per AGENTS §9.2. */
1040
- var relationKeyShape = unionShape(arrayShape(unionShape(stringShape(), numberShape()), { description: "Multiple row keys, positional — a miss at an index is undefined there." }), stringShape({ description: "One row key." }), numberShape({ description: "One row key." }));
1041
- /** A single row key (not an array) — used by `'link'` / `'unlink'` / `'links'`, which address exactly one owning row. */
1203
+ /** Describes a single row key (not an array) — used by `'link'` / `'unlink'` / `'links'`, which address exactly one owning row. */
1042
1204
  var singleKeyShape = unionShape(stringShape({ description: "The owning row key." }), numberShape({ description: "The owning row key." }));
1043
- /** Flat dot-path relation include list, expanded via {@link import('./helpers.js').expandInclude}. */
1044
- var includeShape = optionalShape(arrayShape(stringShape({ description: "A dot-separated chain of relation names, e.g. \"contacts.account\"." }), { description: "Which relations to attach, as flat dot-paths." }));
1045
- /** Which registered relation manager to address — omitted resolves to the sole registered manager. */
1205
+ /** Describes the flat dot-path relation include list, expanded through {@link import('./helpers.js').expandInclude}. */
1206
+ var includeShape = optionalShape(arrayShape(stringShape({ description: "A dot-separated chain of relation names, for example \"contacts.account\"." }), { description: "Which relations to attach, as flat dot-paths." }));
1207
+ /** Describes which registered relation manager to address — omitted resolves to the sole registered manager. */
1046
1208
  var managerShape = optionalShape(stringShape({
1047
1209
  min: 1,
1048
1210
  description: "Which registered relation manager to address."
1049
1211
  }));
1050
1212
  /**
1051
- * The shape of {@link import('./factories.js').createRelationTool}'s call arguments —
1052
- * discriminated by `operation` into the 5 relation operations (`'load'` / `'find'` / `'link'` /
1053
- * `'unlink'` / `'links'`).
1213
+ * Describes the shape of {@link import('./factories.js').createRelationTool}'s call arguments —
1214
+ * discriminated by `operation` into the relation operations `'load'`, `'find'`, `'link'`,
1215
+ * `'unlink'`, and `'links'`.
1054
1216
  *
1055
1217
  * @remarks
1056
1218
  * `'load'` fetches one or more rows (positional key/array) with `include` attached. `'find'`
@@ -1064,7 +1226,7 @@ var relationToolShape = unionShape(objectShape({
1064
1226
  min: 1,
1065
1227
  description: "The model (table) name."
1066
1228
  }),
1067
- key: relationKeyShape,
1229
+ key: keyShape,
1068
1230
  include: includeShape
1069
1231
  }), objectShape({
1070
1232
  operation: literalShape(["find"], { description: "List rows, with related rows attached." }),
@@ -1127,7 +1289,7 @@ var relationToolShape = unionShape(objectShape({
1127
1289
  })
1128
1290
  }));
1129
1291
  /**
1130
- * The shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
1292
+ * Describes the shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
1131
1293
  * example `samples` to infer a JSON Schema from, plus per-call `format` / `enum` toggles and an
1132
1294
  * optional `candidates` array to check against the inferred schema.
1133
1295
  *
@@ -1135,8 +1297,8 @@ var relationToolShape = unionShape(objectShape({
1135
1297
  * `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
1136
1298
  * surfaced by the handler as a typed `TOOL` {@link import('./errors.js').ToolboxError}. When
1137
1299
  * `candidates` is present (any array, including empty), the handler compiles a contract from the
1138
- * freshly inferred schema and checks each candidate against it with a STRICT guard (`.is`, no
1139
- * coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s NORMALIZING
1300
+ * freshly inferred schema and checks each candidate against it with a strict guard (`.is`, no
1301
+ * coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s normalizing
1140
1302
  * `.parse` enforcement.
1141
1303
  */
1142
1304
  var inferToolShape = objectShape({
@@ -1151,24 +1313,24 @@ var inferToolShape = objectShape({
1151
1313
  //#endregion
1152
1314
  //#region src/core/helpers.ts
1153
1315
  /**
1154
- * The ancestry identifier of a workflow in a run chain — `workflow:<id>`.
1316
+ * Returns the ancestry identifier of a workflow in a run chain — `workflow:<id>`.
1155
1317
  *
1156
1318
  * @remarks
1157
- * Namespacing keeps a workflow id and an {@link agentTag} agent name in ONE set without
1158
- * collision, so re-entering a workflow OR an agent already in the chain is a single `includes`
1319
+ * Namespacing keeps a workflow id and a {@link tagAgent} agent name in one set without
1320
+ * collision, so re-entering a workflow or an agent already in the chain is a single `includes`
1159
1321
  * check.
1160
1322
  *
1161
1323
  * @param id - The workflow definition's `id`
1162
1324
  * @returns The namespaced ancestry tag (`workflow:<id>`)
1163
1325
  */
1164
- function workflowTag(id) {
1326
+ function tagWorkflow(id) {
1165
1327
  return `workflow:${id}`;
1166
1328
  }
1167
1329
  /**
1168
- * The ancestry identifier of an agent in a run chain — `agent:<name>`.
1330
+ * Returns the ancestry identifier of an agent in a run chain — `agent:<name>`.
1169
1331
  *
1170
1332
  * @remarks
1171
- * The agent counterpart of {@link workflowTag}: {@link import('./factories.js').createAgentFunction}
1333
+ * The agent counterpart of {@link tagWorkflow}: {@link import('./factories.js').createAgentFunction}
1172
1334
  * / {@link import('./factories.js').createWorkflowTool} guard against re-entering an agent or
1173
1335
  * workflow already in the chain (this package's typed `DEPTH` `ToolboxError`). The
1174
1336
  * `agent:` namespace keeps it distinct from a same-string workflow id.
@@ -1176,88 +1338,53 @@ function workflowTag(id) {
1176
1338
  * @param name - The agent's identifier / registry name
1177
1339
  * @returns The namespaced ancestry tag (`agent:<name>`)
1178
1340
  */
1179
- function agentTag(name) {
1341
+ function tagAgent(name) {
1180
1342
  return `agent:${name}`;
1181
1343
  }
1182
1344
  /**
1183
- * Narrow an unknown value to a valid alternating workflow lineage.
1184
- *
1185
- * @param value - The value to inspect
1186
- * @returns Whether `value` is a unique, nonempty-tagged workflow/agent chain
1187
- */
1188
- function isWorkflowLineage(value) {
1189
- const inspected = attempt(() => {
1190
- if (!Array.isArray(value)) return false;
1191
- const seen = /* @__PURE__ */ new Set();
1192
- for (const [index, tag] of value.entries()) {
1193
- if (!isString(tag)) return false;
1194
- const prefix = index % 2 === 0 ? "workflow:" : "agent:";
1195
- if (!tag.startsWith(prefix) || tag.length === prefix.length || seen.has(tag)) return false;
1196
- seen.add(tag);
1197
- }
1198
- return true;
1199
- });
1200
- return inspected.success && inspected.value;
1201
- }
1202
- /**
1203
- * Build a validated, copied, and frozen workflow lineage value.
1345
+ * Returns the canonical workflow lineage — validated, copied, and frozen.
1204
1346
  *
1205
1347
  * @param lineage - The configured chain; omitted means a direct root
1206
1348
  * @returns An immutable caller-isolated lineage
1207
1349
  */
1208
- function lineageOf(lineage = []) {
1350
+ function normalizeLineage(lineage = []) {
1209
1351
  if (!isWorkflowLineage(lineage)) throw new ToolboxError("TOOL", "workflow lineage must alternate unique workflow and agent tags");
1210
1352
  return Object.freeze([...lineage]);
1211
1353
  }
1212
1354
  /**
1213
- * Append one tag to a workflow lineage and return a frozen copy.
1355
+ * Appends one tag to a workflow lineage and returns a frozen copy.
1214
1356
  *
1215
1357
  * @param lineage - The valid chain to extend
1216
1358
  * @param tag - The workflow or agent tag to append
1217
1359
  * @returns The validated immutable extension
1218
1360
  */
1219
1361
  function extendLineage(lineage, tag) {
1220
- return lineageOf([...lineage, tag]);
1362
+ return normalizeLineage([...lineage, tag]);
1221
1363
  }
1222
1364
  /**
1223
- * Derive the zero-based workflow nesting depth from a valid lineage.
1365
+ * Derives the zero-based workflow nesting depth from a valid lineage.
1224
1366
  *
1225
1367
  * @param lineage - The workflow/agent chain
1226
1368
  * @returns Zero for an empty/root lineage, then one per nested workflow
1227
1369
  */
1228
1370
  function deriveWorkflowDepth(lineage) {
1229
- const workflows = lineageOf(lineage).filter((tag) => tag.startsWith("workflow:")).length;
1371
+ const workflows = normalizeLineage(lineage).filter((tag) => tag.startsWith("workflow:")).length;
1230
1372
  return Math.max(0, workflows - 1);
1231
1373
  }
1232
1374
  /**
1233
- * Narrow an unknown callable to Toolbox's frozen contextual agent adapter metadata.
1234
- *
1235
- * @param value - The value to inspect
1236
- * @returns Whether it is a frozen {@link AgentFunction} with a frozen valid lineage
1237
- */
1238
- function isAgentFunction(value) {
1239
- const inspected = attempt(() => {
1240
- if (typeof value !== "function" || !Object.isFrozen(value)) return false;
1241
- const category = Reflect.getOwnPropertyDescriptor(value, "category");
1242
- const lineage = Reflect.getOwnPropertyDescriptor(value, "lineage");
1243
- return category?.value === "agent" && lineage !== void 0 && "value" in lineage && Object.isFrozen(lineage.value) && isWorkflowLineage(lineage.value);
1244
- });
1245
- return inspected.success && inspected.value;
1246
- }
1247
- /**
1248
- * Build the plain success summary {@link import('./factories.js').createWorkflowTool} returns on
1249
- * a completed run — the universal tool-handler contract (AGENTS §14): return a plain value on
1250
- * success, appearing identically over BOTH the agent loop and MCP.
1375
+ * Builds the plain success summary {@link import('./factories.js').createWorkflowTool} returns on a
1376
+ * completed run — the universal tool-handler contract: return a plain value on success, appearing
1377
+ * identically over both the agent loop and MCP.
1251
1378
  *
1252
1379
  * @remarks
1253
- * The summary is LEAN: the workflow's terminal `status`, settled-task count, and exact optional
1380
+ * The summary is lean: the workflow's terminal `status`, settled-task count, and exact optional
1254
1381
  * native persistence outcome. It carries no synthetic `id` / `name`: a tool handler has no call
1255
1382
  * id; the `ToolManagerInterface` (`@orkestrel/tool`) supplies the canonical envelope's identity.
1256
1383
  *
1257
1384
  * @param result - The terminal `WorkflowResult` (`@orkestrel/workflow`) the run produced
1258
1385
  * @returns The plain success summary — `{ status, count, durable?, fault? }`
1259
1386
  */
1260
- function workflowToolSummary(result) {
1387
+ function summarizeWorkflow(result) {
1261
1388
  return {
1262
1389
  status: result.status,
1263
1390
  count: result.results.length,
@@ -1266,21 +1393,21 @@ function workflowToolSummary(result) {
1266
1393
  };
1267
1394
  }
1268
1395
  /**
1269
- * Complete a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesize any
1270
- * MISSING `id` deterministically + positionally, and default any MISSING `name` to its
1271
- * (now-resolved) `id`.
1396
+ * Completes a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesizes a
1397
+ * missing `id` deterministically and positionally, and defaults a missing `name` to its resolved
1398
+ * `id`.
1272
1399
  *
1273
1400
  * @remarks
1274
1401
  * The positional id scheme is stable and human-legible: the workflow is `wf`, phase `i` is
1275
1402
  * `phase-<i>`, and task `j` of that phase is `<phaseId>-task-<j>` (so a provided phase id flows
1276
- * into its tasks' synthesized ids). A PROVIDED `id` / `name` at any level is kept VERBATIM —
1403
+ * into its tasks' synthesized ids). A provided `id` / `name` at any level is kept verbatim —
1277
1404
  * synthesis touches only the omitted ones. A missing `name` defaults to the resolved `id` (never
1278
- * the other way round), so the result always has both. `run`, `description`, the per-phase
1405
+ * the other way round), so the result always carries an `id` and a `name`. `behavior`, `description`, the per-phase
1279
1406
  * `concurrency` / `bail`, the per-task `retries` / `timeout`, and the workflow `bail` carry over
1280
1407
  * unchanged. The result is a complete {@link WorkflowDefinition}; the caller still validates it
1281
- * against the STRICT contract.
1408
+ * against the strict contract.
1282
1409
  *
1283
- * @param draft - The draft workflow (id/name optional at all three levels)
1410
+ * @param draft - The draft workflow (`id` and `name` optional at the workflow, phase, and task levels)
1284
1411
  * @returns A complete {@link WorkflowDefinition} with every id/name filled
1285
1412
  */
1286
1413
  function completeDraft(draft) {
@@ -1294,7 +1421,7 @@ function completeDraft(draft) {
1294
1421
  };
1295
1422
  }
1296
1423
  /**
1297
- * Complete one {@link PhaseDraft} into a strict phase definition — the per-phase step of
1424
+ * Completes one {@link PhaseDraft} into a strict phase definition — the per-phase step of
1298
1425
  * {@link completeDraft} (phase `index` → `phase-<index>` when its id is omitted).
1299
1426
  *
1300
1427
  * @param phase - The draft phase
@@ -1313,7 +1440,7 @@ function completePhaseDraft(phase, index) {
1313
1440
  };
1314
1441
  }
1315
1442
  /**
1316
- * Complete one {@link TaskDraft} into a strict task definition — the per-task leaf step of
1443
+ * Completes one {@link TaskDraft} into a strict task definition — the per-task leaf step of
1317
1444
  * {@link completeDraft} (task `index` of phase `<phaseId>` → `<phaseId>-task-<index>` when its id
1318
1445
  * is omitted).
1319
1446
  *
@@ -1328,25 +1455,25 @@ function completeTaskDraft(task, phaseId, index) {
1328
1455
  id,
1329
1456
  name: task.name ?? id,
1330
1457
  ...task.description === void 0 ? {} : { description: task.description },
1331
- ...task.run === void 0 ? {} : { run: task.run },
1458
+ ...task.behavior === void 0 ? {} : { behavior: task.behavior },
1332
1459
  ...task.retries === void 0 ? {} : { retries: task.retries },
1333
1460
  ...task.timeout === void 0 ? {} : { timeout: task.timeout }
1334
1461
  };
1335
1462
  }
1336
1463
  /**
1337
- * Expand a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each step
1338
- * becomes a one-task phase, IN ORDER.
1464
+ * Expands a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each step
1465
+ * becomes a one-task phase, in order.
1339
1466
  *
1340
1467
  * @remarks
1341
- * The expansion of the tool's ADVERTISED surface: the deliberately-reduced flat form. Each
1468
+ * The expansion of the tool's advertised surface: the deliberately-reduced flat form. Each
1342
1469
  * {@link import('./types.js').WorkflowStep} maps to a phase holding exactly one task: the step's
1343
- * `name` becomes the task's `run` (the behavior-registry key). Ids/names are auto-filled
1470
+ * `name` becomes the task's `behavior` (the behavior-registry key). Ids/names are auto-filled
1344
1471
  * positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates to
1345
- * {@link completeDraft}, so the two lenient surfaces share ONE synthesis path (step `i` → phase
1472
+ * {@link completeDraft}, so the lenient surfaces share one synthesis path (step `i` → phase
1346
1473
  * `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes both the workflow's
1347
1474
  * deterministic id and its name, so named flat workflows retain distinct persistence keys;
1348
1475
  * omission keeps the shared draft fallback `wf`. The result is a complete definition the caller
1349
- * validates against the STRICT contract before running.
1476
+ * validates against the strict contract before running.
1350
1477
  *
1351
1478
  * @param flat - The flat steps blob (`{ name?, steps: [{ name }] }`)
1352
1479
  * @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
@@ -1357,12 +1484,12 @@ function expandSteps(flat) {
1357
1484
  id: flat.name,
1358
1485
  name: flat.name
1359
1486
  },
1360
- phases: flat.steps.map((step) => ({ tasks: [{ run: step.name }] }))
1487
+ phases: flat.steps.map((step) => ({ tasks: [{ behavior: step.name }] }))
1361
1488
  });
1362
1489
  }
1363
1490
  /**
1364
- * Map a caught error to the {@link ToolboxErrorCode} the terminal-tool factory should throw
1365
- * with — the pure classification step of that factory's error handling.
1491
+ * Maps a caught error to the {@link ToolboxErrorCode} the terminal-tool factory throws with — the
1492
+ * pure classification step of that factory's error handling.
1366
1493
  *
1367
1494
  * @remarks
1368
1495
  * Narrows `error` with {@link isTerminalError} (`@orkestrel/terminal`) first: a non-`TerminalError`
@@ -1375,119 +1502,46 @@ function expandSteps(flat) {
1375
1502
  * @param error - The value caught from a terminal-manager operation (`ask` / `answer` / …)
1376
1503
  * @returns The mapped {@link ToolboxErrorCode}, or `undefined` if `error` is not a `TerminalError`
1377
1504
  */
1378
- function terminalToolCode(error) {
1505
+ function inferTerminalCode(error) {
1379
1506
  if (!isTerminalError(error)) return void 0;
1380
1507
  if (error.code === "DEADLOCK") return "DEADLOCK";
1381
1508
  if (error.code === "EXPIRE") return "EXPIRE";
1382
1509
  return "TOOL";
1383
1510
  }
1384
- /** Narrow an unknown value to a {@link ColumnSpec} — a valid {@link import('./types.js').ColumnKind} shorthand, or `{ type, optional }` with a valid `type`. */
1385
- function isColumnSpec(value) {
1386
- if (isColumnKind(value)) return true;
1387
- if (!isRecord(value)) return false;
1388
- return isColumnKind(value.type) && (value.optional === void 0 || typeof value.optional === "boolean");
1389
- }
1390
- /** Narrow an unknown value to a {@link import('./types.js').ColumnKind}. */
1391
- function isColumnKind(value) {
1392
- return value === "string" || value === "integer" || value === "number" || value === "boolean";
1393
- }
1394
- /**
1395
- * Compile a {@link TableSpec} into the `@orkestrel/database` {@link TableMap} it configures —
1396
- * each {@link ColumnSpec} maps to the matching primitive shaper (`'string'` → `stringShape()`,
1397
- * `'integer'` → `integerShape()`, `'number'` → `numberShape()`, `'boolean'` → `booleanShape()`),
1398
- * wrapped in `optionalShape` when the column declares `optional: true`. Total, pure.
1399
- *
1400
- * @param spec - The small-model-facing table layout
1401
- * @returns The compiled `TableMap` a `@orkestrel/database` `createDatabase` call accepts
1402
- */
1403
- function expandTables(spec) {
1404
- const tables = {};
1405
- for (const [table, definition] of Object.entries(spec)) {
1406
- const columns = {};
1407
- for (const [column, kind] of Object.entries(definition.columns)) columns[column] = columnShape(kind);
1408
- tables[table] = columns;
1409
- }
1410
- return tables;
1411
- }
1412
- /** Compile one {@link ColumnSpec} into its `@orkestrel/database` column shape — the per-column leaf {@link expandTables} maps over. */
1413
- function columnShape(spec) {
1414
- const kind = isString(spec) ? spec : spec.type;
1415
- const optional = !isString(spec) && spec.optional === true;
1416
- const shape = kindShape(kind);
1417
- return optional ? optionalShape(shape) : shape;
1418
- }
1419
- /** Map one {@link import('./types.js').ColumnKind} to its primitive `@orkestrel/database` shape — the leaf {@link columnShape} wraps. */
1420
- function kindShape(kind) {
1421
- if (kind === "string") return stringShape();
1422
- if (kind === "integer") return integerShape();
1423
- if (kind === "number") return numberShape();
1424
- return booleanShape();
1425
- }
1426
- /**
1427
- * Narrow an unknown value to a {@link DatabaseDefinition} — a non-empty `id` + `driver`, a
1428
- * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
1429
- * `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
1430
- * {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
1431
- * before trusting it as a definition (never an `as`).
1432
- */
1433
- function isDatabaseDefinition(value) {
1434
- if (!isRecord(value)) return false;
1435
- if ("keys" in value) return false;
1436
- if (!isNonEmptyString(value.id) || !isNonEmptyString(value.driver)) return false;
1437
- if (!isRecord(value.tables)) return false;
1438
- for (const table of Object.values(value.tables)) {
1439
- if (!isRecord(table) || !isRecord(table.columns)) return false;
1440
- for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
1441
- }
1442
- if (value.primary !== void 0) {
1443
- if (!isRecord(value.primary)) return false;
1444
- for (const key of Object.values(value.primary)) if (!isNonEmptyString(key)) return false;
1445
- }
1446
- if (value.indexes !== void 0) {
1447
- if (!isRecord(value.indexes)) return false;
1448
- for (const groups of Object.values(value.indexes)) {
1449
- if (!Array.isArray(groups)) return false;
1450
- for (const group of groups) {
1451
- if (!Array.isArray(group) || group.length === 0) return false;
1452
- for (const column of group) if (!isNonEmptyString(column)) return false;
1453
- }
1454
- }
1455
- }
1456
- if (value.version !== void 0 && !isFiniteNumber(value.version)) return false;
1457
- return true;
1458
- }
1459
1511
  /**
1460
- * Map a caught error to the {@link ToolboxErrorCode} the upcoming database tool should throw
1461
- * with — the pure classification step of that factory's error handling, mirroring
1462
- * {@link terminalToolCode}'s idiom for `@orkestrel/database`.
1512
+ * Maps a caught error to the granular {@link DatabaseErrorCode} (`@orkestrel/database`) the code
1513
+ * {@link import('./factories.js').createDatabaseTool} throws with — the pure classification step
1514
+ * of that factory's error handling, mirroring {@link inferTerminalCode}'s idiom for
1515
+ * `@orkestrel/database`.
1463
1516
  *
1464
1517
  * @param error - The value caught from a `@orkestrel/database` table operation
1465
1518
  * @returns The granular {@link DatabaseErrorCode}, or `undefined` if `error` is not a `DatabaseError`
1466
1519
  */
1467
- function databaseToolCode(error) {
1520
+ function inferDatabaseCode(error) {
1468
1521
  return isDatabaseError(error) ? error.code : void 0;
1469
1522
  }
1470
1523
  /**
1471
- * Map a caught error to the {@link ToolboxErrorCode} the upcoming relation tool should throw
1472
- * with — the pure classification step of that factory's error handling, mirroring
1473
- * {@link terminalToolCode}'s idiom for `@orkestrel/relation`.
1524
+ * Maps a caught error to the granular {@link RelationErrorCode} (`@orkestrel/relation`) the code
1525
+ * {@link import('./factories.js').createRelationTool} throws with — the pure classification step
1526
+ * of that factory's error handling, mirroring {@link inferTerminalCode}'s idiom for
1527
+ * `@orkestrel/relation`.
1474
1528
  *
1475
1529
  * @param error - The value caught from a `@orkestrel/relation` operation
1476
1530
  * @returns The granular {@link RelationErrorCode}, or `undefined` if `error` is not a `RelationError`
1477
1531
  */
1478
- function relationToolCode(error) {
1532
+ function inferRelationCode(error) {
1479
1533
  return isRelationError(error) ? error.code : void 0;
1480
1534
  }
1481
1535
  /**
1482
- * Expand the relation tool's FLAT dot-path `include` list into a live `@orkestrel/relation`
1536
+ * Expands the relation tool's flat dot-path `include` list into a live `@orkestrel/relation`
1483
1537
  * {@link Include} tree — the pure leaf {@link import('./factories.js').createRelationTool} calls
1484
1538
  * before a `'load'` / `'find'` call.
1485
1539
  *
1486
1540
  * @remarks
1487
1541
  * Each path splits on `'.'` into a chain of relation names, deep-merged into one nested
1488
- * `Include` object with a leaf `true`. A longer path SUBSUMES a shorter sibling's bare `true` —
1542
+ * `Include` object with a leaf `true`. A longer path subsumes a shorter sibling's bare `true` —
1489
1543
  * `'contacts'` followed by `'contacts.account'` yields `{ contacts: { account: true } }`, never
1490
- * overwriting the deeper chain. An EMPTY segment (`''`, from a leading/trailing/doubled `.`) or a
1544
+ * overwriting the deeper chain. An empty segment (`''`, from a leading/trailing/doubled `.`) or a
1491
1545
  * path whose segment count exceeds `depth` throws a typed `TOOL` {@link ToolboxError}.
1492
1546
  *
1493
1547
  * @param paths - The flat dot-path `include` list (or `undefined` — yields `{}`)
@@ -1550,20 +1604,20 @@ function expandInclude(paths, depth) {
1550
1604
  return include;
1551
1605
  }
1552
1606
  /**
1553
- * Resolve which registered {@link RelationManagerInterface} a relation-tool call addresses — the
1607
+ * Resolves which registered {@link RelationManagerInterface} a relation-tool call addresses — the
1554
1608
  * pure manager-resolution leaf {@link import('./factories.js').createRelationTool} calls on
1555
1609
  * every operation.
1556
1610
  *
1557
1611
  * @remarks
1558
1612
  * An explicit `name` must match a key of `managers` (a miss throws a typed `TOOL`
1559
- * {@link ToolboxError} naming the registered managers). An OMITTED `name` resolves to the sole
1613
+ * {@link ToolboxError} naming the registered managers). An omitted `name` resolves to the sole
1560
1614
  * registered manager when exactly one is registered, else throws the same typed error.
1561
1615
  *
1562
1616
  * @param managers - The tool's registered `RelationManagerInterface` map
1563
1617
  * @param name - The call's optional `manager` field
1564
1618
  * @returns The resolved {@link RelationManagerInterface}
1565
1619
  */
1566
- function relationManagerOf(managers, name) {
1620
+ function resolveRelationManager(managers, name) {
1567
1621
  if (name !== void 0) {
1568
1622
  const manager = managers[name];
1569
1623
  if (manager === void 0) throw new ToolboxError("TOOL", `unknown relation manager '${name}'`, {
@@ -1581,35 +1635,35 @@ function relationManagerOf(managers, name) {
1581
1635
  throw new ToolboxError("TOOL", "no relation manager resolved for the call", { managers: names });
1582
1636
  }
1583
1637
  /**
1584
- * Resolve a `model` name against a live {@link RelationManagerInterface} — the pure model-lookup
1638
+ * Resolves a `model` name against a live {@link RelationManagerInterface} — the pure model-lookup
1585
1639
  * leaf {@link import('./factories.js').createRelationTool} calls on every operation, mirroring
1586
- * {@link relationManagerOf}'s guard shape.
1640
+ * {@link resolveRelationManager}'s guard shape.
1587
1641
  *
1588
1642
  * @param manager - The resolved {@link RelationManagerInterface}
1589
1643
  * @param name - The call's `model` field
1590
1644
  * @returns The model's {@link ModelInterface}
1591
1645
  */
1592
- function relationModelOf(manager, name) {
1646
+ function resolveRelationModel(manager, name) {
1593
1647
  if (!manager.has(name)) throw new ToolboxError("TOOL", `unknown model '${name}'`, {
1594
1648
  model: name,
1595
- models: manager.models()
1649
+ models: manager.names()
1596
1650
  });
1597
1651
  return manager.model(name);
1598
1652
  }
1599
1653
  /**
1600
- * Normalize the database tool's parsed SERIALIZED query into a live `@orkestrel/database`
1601
- * {@link QueryInput} — default each condition's OMITTED `connector` to `'and'`.
1654
+ * Returns the canonical live `@orkestrel/database` {@link QueryInput} for the database tool's
1655
+ * parsed serialized query — each condition's omitted `connector` defaults to `'and'`.
1602
1656
  *
1603
1657
  * @remarks
1604
1658
  * The wire form ({@link import('./shapers.js').databaseToolShape}) lets a caller drop `connector`
1605
- * on the last condition (it has nothing to join FORWARD to); the compiled `Condition` a live
1659
+ * on the last condition (it has nothing to join forward to); the compiled `Condition` a live
1606
1660
  * `@orkestrel/database` table call accepts always carries one, so this fills the gap. `order` /
1607
1661
  * `limit` / `offset` pass through unchanged. Pure and total.
1608
1662
  *
1609
1663
  * @param query - The parsed query (or `undefined`)
1610
1664
  * @returns The equivalent live `QueryInput`, or `undefined` when `query` is `undefined`
1611
1665
  */
1612
- function queryOf(query) {
1666
+ function normalizeQuery(query) {
1613
1667
  if (query === void 0) return void 0;
1614
1668
  const conditions = query.conditions?.map((condition) => ({
1615
1669
  ...condition,
@@ -1623,14 +1677,39 @@ function queryOf(query) {
1623
1677
  };
1624
1678
  }
1625
1679
  /**
1626
- * Clamp a `'records'` call's query to a row cap, and build the PROBE query the caller reads
1627
- * with — the pure leaf {@link import('./factories.js').createDatabaseTool}'s `'records'` operation
1628
- * uses to detect truncation without a separate `count` round trip.
1680
+ * Picks the effective row limit a tool reads with — the requested count when it sits inside the
1681
+ * cap, the cap when it exceeds it, and `0` when either falls below zero.
1629
1682
  *
1630
1683
  * @remarks
1631
- * The effective limit is `min(query?.limit ?? cap, cap)`, floored at `0` (so a caller can never
1632
- * exceed the configured cap by supplying a larger `query.limit`). The returned probe query
1633
- * requests ONE MORE row than the effective limit (`limit: effective + 1`) — if storage returns
1684
+ * The one place the database tool's `'records'` clamp ({@link clampQuery}) and the relation tool's
1685
+ * `'find'` / `'links'` truncation both take their ceiling from, so a negative construction-time
1686
+ * option can never reach a slice. Pure and total.
1687
+ *
1688
+ * @example
1689
+ * ```ts
1690
+ * import { resolveLimit } from '@src/core'
1691
+ *
1692
+ * resolveLimit(undefined, 100) // 100 — an omitted request takes the cap
1693
+ * resolveLimit(500, 100) // 100 — a request over the cap is clamped down
1694
+ * resolveLimit(-1, 100) // 0 — a negative request floors at zero
1695
+ * ```
1696
+ *
1697
+ * @param requested - The caller's requested row count (or `undefined`)
1698
+ * @param cap - The row-count ceiling
1699
+ * @returns The effective row limit, never below `0` and never above `cap`
1700
+ */
1701
+ function resolveLimit(requested, cap) {
1702
+ return Math.max(0, Math.min(requested ?? cap, cap));
1703
+ }
1704
+ /**
1705
+ * Clamps a `'records'` call's query to a row cap, and builds the probe query the caller reads with
1706
+ * — the pure leaf {@link import('./factories.js').createDatabaseTool}'s `'records'` operation uses
1707
+ * to detect truncation without a separate `count` round trip.
1708
+ *
1709
+ * @remarks
1710
+ * {@link resolveLimit} picks the effective limit, so a caller can never exceed the configured cap
1711
+ * by supplying a larger `query.limit`, and can never drive it below `0`. The returned probe query
1712
+ * requests one more row than the effective limit (`limit: effective + 1`) — if storage returns
1634
1713
  * that many, the caller knows the true result was truncated (`rows.length > effective`) and slices
1635
1714
  * back down to `effective` before returning.
1636
1715
  *
@@ -1646,10 +1725,10 @@ function queryOf(query) {
1646
1725
  *
1647
1726
  * @param query - The live query to clamp (or `undefined`)
1648
1727
  * @param cap - The row-count ceiling
1649
- * @returns The PROBE query (`limit` bumped by one) and the effective `limit`
1728
+ * @returns The probe query (`limit` bumped by one) and the effective `limit`
1650
1729
  */
1651
1730
  function clampQuery(query, cap) {
1652
- const limit = Math.max(0, Math.min(query?.limit ?? cap, cap));
1731
+ const limit = resolveLimit(query?.limit, cap);
1653
1732
  return {
1654
1733
  query: {
1655
1734
  ...query,
@@ -1659,28 +1738,77 @@ function clampQuery(query, cap) {
1659
1738
  };
1660
1739
  }
1661
1740
  //#endregion
1741
+ //#region src/core/compilers.ts
1742
+ /**
1743
+ * Compiles a {@link TableSpec} into the `@orkestrel/database` {@link TableMap} it configures —
1744
+ * each {@link ColumnSpec} maps to the matching primitive shaper (`'string'` → `stringShape()`,
1745
+ * `'integer'` → `integerShape()`, `'number'` → `numberShape()`, `'boolean'` → `booleanShape()`),
1746
+ * wrapped in `optionalShape` when the column declares `optional: true`. Total, pure.
1747
+ *
1748
+ * @param spec - The small-model-facing table layout
1749
+ * @returns The compiled `TableMap` a `@orkestrel/database` `createDatabase` call accepts
1750
+ */
1751
+ function expandTables(spec) {
1752
+ const tables = {};
1753
+ for (const [table, definition] of Object.entries(spec)) {
1754
+ const columns = {};
1755
+ for (const [column, columnSpec] of Object.entries(definition.columns)) columns[column] = compileColumn(columnSpec);
1756
+ tables[table] = columns;
1757
+ }
1758
+ return tables;
1759
+ }
1760
+ /**
1761
+ * Compiles one {@link ColumnSpec} into its `@orkestrel/database` column shape — the per-column leaf
1762
+ * {@link expandTables} maps over.
1763
+ *
1764
+ * @param spec - The column spec to compile
1765
+ * @returns The `@orkestrel/database` column shape, wrapped in `optionalShape` when the spec declares `optional: true`
1766
+ */
1767
+ function compileColumn(spec) {
1768
+ const primitive = isString(spec) ? spec : spec.primitive;
1769
+ const optional = !isString(spec) && spec.optional === true;
1770
+ const shape = compileColumnPrimitive(primitive);
1771
+ return optional ? optionalShape(shape) : shape;
1772
+ }
1773
+ /**
1774
+ * Compiles one {@link import('./types.js').ColumnPrimitive} into its primitive
1775
+ * `@orkestrel/database` shape — the leaf {@link compileColumn} wraps.
1776
+ *
1777
+ * @param primitive - The declared column primitive
1778
+ * @returns The matching primitive `@orkestrel/database` shape
1779
+ */
1780
+ function compileColumnPrimitive(primitive) {
1781
+ if (primitive === "string") return stringShape();
1782
+ if (primitive === "integer") return integerShape();
1783
+ if (primitive === "number") return numberShape();
1784
+ return booleanShape();
1785
+ }
1786
+ //#endregion
1662
1787
  //#region src/core/stores/MemoryDefinitionStore.ts
1663
1788
  /**
1664
- * The in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of
1665
- * {@link DatabaseDefinition}s keyed by database id, the DEFAULT store
1666
- * {@link import('../factories.js').createMemoryDefinitionStore} builds. The EXACT twin of
1667
- * {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}.
1789
+ * Represents the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of
1790
+ * {@link DatabaseDefinition}s keyed by database id, the default store
1791
+ * {@link import('../factories.js').createMemoryDefinitionStore} builds. It implements the same
1792
+ * {@link DefinitionStoreInterface} contract as
1793
+ * {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}: this store copies on
1794
+ * write and on read; the table-backed store narrows an untrusted stored blob and reports
1795
+ * `undefined` for a malformed one.
1668
1796
  *
1669
1797
  * @remarks
1670
- * A plain `Map<string, DatabaseDefinition>` (AGENTS §21 — the definition is already pure,
1671
- * self-contained CONFIG-only JSON, so no encoding is needed for the memory tier). Values are
1672
- * structured-cloned on both write and read, keeping caller mutation outside the store. There is NO
1673
- * idle-TTL and NO eviction: a persisted definition lives until an explicit `delete`. A durable
1674
- * backend (JSON / SQLite / IndexedDB) swaps in through the SAME interface without touching a
1798
+ * A plain `Map<string, DatabaseDefinition>` (the definition is already pure,
1799
+ * self-contained config-only JSON, so no encoding is needed for the memory tier). Values are
1800
+ * structured-cloned on both write and read, keeping caller mutation outside the store. There is no
1801
+ * idle-TTL and no eviction: a persisted definition lives until an explicit `delete`. A durable
1802
+ * backend (JSON / SQLite / IndexedDB) swaps in through the same interface without touching a
1675
1803
  * consumer — its driver-pluggable twin is
1676
1804
  * {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore} (the definition as one
1677
1805
  * opaque JSON column).
1678
1806
  *
1679
1807
  * - **`get` resolves the persisted definition for an id**, or `undefined` if none is stored.
1680
- * - **`set` inserts / replaces under the definition's OWN `id`** (no separate id param).
1808
+ * - **`set` inserts / replaces under the definition's own `id`** (no separate id param).
1681
1809
  * - **`delete` drops a definition by id**; an absent id is a no-op (no throw).
1682
1810
  *
1683
- * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method
1811
+ * The public surface is exactly `get` / `set` / `delete` — no extra members (the method
1684
1812
  * bijection with {@link DefinitionStoreInterface}).
1685
1813
  *
1686
1814
  * @example
@@ -1695,14 +1823,32 @@ function clampQuery(query, cap) {
1695
1823
  */
1696
1824
  var MemoryDefinitionStore = class {
1697
1825
  #definitions = /* @__PURE__ */ new Map();
1826
+ /**
1827
+ * Resolves the persisted definition for `id`, copied out of the backing `Map`.
1828
+ *
1829
+ * @param id - The database id to read
1830
+ * @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored
1831
+ */
1698
1832
  get(id) {
1699
1833
  const definition = this.#definitions.get(id);
1700
1834
  return Promise.resolve(definition === void 0 ? void 0 : structuredClone(definition));
1701
1835
  }
1836
+ /**
1837
+ * Inserts or replaces a definition under its own `id`, copied into the backing `Map`.
1838
+ *
1839
+ * @param definition - The config to persist; its own `id` is the key (no separate id param)
1840
+ * @returns A promise settling after the definition is stored
1841
+ */
1702
1842
  set(definition) {
1703
1843
  this.#definitions.set(definition.id, structuredClone(definition));
1704
1844
  return Promise.resolve();
1705
1845
  }
1846
+ /**
1847
+ * Drops the definition stored under `id`.
1848
+ *
1849
+ * @param id - The database id to drop; an absent id is a no-op, never a throw
1850
+ * @returns A promise settling after no definition is stored under `id`
1851
+ */
1706
1852
  delete(id) {
1707
1853
  this.#definitions.delete(id);
1708
1854
  return Promise.resolve();
@@ -1711,34 +1857,34 @@ var MemoryDefinitionStore = class {
1711
1857
  //#endregion
1712
1858
  //#region src/core/stores/DatabaseDefinitionStore.ts
1713
1859
  /**
1714
- * A {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database` layer — a
1715
- * database's durable CONFIG state IS a row, so persistence reduces to keyed point-access
1860
+ * Represents a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database` layer — a
1861
+ * database's durable config state is a row, so persistence reduces to keyed point-access
1716
1862
  * (`get` / `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the
1717
1863
  * plain-`Map` {@link import('./MemoryDefinitionStore.js').MemoryDefinitionStore}.
1718
1864
  *
1719
1865
  * @remarks
1720
1866
  * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
1721
1867
  * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
1722
- * IndexedDB backend swaps in WITHOUT touching a consumer — the same seam as
1868
+ * IndexedDB backend swaps in without touching a consumer — the same seam as
1723
1869
  * {@link import('./MemoryDefinitionStore.js').MemoryDefinitionStore}. The driver defaults to
1724
1870
  * memory ({@link import('../factories.js').createDatabaseDefinitionStore} passes
1725
- * `createMemoryDriver()`), so it ALSO works in memory out of the box; you opt into the durable
1871
+ * `createMemoryDriver()`), so it also works in memory out of the box; you opt into the durable
1726
1872
  * plumbing by passing a JSON / SQLite / IndexedDB driver.
1727
1873
  *
1728
- * The {@link DatabaseDefinition} is stored as ONE OPAQUE JSON COLUMN — the table is a row of
1729
- * `{ id; definition }` ({@link DatabaseDefinitionRow}). The definition is already a COMPLETE,
1730
- * self-contained, pure-JSON CONFIG payload (never a live handle), so storing it whole is lossless
1731
- * AND keeps the row type flat (`definition` reads back as `unknown`).
1874
+ * The {@link DatabaseDefinition} is stored as one opaque JSON column — the table is a row of
1875
+ * `{ id; definition }` ({@link DatabaseDefinitionRow}). The definition is already a complete,
1876
+ * self-contained, pure-JSON config payload (never a live handle), so storing it whole is lossless
1877
+ * and keeps the row type flat (`definition` reads back as `unknown`).
1732
1878
  *
1733
- * - **`set(definition)` upserts under the definition's OWN `id`** (no separate id param) — it
1879
+ * - **`set(definition)` upserts under the definition's own `id`** (no separate id param) — it
1734
1880
  * writes the row `{ id: definition.id, definition }`.
1735
1881
  * - **`get(id)` resolves the stored definition for an id**, narrowing the opaque JSON column back
1736
- * to a {@link DatabaseDefinition} ({@link import('../helpers.js').isDatabaseDefinition} — the
1737
- * AGENTS §14 boundary narrow for an untrusted storage read), or `undefined` if none is stored
1882
+ * to a {@link DatabaseDefinition} ({@link import('../validators.js').isDatabaseDefinition} — the
1883
+ * boundary narrow for an untrusted storage read), or `undefined` if none is stored
1738
1884
  * or the stored blob is malformed.
1739
1885
  * - **`delete(id)` drops a definition by id**; an absent id is a no-op (no throw).
1740
1886
  *
1741
- * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method
1887
+ * The public surface is exactly `get` / `set` / `delete` — no extra members (the method
1742
1888
  * bijection with {@link DefinitionStoreInterface}).
1743
1889
  *
1744
1890
  * @example
@@ -1754,7 +1900,7 @@ var MemoryDefinitionStore = class {
1754
1900
  var DatabaseDefinitionStore = class {
1755
1901
  #table;
1756
1902
  /**
1757
- * Wrap a table as a definition store.
1903
+ * Wraps a table as a definition store.
1758
1904
  *
1759
1905
  * @param table - The {@link TableInterface} holding the definitions — its row is the
1760
1906
  * {@link DatabaseDefinitionRow} `{ id; definition }` shape (the definition one opaque JSON column)
@@ -1762,20 +1908,37 @@ var DatabaseDefinitionStore = class {
1762
1908
  constructor(table) {
1763
1909
  this.#table = table;
1764
1910
  }
1765
- /** Resolve the persisted definition for `id`, narrowing the opaque JSON column back to a `DatabaseDefinition`. */
1911
+ /**
1912
+ * Resolves the persisted definition for `id`, narrowing the opaque JSON column back to a
1913
+ * {@link DatabaseDefinition}.
1914
+ *
1915
+ * @param id - The database id to read
1916
+ * @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored or the
1917
+ * stored blob is malformed
1918
+ */
1766
1919
  async get(id) {
1767
1920
  const row = await this.#table.get(id);
1768
1921
  if (row === void 0) return void 0;
1769
1922
  return isDatabaseDefinition(row.definition) ? row.definition : void 0;
1770
1923
  }
1771
- /** Insert or replace under the definition's OWN `id` (no separate id param) — the row is `{ id, definition }`. */
1924
+ /**
1925
+ * Inserts or replaces a definition under its own `id` — the written row is `{ id, definition }`.
1926
+ *
1927
+ * @param definition - The config to persist; its own `id` is the row key (no separate id param)
1928
+ * @returns A promise settling after the row is written
1929
+ */
1772
1930
  async set(definition) {
1773
1931
  await this.#table.set({
1774
1932
  id: definition.id,
1775
1933
  definition
1776
1934
  });
1777
1935
  }
1778
- /** Drop a definition by id; an absent id is a no-op (no throw). */
1936
+ /**
1937
+ * Drops the definition stored under `id`.
1938
+ *
1939
+ * @param id - The database id to drop; an absent id is a no-op, never a throw
1940
+ * @returns A promise settling after no row is stored under `id`
1941
+ */
1779
1942
  async delete(id) {
1780
1943
  await this.#table.remove(id);
1781
1944
  }
@@ -1783,7 +1946,8 @@ var DatabaseDefinitionStore = class {
1783
1946
  //#endregion
1784
1947
  //#region src/core/databases/DatabaseResolver.ts
1785
1948
  /**
1786
- * Resolve database definitions into cached live handles for database tools.
1949
+ * Resolves a database definition into a cached live handle for a database tool, over the tool's
1950
+ * live handles, its stored definitions, its driver registry, and an optional key generator.
1787
1951
  *
1788
1952
  * @example
1789
1953
  * ```ts
@@ -1799,7 +1963,7 @@ var DatabaseResolver = class {
1799
1963
  #generator;
1800
1964
  #store;
1801
1965
  /**
1802
- * Create a database resolver over the tool's live state and optional definition store.
1966
+ * Creates a database resolver over the tool's live state and optional definition store.
1803
1967
  *
1804
1968
  * @param handles - Initial live database handles cached by id
1805
1969
  * @param drivers - Driver factories keyed by definition driver name
@@ -1813,16 +1977,16 @@ var DatabaseResolver = class {
1813
1977
  this.#store = store;
1814
1978
  }
1815
1979
  /**
1816
- * Determine whether a live database is cached by id.
1980
+ * Determines whether a live database is cached by id.
1817
1981
  *
1818
1982
  * @param id - Database id
1819
- * @returns Whether a live handle is cached
1983
+ * @returns True if a live handle is cached; false otherwise
1820
1984
  */
1821
1985
  has(id) {
1822
1986
  return this.#handles.has(id);
1823
1987
  }
1824
1988
  /**
1825
- * Read a cached database without consulting the definition store.
1989
+ * Reads a cached database without consulting the definition store.
1826
1990
  *
1827
1991
  * @param id - Database id
1828
1992
  * @returns The cached live database, or `undefined`
@@ -1831,7 +1995,7 @@ var DatabaseResolver = class {
1831
1995
  return this.#handles.get(id);
1832
1996
  }
1833
1997
  /**
1834
- * Cache a live database by id.
1998
+ * Caches a live database by id.
1835
1999
  *
1836
2000
  * @param id - Database id
1837
2001
  * @param database - Live database handle
@@ -1841,7 +2005,7 @@ var DatabaseResolver = class {
1841
2005
  this.#handles.set(id, database);
1842
2006
  }
1843
2007
  /**
1844
- * Remove a cached live database by id.
2008
+ * Removes a cached live database by id.
1845
2009
  *
1846
2010
  * @param id - Database id
1847
2011
  * @returns Nothing
@@ -1850,7 +2014,8 @@ var DatabaseResolver = class {
1850
2014
  this.#handles.delete(id);
1851
2015
  }
1852
2016
  /**
1853
- * Resolve a cached or stored database by id.
2017
+ * Resolves a live database by id — the cached handle when one exists, otherwise a database
2018
+ * constructed from its stored definition.
1854
2019
  *
1855
2020
  * @param id - Database definition id
1856
2021
  * @returns The cached or newly constructed live database
@@ -1885,8 +2050,8 @@ var DatabaseResolver = class {
1885
2050
  //#endregion
1886
2051
  //#region src/core/factories.ts
1887
2052
  /**
1888
- * Wrap a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the OPT-IN
1889
- * adapter that lets a `function`-form task run a `@orkestrel/tool` tool BY NAME.
2053
+ * Wraps a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the opt-in
2054
+ * adapter that lets a `function`-form task run a `@orkestrel/tool` tool by name.
1890
2055
  *
1891
2056
  * @remarks
1892
2057
  * Composes into a caller's `WorkflowOptions.functions` registry like any other behavior
@@ -1924,7 +2089,7 @@ function createToolFunction(tools, name) {
1924
2089
  };
1925
2090
  }
1926
2091
  /**
1927
- * Wrap a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction}
2092
+ * Wraps a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction}
1928
2093
  * (`@orkestrel/workflow`) — the opt-in adapter that runs the agent to a settled result and carries
1929
2094
  * immutable lineage metadata for contextual Toolbox composition.
1930
2095
  *
@@ -1932,7 +2097,7 @@ function createToolFunction(tools, name) {
1932
2097
  * Composes into a caller's registry like any other behavior; the pure workflow runner has no
1933
2098
  * knowledge of agents itself. A root adapter derives its workflow lineage from the controller,
1934
2099
  * while a contextual adapter requires an exact match. Repeated agents, repeated workflows, and
1935
- * targets deeper than {@link import('./constants.js').MAX_WORKFLOW_DEPTH} are rejected before
2100
+ * targets deeper than {@link import('./constants.js').MAX_WORKFLOW_CHAIN} are rejected before
1936
2101
  * agent or runner activity. When {@link import('./types.js').AgentFunctionOptions.runner} is
1937
2102
  * supplied, the adapter binds a recursion-safe {@link createWorkflowTool} onto the agent's
1938
2103
  * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The
@@ -1960,12 +2125,12 @@ function createToolFunction(tools, name) {
1960
2125
  * ```
1961
2126
  */
1962
2127
  function createAgentFunction(agent, options) {
1963
- const lineage = lineageOf(options?.lineage);
2128
+ const lineage = normalizeLineage(options?.lineage);
1964
2129
  if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "agent function lineage must end with a workflow tag");
1965
2130
  const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
1966
2131
  const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
1967
2132
  return Object.freeze(Object.assign(async (controller) => {
1968
- const workflow = workflowTag(controller.task.phase.workflow.id);
2133
+ const workflow = tagWorkflow(controller.task.phase.workflow.id);
1969
2134
  const current = lineage.length === 0 ? extendLineage(lineage, workflow) : lineage;
1970
2135
  if (current.at(-1) !== workflow) throw new ToolboxError("DEPTH", "agent function workflow does not match its lineage", {
1971
2136
  workflow,
@@ -1977,7 +2142,7 @@ function createAgentFunction(agent, options) {
1977
2142
  depth,
1978
2143
  max: 8
1979
2144
  });
1980
- const tag = agentTag(agent.id);
2145
+ const tag = tagAgent(agent.id);
1981
2146
  if (current.includes(tag)) throw new ToolboxError("DEPTH", `agent '${agent.id}' is already an ancestor (cycle)`, {
1982
2147
  agent: agent.id,
1983
2148
  lineage: [...current]
@@ -2009,7 +2174,7 @@ function createAgentFunction(agent, options) {
2009
2174
  }));
2010
2175
  }
2011
2176
  /**
2012
- * Compose opaque host functions and raw agents into one immutable workflow registry.
2177
+ * Composes opaque host functions and raw agents into one immutable workflow registry.
2013
2178
  *
2014
2179
  * @remarks
2015
2180
  * This is the target-aware composition boundary used by direct runner consumers and
@@ -2036,7 +2201,7 @@ function createAgentFunction(agent, options) {
2036
2201
  * ```
2037
2202
  */
2038
2203
  function createWorkflowFunctions(runner, options) {
2039
- const lineage = lineageOf(options?.lineage);
2204
+ const lineage = normalizeLineage(options?.lineage);
2040
2205
  if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "workflow functions lineage must end with a workflow tag");
2041
2206
  const functions = options?.functions === void 0 ? Object.freeze({}) : Object.freeze({ ...options.functions });
2042
2207
  const agents = options?.agents === void 0 ? Object.freeze({}) : Object.freeze({ ...options.agents });
@@ -2060,7 +2225,9 @@ function createWorkflowFunctions(runner, options) {
2060
2225
  return Object.freeze(composed);
2061
2226
  }
2062
2227
  /**
2063
- * Compile the lenient workflow draft contract used by {@link createWorkflowTool}.
2228
+ * Compiles the lenient workflow draft contract {@link createWorkflowTool} parses an authored tree
2229
+ * with — a `ContractInterface` over a {@link WorkflowDraft}, whose `id` and `name` are optional at
2230
+ * the workflow, phase, and task levels.
2064
2231
  *
2065
2232
  * @returns The compiled {@link import('./types.js').WorkflowDraft} contract
2066
2233
  */
@@ -2068,34 +2235,33 @@ function createWorkflowDraftContract() {
2068
2235
  return createContract(workflowDraftShape);
2069
2236
  }
2070
2237
  /**
2071
- * Wrap a {@link WorkflowDefinition} as an LLM-callable tool — it ADVERTISES the SIMPLE flat
2072
- * authoring shape (`{ name?, steps: [{ name }] }`) as its `parameters` so even a small model can
2073
- * author a complete tree, and its handler EXPANDS / COMPLETES the authored blob, validates it
2074
- * against the STRICT contract, and runs it through `runner`, forwarding the caller's optional
2075
- * named functions and native checkpoint store.
2238
+ * Wraps a {@link WorkflowDefinition} as an LLM-callable tool — it advertises the flat authoring
2239
+ * shape (`{ name?, steps: [{ name }] }`) as its `parameters`, and its handler completes the
2240
+ * authored blob, validates it against the strict contract, and runs it through `runner`, forwarding
2241
+ * the caller's optional named functions and native checkpoint store.
2076
2242
  *
2077
2243
  * @remarks
2078
2244
  * A plain `ToolManagerInterface`-compatible tool (`@orkestrel/tool`), reproducing
2079
2245
  * Toolbox's flat / draft / full authoring contract, strict soundness gate, and lineage guard.
2080
- * It is ALSO the propagation carrier
2246
+ * It is also the propagation carrier
2081
2247
  * {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
2082
2248
  * handler receives only the model-supplied `args`, the immutable lineage is closed over at bind
2083
2249
  * time. The authored target is appended and its zero-based depth derived from workflow tags.
2084
2250
  *
2085
- * **Widened authoring surface (additive — the canonical contract + runner stay STRICT and
2086
- * unchanged).** A 2B model reliably CALLS the tool but cannot reliably emit the full four-level
2087
- * nested {@link WorkflowDefinition} (six required `id`/`name` strings, an all-or-nothing tree).
2088
- * So the tool ACCEPTS three authoring forms and converges them on the SAME strict
2251
+ * **Widened authoring surface (additive — the canonical contract and runner stay strict and
2252
+ * unchanged).** A 2B model reliably calls the tool but cannot reliably emit the full nested
2253
+ * {@link WorkflowDefinition}, whose every `id` and `name` is required across an all-or-nothing
2254
+ * tree. So the tool accepts the authoring forms that follow and converges them on the same strict
2089
2255
  * `createWorkflowContract` gate before running (soundness preserved):
2090
- * - the FLAT shape `{ name?, steps: [{ name }] }` — the ADVERTISED `parameters` (the simplest
2256
+ * - the flat shape `{ name?, steps: [{ name }] }` — the advertised `parameters` (the simplest
2091
2257
  * form, {@link import('./helpers.js').expandSteps}'d into one one-task phase per step);
2092
- * - a nested DRAFT with any `id`/`name` OMITTED — {@link createWorkflowDraftContract}-parsed then
2258
+ * - a nested draft with any `id`/`name` omitted — {@link createWorkflowDraftContract}-parsed then
2093
2259
  * {@link import('./helpers.js').completeDraft}'d (missing ids synthesized positionally);
2094
2260
  * - the full nested {@link WorkflowDefinition} — the advanced escape-hatch, accepted as the draft
2095
2261
  * super-set.
2096
2262
  *
2097
- * The universal tool-handler contract (AGENTS §14): returns the plain run summary
2098
- * (`{ status, count, durable?, fault? }`) on success, THROWS a typed `ToolboxError` on Toolbox
2263
+ * The universal tool-handler contract: returns the plain run summary
2264
+ * (`{ status, count, durable?, fault? }`) on success, throws a typed `ToolboxError` on Toolbox
2099
2265
  * boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
2100
2266
  * (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
2101
2267
  * top-level `error`, so nothing escapes the run. `options.lineage` is the propagation carrier
@@ -2109,6 +2275,34 @@ function createWorkflowDraftContract() {
2109
2275
  * @returns A `ToolInterface` (named {@link import('./constants.js').WORKFLOW_TOOL_NAME}) whose
2110
2276
  * `parameters` advertise the flat authoring schema
2111
2277
  *
2278
+ * @example Authoring and running a workflow through the tool with a real ToolManager
2279
+ * ```ts
2280
+ * import { createWorkflowTool } from '@orkestrel/toolbox'
2281
+ * import { createToolManager } from '@orkestrel/tool'
2282
+ * import { createWorkflowRunner } from '@orkestrel/workflow'
2283
+ * import type { WorkflowDefinition } from '@orkestrel/workflow'
2284
+ *
2285
+ * const definition: WorkflowDefinition = { id: 'release', name: 'Release', phases: [] }
2286
+ * const runner = createWorkflowRunner()
2287
+ * const functions = {
2288
+ * compile: () => 'compiled',
2289
+ * publish: () => 'published',
2290
+ * }
2291
+ * const tool = createWorkflowTool(definition, runner, { functions })
2292
+ *
2293
+ * const tools = createToolManager()
2294
+ * tools.add(tool)
2295
+ *
2296
+ * // A small model authors the simple flat shape — no ids/names required.
2297
+ * const result = await tools.execute({
2298
+ * id: 'call-1',
2299
+ * name: 'workflow',
2300
+ * arguments: { name: 'release', steps: [{ name: 'compile' }, { name: 'publish' }] },
2301
+ * })
2302
+ * if (!result.success) throw new Error(result.error)
2303
+ * result.value // { status: 'completed', count: 2 } — the single-level envelope; no nested { id, name, value }
2304
+ * ```
2305
+ *
2112
2306
  * @example
2113
2307
  * ```ts
2114
2308
  * import { createWorkflowTool } from '@src/core'
@@ -2126,7 +2320,7 @@ function createWorkflowTool(definition, runner, options) {
2126
2320
  const strict = createWorkflowContract();
2127
2321
  const draft = createWorkflowDraftContract();
2128
2322
  const steps = createContract(workflowStepsShape);
2129
- const lineage = lineageOf(options?.lineage);
2323
+ const lineage = normalizeLineage(options?.lineage);
2130
2324
  if (lineage.length > 0 && !lineage.at(-1)?.startsWith("agent:")) throw new ToolboxError("TOOL", "workflow tool lineage must end with an agent tag");
2131
2325
  const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
2132
2326
  const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
@@ -2151,7 +2345,7 @@ function createWorkflowTool(definition, runner, options) {
2151
2345
  target = parsed === void 0 ? void 0 : completeDraft(parsed);
2152
2346
  }
2153
2347
  if (target === void 0 || !strict.is(target)) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
2154
- const tag = workflowTag(target.id);
2348
+ const tag = tagWorkflow(target.id);
2155
2349
  if (lineage.includes(tag)) throw new ToolboxError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
2156
2350
  workflow: target.id,
2157
2351
  lineage: [...lineage]
@@ -2169,7 +2363,7 @@ function createWorkflowTool(definition, runner, options) {
2169
2363
  ...agents === void 0 ? {} : { agents },
2170
2364
  ...store === void 0 ? {} : { store }
2171
2365
  });
2172
- return workflowToolSummary(await runner.execute(target, {
2366
+ return summarizeWorkflow(await runner.execute(target, {
2173
2367
  ...registry === void 0 ? {} : { functions: registry },
2174
2368
  ...store === void 0 ? {} : { store }
2175
2369
  }));
@@ -2177,42 +2371,35 @@ function createWorkflowTool(definition, runner, options) {
2177
2371
  });
2178
2372
  }
2179
2373
  /**
2180
- * Build an LLM-callable workspace-editing tool — it ADVERTISES the `operation`-discriminated
2181
- * 13-op union ({@link import('./shapers.js').workspaceToolShape}) as its `parameters`, and its
2182
- * handler PARSES the model-supplied args against that contract and DISPATCHES the matched
2183
- * operation against the manager's ACTIVE workspace (the registry ops drive the manager itself),
2184
- * returning the plain result. A malformed operation throws this package's `ToolboxError`; a
2185
- * genuine workspace-domain failure propagates `@orkestrel/workspace`'s typed `WorkspaceError`.
2186
- * EITHER drives a caller-supplied {@link WorkspaceToolOptions.manager} directly, OR
2187
- * constructs a fresh `WorkspaceManagerInterface` (`@orkestrel/workspace`) over
2188
- * {@link import('./types.js').WorkspaceToolOptions.store} (via `@orkestrel/workspace`'s
2189
- * `createWorkspaceManager`); neither given constructs a manager backed by
2190
- * `@orkestrel/workspace`'s in-memory store default.
2374
+ * Builds an LLM-callable workspace-editing tool — it advertises the `operation`-discriminated union
2375
+ * ({@link import('./shapers.js').workspaceToolShape}) as its `parameters`, and its handler parses
2376
+ * the model-supplied args against that contract and dispatches the matched operation against the
2377
+ * manager's active workspace, returning the plain result.
2191
2378
  *
2192
2379
  * @remarks
2193
- * MANAGER-DRIVEN: every edit / read op (read / list / has / search / replace / write / splice /
2380
+ * Manager-driven: every edit / read op (read / list / has / search / replace / write / splice /
2194
2381
  * prepend / append / move / remove) targets `manager.active`, so the model edits whichever
2195
2382
  * workspace is active and a host can re-point it (`WorkspaceManagerInterface.switch`) between
2196
- * turns. Two REGISTRY ops make the model self-sufficient: `workspaces` LISTS the registered
2383
+ * turns. The registry ops make the model self-sufficient: `workspaces` lists the registered
2197
2384
  * workspaces (each `{ id, files, active }`) so it can discover an id, and `switch` re-points the
2198
2385
  * active workspace by id (lenient — an unknown id is a no-op reporting `switched: false`, never a
2199
2386
  * throw).
2200
2387
  *
2201
- * NO-ACTIVE RULE (the ergonomic seam): a WRITING op (write / splice / prepend / append / move /
2202
- * remove / replace) run when `manager.active` is `undefined` AUTO-CREATES + activates a default
2203
- * workspace (`manager.add()`) so the model can just start writing; a pure-READ op (read / list /
2204
- * has / search) against no active workspace returns the EMPTY result (`undefined` / `[]` /
2388
+ * No-active rule (the ergonomic seam): a writing op (write / splice / prepend / append / move /
2389
+ * remove / replace) run when `manager.active` is `undefined` auto-creates and activates a default
2390
+ * workspace (`manager.add()`) so the model can start writing; a pure-read op (read / list /
2391
+ * has / search) against no active workspace returns the empty result (`undefined` / `[]` /
2205
2392
  * `false`), never creating one and never throwing.
2206
2393
  *
2207
- * The handler conforms to the universal tool-handler contract (AGENTS §14): it `contract.parse`s
2208
- * the args, THROWS a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
2209
- * operation), else `switch`es on `op.operation` and RETURNS the plain result — letting a
2210
- * `WorkspaceError` raised by the live workspace (`MODALITY` / `PATTERN` / `RANGE`) PROPAGATE
2211
- * uncaught. The range edit is the FLAT `'splice'` op: its four flat caret integers are
2394
+ * The handler conforms to the universal tool-handler contract: it `contract.parse`s
2395
+ * the args, throws a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
2396
+ * operation), else `switch`es on `op.operation` and returns the plain result — letting a
2397
+ * `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` / `RANGE`)
2398
+ * propagate uncaught. The range edit is the flat `'splice'` op: its flat caret integers are
2212
2399
  * reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
2213
2400
  * ranged `write`.
2214
2401
  *
2215
- * @param options - `manager` (drive directly) OR `store` (build a manager over it); neither ⇒
2402
+ * @param options - `manager` (drive directly) or `store` (build a manager over it); neither ⇒
2216
2403
  * an in-memory-backed manager (see {@link import('./types.js').WorkspaceToolOptions})
2217
2404
  * @returns A `ToolInterface` (named {@link import('./constants.js').WORKSPACE_TOOL_NAME} by default)
2218
2405
  *
@@ -2330,19 +2517,19 @@ function createWorkspaceTool(options) {
2330
2517
  });
2331
2518
  }
2332
2519
  /**
2333
- * Build an LLM-callable sub-agent delegation tool — resolves a live, seeded `AgentInterface`
2334
- * from `registry` and runs it to completion for ONE delegated `task`.
2520
+ * Builds an LLM-callable sub-agent delegation tool — it resolves a live, seeded `AgentInterface`
2521
+ * from `registry` and runs it to completion for one delegated `task`.
2335
2522
  *
2336
2523
  * @remarks
2337
- * The universal tool-handler contract (AGENTS §14): validates the call args against
2524
+ * The universal tool-handler contract: validates the call args against
2338
2525
  * {@link import('./shapers.js').agentToolShape}, assembles an `AgentJobInput` (`task` seeds the
2339
2526
  * sub-agent's conversation as a single `user` message; `provider` / `tools` / `system` fall
2340
- * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent via
2527
+ * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent through
2341
2528
  * `registry.build`, runs it with `agent.generate()`, and returns the settled
2342
- * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, THROWS a typed `TOOL`
2529
+ * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, throws a typed `TOOL`
2343
2530
  * {@link import('./errors.js').ToolboxError}; a delegation that would exceed
2344
2531
  * {@link import('./constants.js').AGENT_TOOL_DEPTH}, or re-enter an already-delegated agent (a
2345
- * cycle), THROWS a typed `DEPTH` {@link import('./errors.js').ToolboxError} — both isolated
2532
+ * cycle), throws a typed `DEPTH` {@link import('./errors.js').ToolboxError}, each isolated
2346
2533
  * by the `ToolManagerInterface` into the canonical tool result's top-level `error`.
2347
2534
  *
2348
2535
  * `AgentInterface` (`@orkestrel/agent`) exposes no teardown method — a bound sub-agent's
@@ -2388,7 +2575,7 @@ function createAgentTool(registry, options) {
2388
2575
  depth,
2389
2576
  max: 8
2390
2577
  });
2391
- const tag = agentTag(provider);
2578
+ const tag = tagAgent(provider);
2392
2579
  if (ancestry.includes(tag)) throw new ToolboxError("DEPTH", `agent '${provider}' is already an ancestor (cycle)`, {
2393
2580
  provider,
2394
2581
  ancestry: [...ancestry]
@@ -2414,21 +2601,20 @@ function createAgentTool(registry, options) {
2414
2601
  });
2415
2602
  }
2416
2603
  /**
2417
- * Build an LLM-callable tool that returns the FULL `description` of another registered tool by
2418
- * name — the counterpart to the lean `summary` the other tools in this package advertise
2419
- * (`AGENT_TOOL_SUMMARY` / `WORKFLOW_TOOL_SUMMARY` / `WORKSPACE_TOOL_SUMMARY`).
2604
+ * Builds an LLM-callable tool that returns the full `description` of another registered tool by
2605
+ * name — the counterpart to the lean `summary` the other tools in this package advertise.
2420
2606
  *
2421
2607
  * @remarks
2422
2608
  * `ToolManagerInterface.definitions()` (`@orkestrel/tool`) advertises `tool.summary ??
2423
2609
  * tool.description` — a lean one-sentence summary stands in for a tool's full teaching
2424
2610
  * description when `summary` is set, keeping the advertised tool list compact for a small model.
2425
2611
  * This tool is the on-demand expansion seam: given a registered tool's `name`, it looks the tool
2426
- * up via `tools.tool(name)` and returns its full `description` (falling back to `summary` when a
2612
+ * up through `tools.tool(name)` and returns its full `description` (falling back to `summary` when a
2427
2613
  * tool has no `description` of its own, then a placeholder when it has neither).
2428
2614
  *
2429
- * The universal tool-handler contract (AGENTS §14): validates the call args against
2430
- * {@link import('./shapers.js').describeToolShape}, RETURNS the plain description string on
2431
- * success, THROWS a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
2615
+ * The universal tool-handler contract: validates the call args against
2616
+ * {@link import('./shapers.js').describeToolShape}, returns the plain description string on
2617
+ * success, throws a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
2432
2618
  * or an unknown tool name.
2433
2619
  *
2434
2620
  * @param tools - The `ToolManagerInterface` (`@orkestrel/tool`) whose registered tools this
@@ -2465,20 +2651,23 @@ function createDescribeTool(tools) {
2465
2651
  });
2466
2652
  }
2467
2653
  /**
2468
- * Build an LLM-callable form tool — the ASK side of the terminal seam. Asks
2469
- * a multi-field form and BLOCKS until it answers, returning the resolved values record.
2654
+ * Builds an LLM-callable form tool — the ask side of the terminal seam. It asks a multi-field form
2655
+ * and blocks until the addressed terminal answers, returning the resolved values record.
2470
2656
  *
2471
2657
  * @remarks
2472
- * The universal tool-handler contract (AGENTS §14): validates the call args against
2658
+ * The universal tool-handler contract: validates the call args against
2473
2659
  * {@link import('./shapers.js').promptToolShape}, parses the call's schema through
2474
2660
  * `@orkestrel/form`, constructs the live form, and passes it to `TerminalManagerInterface.ask`.
2475
- * `from` is FIXED at construction
2661
+ * `from` is fixed at construction
2476
2662
  * ({@link import('./types.js').PromptToolOptions.from}) — never read from the model-supplied
2477
- * args — so a model cannot spoof which terminal is asking. A form CYCLE rejects with
2663
+ * args — so a model cannot spoof which terminal is asking. A form cycle rejects with
2478
2664
  * `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
2479
2665
  * {@link import('./errors.js').ToolboxError}; an expired form re-surfaces as `EXPIRE`; an
2480
2666
  * unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
2481
- * plus the known ones (`manager.terminals()`).
2667
+ * and how many brokers the manager holds (`manager.count`). A schema `parseForm` refuses — an
2668
+ * unknown control, a duplicate field name, a `'select'` or `'checkbox'` field without usable
2669
+ * `choices` — throws a typed `TOOL` error up front, so a malformed schema never parks as a form
2670
+ * nobody can answer.
2482
2671
  *
2483
2672
  * @param options - The live manager, the fixed `from` identity, and advertised overrides (see
2484
2673
  * {@link import('./types.js').PromptToolOptions})
@@ -2519,7 +2708,7 @@ function createPromptTool(options) {
2519
2708
  return await options.manager.ask(options.from, call.to, createForm(schema));
2520
2709
  } catch (error) {
2521
2710
  if (isFormError(error) && error.code === "ABANDONED") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
2522
- const code = terminalToolCode(error);
2711
+ const code = inferTerminalCode(error);
2523
2712
  if (code === void 0) throw error;
2524
2713
  if (code === "DEADLOCK") throw new ToolboxError("DEADLOCK", `asking '${call.to}' would create a form cycle`, isTerminalError(error) ? error.context : {
2525
2714
  from: options.from,
@@ -2528,7 +2717,7 @@ function createPromptTool(options) {
2528
2717
  if (code === "EXPIRE") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
2529
2718
  if (isTerminalError(error) && error.code === "TARGET") throw new ToolboxError("TOOL", `unknown terminal '${call.to}'`, {
2530
2719
  to: call.to,
2531
- known: options.manager.terminals()
2720
+ count: options.manager.count
2532
2721
  });
2533
2722
  throw new ToolboxError("TOOL", `asking '${call.to}' failed`, { to: call.to });
2534
2723
  }
@@ -2536,22 +2725,21 @@ function createPromptTool(options) {
2536
2725
  });
2537
2726
  }
2538
2727
  /**
2539
- * Build an LLM-callable answer tool — the ANSWER side of the terminal seam. Lists the forms
2540
- * currently addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of
2541
- * them by id.
2728
+ * Builds an LLM-callable answer tool — the answer side of the terminal seam. It lists the forms
2729
+ * addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of them by id.
2542
2730
  *
2543
2731
  * @remarks
2544
- * The universal tool-handler contract (AGENTS §14): validates the call args against
2732
+ * The universal tool-handler contract: validates the call args against
2545
2733
  * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`). `'pending'`
2546
- * returns a compact list (`{ id, from, schema }`) of every form currently addressed to `to`.
2734
+ * returns a compact list (`{ id, from, schema }`) of every form addressed to `to`.
2547
2735
  * `'answer'` looks the form up by `id`, narrows the supplied `values` through `@orkestrel/form`,
2548
- * and applies it via
2736
+ * and applies it through
2549
2737
  * `TerminalManagerInterface.answer` — a rejected / unknown / unresolvable outcome
2550
2738
  * (`TerminalAnswerResult.error`) re-surfaces as a typed `ANSWER` `ToolboxError`; success returns
2551
- * `{ answered: id }`. `to` is FIXED at construction
2739
+ * `{ answered: id }`. `to` is fixed at construction
2552
2740
  * ({@link import('./types.js').AnswerToolOptions.to}) — never read from the model-supplied args —
2553
2741
  * so a model cannot spoof which terminal it is answering for. Concurrent answerers racing on one
2554
- * endpoint are FIRST-WRITE-WINS — a late answer to an already-settled form returns a typed
2742
+ * endpoint are first-write-wins — a late answer to an already-settled form returns a typed
2555
2743
  * `ANSWER` `ToolboxError` (surfaced as a 422 over HTTP).
2556
2744
  *
2557
2745
  * @param options - The live manager, the fixed `to` identity, and advertised overrides (see
@@ -2603,9 +2791,9 @@ function createAnswerTool(options) {
2603
2791
  });
2604
2792
  }
2605
2793
  /**
2606
- * Create the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
2607
- * definitions, the DEFAULT store the upcoming database / relation tools will persist their
2608
- * `DatabaseDefinition` configs through.
2794
+ * Creates the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
2795
+ * definitions, the default store the database and relation tools persist their `DatabaseDefinition`
2796
+ * configs through.
2609
2797
  *
2610
2798
  * @returns A {@link DefinitionStoreInterface}
2611
2799
  *
@@ -2620,7 +2808,7 @@ function createMemoryDefinitionStore() {
2620
2808
  return new MemoryDefinitionStore();
2621
2809
  }
2622
2810
  /**
2623
- * Create a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database`
2811
+ * Creates a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database`
2624
2812
  * layer — the driver-pluggable twin of {@link createMemoryDefinitionStore}, storing each
2625
2813
  * database's definition as one opaque JSON column.
2626
2814
  *
@@ -2645,15 +2833,15 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2645
2833
  }).table("definitions"));
2646
2834
  }
2647
2835
  /**
2648
- * Build an LLM-callable database tool — create, query, and mutate `@orkestrel/database`
2649
- * databases through one `operation`-discriminated call (AGENTS §14, matching
2836
+ * Builds an LLM-callable database tool — it creates, queries, and mutates `@orkestrel/database`
2837
+ * databases through one `operation`-discriminated call (matching
2650
2838
  * {@link createWorkspaceTool}'s single-tool-many-operations shape).
2651
2839
  *
2652
2840
  * @remarks
2653
- * The universal tool-handler contract (AGENTS §14): validates the call args against
2841
+ * The universal tool-handler contract: validates the call args against
2654
2842
  * {@link import('./shapers.js').databaseToolShape}, dispatches to the matching operation, and
2655
- * RETURNS a plain result on success. A database is resolved lazily and cached for the tool's
2656
- * lifetime — `'create'` mints one from `tables` ({@link import('./helpers.js').expandTables}) and
2843
+ * returns a plain result on success. A database is resolved lazily and cached for the tool's
2844
+ * lifetime — `'create'` mints one from `tables` ({@link import('./compilers.js').expandTables}) and
2657
2845
  * a registered `driver` key ({@link import('./types.js').DatabaseToolOptions.drivers}, default
2658
2846
  * `{ memory: () => createMemoryDriver() }`); any other operation addressing an uncached id falls
2659
2847
  * back to {@link import('./types.js').DatabaseToolOptions.store} (an unknown id throws a typed
@@ -2662,10 +2850,10 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2662
2850
  *
2663
2851
  * `'records'` clamps its `query` to
2664
2852
  * {@link import('./types.js').DatabaseToolOptions.limit} (default
2665
- * {@link import('./constants.js').DATABASE_TOOL_LIMIT}) via
2853
+ * {@link import('./constants.js').DATABASE_TOOL_LIMIT}) through
2666
2854
  * {@link import('./helpers.js').clampQuery}, reporting `truncated` when storage held more rows
2667
- * than the cap. Every operation's `query` is normalized via
2668
- * {@link import('./helpers.js').queryOf} (defaults an omitted condition `connector` to `'and'`).
2855
+ * than the cap. Every operation's `query` is normalized through
2856
+ * {@link import('./helpers.js').normalizeQuery} (defaults an omitted condition `connector` to `'and'`).
2669
2857
  * When {@link import('./types.js').DatabaseToolOptions.readonly} is `true`, every mutating
2670
2858
  * operation throws a typed `TOOL` `ToolboxError` before doing anything. A configured
2671
2859
  * {@link import('./types.js').DatabaseToolOptions.timeout} is passed as a fresh
@@ -2674,10 +2862,10 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2674
2862
  * not an outer deadline for resolution, construction, schema inspection, `get`, or `close`. A typed `@orkestrel/database`
2675
2863
  * failure (`DatabaseError`) re-surfaces as a typed `DATABASE` `ToolboxError` carrying the
2676
2864
  * original {@link import('@orkestrel/database').DatabaseErrorCode} in `context.code`
2677
- * ({@link import('./helpers.js').databaseToolCode}); an `ToolboxError` thrown by this tool's own
2865
+ * ({@link import('./helpers.js').inferDatabaseCode}); an `ToolboxError` thrown by this tool's own
2678
2866
  * guards passes through unwrapped.
2679
2867
  *
2680
- * A lazily re-minted database over the DEFAULT in-memory driver yields an EMPTY database — only
2868
+ * A lazily re-minted database over the default in-memory driver yields an empty database — only
2681
2869
  * the {@link import('./types.js').DatabaseDefinition} schema persists in `store`, never rows;
2682
2870
  * durable rows need a persistent driver factory registered in
2683
2871
  * {@link import('./types.js').DatabaseToolOptions.drivers}. `'destroy'` closes whatever handle is
@@ -2685,7 +2873,7 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2685
2873
  * {@link import('./types.js').DatabaseToolOptions.databases} handle — the embedder relinquishes
2686
2874
  * that handle's lifecycle to this tool for any id it wires in. This tool assumes the
2687
2875
  * single-writer, non-reentrant model `@orkestrel/database` itself assumes — concurrent calls
2688
- * against one id are NOT serialized by this tool. `'get'` is uncapped by
2876
+ * against one id are not serialized by this tool. `'get'` is uncapped by
2689
2877
  * {@link import('./types.js').DatabaseToolOptions.limit} (bounded only by the caller's `key` array
2690
2878
  * size), unlike `'records'` / `'find'` / `'links'`.
2691
2879
  *
@@ -2782,7 +2970,7 @@ function createDatabaseTool(options = {}) {
2782
2970
  }
2783
2971
  case "records": {
2784
2972
  const table = (await resolver.resolve(call.id)).table(call.table);
2785
- const { query: probe, limit } = clampQuery(queryOf(call.query), cap);
2973
+ const { query: probe, limit } = clampQuery(normalizeQuery(call.query), cap);
2786
2974
  const rows = await table.records(probe, read);
2787
2975
  const truncated = rows.length > limit;
2788
2976
  const sliced = rows.slice(0, limit);
@@ -2793,8 +2981,8 @@ function createDatabaseTool(options = {}) {
2793
2981
  limit
2794
2982
  };
2795
2983
  }
2796
- case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(queryOf(call.query), read) };
2797
- case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, queryOf(call.query), read) };
2984
+ case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(normalizeQuery(call.query), read) };
2985
+ case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, normalizeQuery(call.query), read) };
2798
2986
  case "add": {
2799
2987
  const table = (await resolver.resolve(call.id)).table(call.table);
2800
2988
  const many = Array.isArray(call.row);
@@ -2840,7 +3028,7 @@ function createDatabaseTool(options = {}) {
2840
3028
  }
2841
3029
  } catch (error) {
2842
3030
  if (isToolboxError(error)) throw error;
2843
- const code = databaseToolCode(error);
3031
+ const code = inferDatabaseCode(error);
2844
3032
  if (code === void 0) throw error;
2845
3033
  throw new ToolboxError("DATABASE", error instanceof Error ? error.message : String(error), {
2846
3034
  code,
@@ -2853,32 +3041,33 @@ function createDatabaseTool(options = {}) {
2853
3041
  });
2854
3042
  }
2855
3043
  /**
2856
- * Build an LLM-callable relation tool — traverse and edit `@orkestrel/relation` relationships
2857
- * through one `operation`-discriminated call (AGENTS §14, matching {@link createDatabaseTool}'s
3044
+ * Builds an LLM-callable relation tool — it traverses and edits `@orkestrel/relation` relationships
3045
+ * through one `operation`-discriminated call (matching {@link createDatabaseTool}'s
2858
3046
  * single-tool-many-operations shape).
2859
3047
  *
2860
3048
  * @remarks
2861
- * The universal tool-handler contract (AGENTS §14): validates the call args against
3049
+ * The universal tool-handler contract: validates the call args against
2862
3050
  * {@link import('./shapers.js').relationToolShape}, resolves the addressed
2863
3051
  * {@link import('@orkestrel/relation').RelationManagerInterface} — an explicit `manager` field
2864
- * must match a key of {@link import('./types.js').RelationToolOptions.managers}, an OMITTED one
2865
- * resolves to the SOLE registered manager, either miss throwing a typed `TOOL`
3052
+ * must match a key of {@link import('./types.js').RelationToolOptions.managers}, an omitted one
3053
+ * resolves to the sole registered manager, either miss throwing a typed `TOOL`
2866
3054
  * {@link import('./errors.js').ToolboxError}
2867
- * ({@link import('./helpers.js').relationManagerOf}) — then resolves `model` against it
2868
- * ({@link import('./helpers.js').relationModelOf}, same typed-`TOOL`-on-miss shape), and
2869
- * dispatches to the matched operation, RETURNING a plain result on success.
3055
+ * ({@link import('./helpers.js').resolveRelationManager}) — then resolves `model` against it
3056
+ * ({@link import('./helpers.js').resolveRelationModel}, same typed-`TOOL`-on-miss shape), and
3057
+ * dispatches to the matched operation, returning a plain result on success.
2870
3058
  *
2871
- * `'load'` / `'find'` expand the call's FLAT dot-path `include` list into a live
2872
- * `@orkestrel/relation` `Include` tree via {@link import('./helpers.js').expandInclude}, capped
3059
+ * `'load'` / `'find'` expand the call's flat dot-path `include` list into a live
3060
+ * `@orkestrel/relation` `Include` tree through {@link import('./helpers.js').expandInclude}, capped
2873
3061
  * at {@link import('./types.js').RelationToolOptions.depth} (default
2874
3062
  * {@link import('./constants.js').RELATION_TOOL_DEPTH}) — a path exceeding the cap, or carrying an
2875
3063
  * empty segment, throws a typed `TOOL` error. `'load'` dispatches on whether `key` is an array
2876
- * (positional many-key form, AGENTS §9.2) or a single key. `'find'` and `'links'` clamp their
3064
+ * (positional many-key form — an array key is read as many keys rather than one) or a single key.
3065
+ * `'find'` and `'links'` clamp their
2877
3066
  * result to {@link import('./types.js').RelationToolOptions.limit} (default
2878
- * {@link import('./constants.js').RELATION_TOOL_LIMIT}) — `'find'` probes one row past the
2879
- * effective limit (mirroring {@link import('./helpers.js').clampQuery}'s idiom) to report
2880
- * `truncated`; `'links'` (which has no upstream pagination) fetches the FULL linked-key list and
2881
- * slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
3067
+ * {@link import('./constants.js').RELATION_TOOL_LIMIT}), taken through
3068
+ * {@link import('./helpers.js').resolveLimit} — `'find'` probes one row past the effective limit to
3069
+ * report `truncated`; `'links'` (which has no upstream pagination) fetches the full linked-key
3070
+ * list and slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
2882
3071
  * row.
2883
3072
  *
2884
3073
  * A typed `@orkestrel/relation` failure (`RelationError`) re-surfaces as a typed `RELATION`
@@ -2913,7 +3102,7 @@ function createRelationTool(options) {
2913
3102
  const call = contract.parse(args);
2914
3103
  if (call === void 0) throw new ToolboxError("TOOL", "malformed relation call", { args });
2915
3104
  try {
2916
- const model = relationModelOf(relationManagerOf(options.managers, call.manager), call.model);
3105
+ const model = resolveRelationModel(resolveRelationManager(options.managers, call.manager), call.model);
2917
3106
  switch (call.operation) {
2918
3107
  case "load": {
2919
3108
  const include = expandInclude(call.include, depth);
@@ -2922,7 +3111,7 @@ function createRelationTool(options) {
2922
3111
  }
2923
3112
  case "find": {
2924
3113
  const include = expandInclude(call.include, depth);
2925
- const effective = Math.min(call.limit ?? cap, cap);
3114
+ const effective = resolveLimit(call.limit, cap);
2926
3115
  const rows = await model.find(include, {
2927
3116
  limit: effective + 1,
2928
3117
  ...call.offset === void 0 ? {} : { offset: call.offset },
@@ -2945,27 +3134,28 @@ function createRelationTool(options) {
2945
3134
  await model.unlink(call.key, call.relation, call.target);
2946
3135
  return { unlinked: true };
2947
3136
  case "links": {
3137
+ const effective = resolveLimit(void 0, cap);
2948
3138
  const keys = await model.links(call.key, call.relation);
2949
- const truncated = keys.length > cap;
2950
- const sliced = keys.slice(0, cap);
3139
+ const truncated = keys.length > effective;
3140
+ const sliced = keys.slice(0, effective);
2951
3141
  return {
2952
3142
  keys: sliced,
2953
3143
  count: sliced.length,
2954
3144
  truncated,
2955
- limit: cap
3145
+ limit: effective
2956
3146
  };
2957
3147
  }
2958
3148
  }
2959
3149
  } catch (error) {
2960
3150
  if (isToolboxError(error)) throw error;
2961
- const relation = relationToolCode(error);
3151
+ const relation = inferRelationCode(error);
2962
3152
  if (relation !== void 0) throw new ToolboxError("RELATION", error instanceof Error ? error.message : String(error), {
2963
3153
  code: relation,
2964
3154
  operation: call.operation,
2965
3155
  model: call.model,
2966
3156
  ..."relation" in call ? { relation: call.relation } : {}
2967
3157
  });
2968
- const database = databaseToolCode(error);
3158
+ const database = inferDatabaseCode(error);
2969
3159
  if (database === void 0) throw error;
2970
3160
  throw new ToolboxError("DATABASE", error instanceof Error ? error.message : String(error), {
2971
3161
  code: database,
@@ -2976,44 +3166,44 @@ function createRelationTool(options) {
2976
3166
  });
2977
3167
  }
2978
3168
  /**
2979
- * Build a standalone LLM-callable tool that infers a JSON Schema from example values — the
2980
- * utility half of the "existing API/DB → MCP tool" bridge (the other half,
2981
- * {@link createEndpointTool}, wraps one CONCRETE endpoint).
3169
+ * Builds a standalone LLM-callable tool that infers a JSON Schema from example values — the utility
3170
+ * half of the bridge from an existing API or database into an MCP tool (the other half,
3171
+ * {@link createEndpointTool}, wraps one concrete endpoint).
2982
3172
  *
2983
3173
  * @remarks
2984
- * The universal tool-handler contract (AGENTS §14): validates the call args against
3174
+ * The universal tool-handler contract: validates the call args against
2985
3175
  * {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
2986
- * booleans, `candidates` an optional array), infers a schema via `@orkestrel/contract`'s
2987
- * `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` via `schemaToObject` (mirrors
2988
- * the tool-parameters convention every other `create*Tool` factory advertises), and RETURNS the
3176
+ * booleans, `candidates` an optional array), infers a schema through `@orkestrel/contract`'s
3177
+ * `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` through `schemaToObject` (mirrors
3178
+ * the tool-parameters convention every other `create*Tool` factory advertises), and returns the
2989
3179
  * resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
2990
3180
  * `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
2991
3181
  * {@link import('./errors.js').ToolboxError}.
2992
3182
  *
2993
- * When `candidates` is ABSENT, the return is the bare parameters record — unchanged from before
2994
- * this array existed. When `candidates` is PRESENT (any array, including empty), the handler
2995
- * compiles a SEPARATE per-call contract from the RAW inferred schema (via `@orkestrel/contract`'s
2996
- * `schemaToShape`, NOT the `schemaToObject`-wrapped parameters — a bare-value sample checks a
3183
+ * When `candidates` is absent, the return is the bare parameters record — unchanged from before
3184
+ * this array existed. When `candidates` is present (any array, including empty), the handler
3185
+ * compiles a separate per-call contract from the raw inferred schema (through `@orkestrel/contract`'s
3186
+ * `schemaToShape`, not the `schemaToObject`-wrapped parameters — a bare-value sample checks a
2997
3187
  * bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
2998
- * index. Every entry has a UNIFORM shape — `{ index, valid, coercible }`, with `faults` added ONLY
2999
- * when `valid` is `false`: `valid` is the STRICT guard verdict (`checker.is(candidate)`), the
3000
- * OPPOSITE of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
3001
- * string slot) — here a conformance report answers "does this value conform AS-IS": `7` against a
3002
- * string slot is `valid: false`, full stop. `coercible` answers a SEPARATE question — "would the
3003
- * NORMALIZING parse accept this value", i.e. would {@link createEndpointTool}'s default enforcement
3188
+ * index. Every entry has a uniform shape — `{ index, valid, coercible }`, with `faults` added only
3189
+ * when `valid` is `false`: `valid` is the strict guard verdict (`checker.is(candidate)`), the
3190
+ * opposite of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
3191
+ * string slot) — here a conformance report answers "does this value conform as-is": `7` against a
3192
+ * string slot is `valid: false`, full stop. `coercible` answers a separate question — "would the
3193
+ * normalizing parse accept this value", that is would {@link createEndpointTool}'s default enforcement
3004
3194
  * admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
3005
- * `valid`; by the house parse/guard round-trip guarantee (AGENTS §14), a `valid: true` entry is
3006
- * ALWAYS also `coercible: true`. `@orkestrel/contract` 0.0.7's `explain` mirrors the normalizing
3195
+ * `valid`; by the house parse/guard round-trip, a `valid: true` entry is
3196
+ * always also `coercible: true`. `@orkestrel/contract`'s `explain` mirrors the normalizing
3007
3197
  * `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
3008
- * against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: EMPTY faults, since
3198
+ * against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: empty faults, because
3009
3199
  * the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
3010
- * therefore only ever populates for a NON-coercible mismatch — a wrong type the parse can't coerce
3200
+ * therefore only ever populates for a non-coercible mismatch — a wrong type the parse can't coerce
3011
3201
  * (a boolean in a string slot), a missing required key, or an out-of-enum value — where
3012
3202
  * `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
3013
- * JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches all three
3014
- * and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (e.g. a
3015
- * throwing-getter `Proxy`) never reaches the checker at all — it fails the OUTER `args` parse
3016
- * against {@link import('./shapers.js').inferToolShape} and rejects the WHOLE call with the same
3203
+ * JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
3204
+ * and yields a bounded, non-throwing per-candidate verdict; a non-JSON-safe candidate (for example a
3205
+ * throwing-getter `Proxy`) never reaches the checker at all — it fails the outer `args` parse
3206
+ * against {@link import('./shapers.js').inferToolShape} and rejects the whole call with the same
3017
3207
  * `TOOL` {@link import('./errors.js').ToolboxError} a malformed `samples`/`format`/`enum` throws,
3018
3208
  * with no per-candidate verdict produced.
3019
3209
  *
@@ -3092,36 +3282,36 @@ function createInferTool(options) {
3092
3282
  });
3093
3283
  }
3094
3284
  /**
3095
- * Wrap one CONCRETE endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
3096
- * `ToolInterface` — the endpoint half of the "existing API/DB → MCP tool" bridge (the other half,
3097
- * {@link createInferTool}, is a standalone inference utility).
3285
+ * Wraps one concrete endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
3286
+ * `ToolInterface` — the endpoint half of the bridge from an existing API or database into an MCP
3287
+ * tool (the other half, {@link createInferTool}, is a standalone inference utility).
3098
3288
  *
3099
3289
  * @remarks
3100
- * `parameters` is inferred ONCE at construction from `definition.samples` via
3290
+ * `parameters` is inferred once at construction from `definition.samples` through
3101
3291
  * `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
3102
- * `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` via `schemaToObject` —
3103
- * the SAME object-rooted schema is both the ADVERTISED `parameters` and, by default
3104
- * ({@link import('./types.js').EndpointToolOptions.validate} `true`), the ENFORCED contract:
3105
- * `@orkestrel/contract` 0.0.7's `schemaToShape` compiles it ONCE (via `createContract`) into a
3106
- * `ContractInterface` whose `.parse` runs on every call's `args` before `definition.invoke` — a
3107
- * NORMALIZING parse, not a strict type check: a scalar is COERCED to its inferred type where the
3292
+ * `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` through `schemaToObject` —
3293
+ * the same object-rooted schema is both the advertised `parameters` and, by default
3294
+ * ({@link import('./types.js').EndpointToolOptions.validate} `true`), the enforced contract:
3295
+ * `@orkestrel/contract`'s `schemaToShape` compiles it once (through `createContract`) into a
3296
+ * `ContractInterface` whose `.parse` runs on every call's `args` before `definition.execute` — a
3297
+ * normalizing parse, not a strict type check: a scalar is coerced to its inferred type where the
3108
3298
  * house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
3109
- * `'false'`/`1`/`0`), so `definition.invoke` receives the COERCED value (e.g. `7` sent for a
3299
+ * `'false'`/`1`/`0`), so `definition.execute` receives the coerced value (for example `7` sent for a
3110
3300
  * string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
3111
- * a record — a required key missing, or a value not coercible to its slot's type — THROWS a
3301
+ * a record — a required key missing, or a value not coercible to its slot's type — throws a
3112
3302
  * typed `TOOL` {@link import('./errors.js').ToolboxError} carrying the compiled contract's
3113
- * structured `explain` faults, and `definition.invoke` is never called. `format` annotations are
3114
- * NEVER asserted, and a key outside the closed inferred schema is SILENTLY DROPPED rather than
3303
+ * structured `explain` faults, and `definition.execute` is never called. `format` annotations are
3304
+ * never asserted, and a key outside the closed inferred schema is silently dropped rather than
3115
3305
  * rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
3116
- * `validate: false`, `execute` PASSES THROUGH the model-supplied `args` to `definition.invoke`
3117
- * WITHOUT re-validation — the pre-0.0.7 behavior, preserved as an explicit opt-out. Either way,
3118
- * `invoke`'s return flows back as the tool call's plain result; a throw PROPAGATES uncaught,
3306
+ * `validate: false`, the tool's `execute` passes through the model-supplied `args` to
3307
+ * `definition.execute` without re-validation — the raw-passthrough opt-out. Either way, the
3308
+ * definition's return flows back as the tool call's plain result; a throw propagates uncaught,
3119
3309
  * isolated by the `ToolManagerInterface` (`@orkestrel/tool`) into the canonical error envelope
3120
- * (AGENTS §14) — never caught or re-wrapped here.
3310
+ * — never caught or re-wrapped here.
3121
3311
  *
3122
3312
  * @param definition - The endpoint's identity, non-empty samples, and local handler (see
3123
3313
  * {@link import('./types.js').EndpointDefinition})
3124
- * @param options - Construction-time inference tuning + the validate opt-out (see
3314
+ * @param options - Construction-time inference tuning and the validate opt-out (see
3125
3315
  * {@link import('./types.js').EndpointToolOptions})
3126
3316
  * @returns A `ToolInterface` named `definition.name`
3127
3317
  *
@@ -3134,12 +3324,12 @@ function createInferTool(options) {
3134
3324
  * name: 'lookupUser',
3135
3325
  * description: 'Look up a user by id.',
3136
3326
  * samples: [{ id: '1', name: 'Ada' }, { id: '2', name: 'Bob' }],
3137
- * invoke: (args) => ({ id: args.id, name: 'Ada' }),
3327
+ * execute: (args) => ({ id: args.id, name: 'Ada' }),
3138
3328
  * })
3139
3329
  * const tools = createToolManager()
3140
3330
  * tools.add(tool)
3141
3331
  *
3142
- * // conforming args (all required keys present) parse and reach `invoke`
3332
+ * // conforming args (all required keys present) parse and reach the definition's `execute`
3143
3333
  * const result = await tools.execute({
3144
3334
  * id: 'call-1',
3145
3335
  * name: 'lookupUser',
@@ -3148,7 +3338,7 @@ function createInferTool(options) {
3148
3338
  * // result.value -> { id: '1', name: 'Ada' }
3149
3339
  *
3150
3340
  * // a nonconforming call (id is not coercible to the required string) is rejected before
3151
- * // `invoke` runs
3341
+ * // the definition's `execute` runs
3152
3342
  * const rejected = await tools.execute({
3153
3343
  * id: 'call-2',
3154
3344
  * name: 'lookupUser',
@@ -3169,7 +3359,7 @@ function createEndpointTool(definition, options) {
3169
3359
  description: definition.description,
3170
3360
  ...parameters === void 0 ? {} : { parameters },
3171
3361
  execute(args) {
3172
- return definition.invoke(args);
3362
+ return definition.execute(args);
3173
3363
  }
3174
3364
  });
3175
3365
  const contract = createContract(schemaToShape(objectSchema));
@@ -3183,11 +3373,11 @@ function createEndpointTool(definition, options) {
3183
3373
  name: definition.name,
3184
3374
  faults: contract.explain(args)
3185
3375
  });
3186
- return definition.invoke(parsed);
3376
+ return definition.execute(parsed);
3187
3377
  }
3188
3378
  });
3189
3379
  }
3190
3380
  //#endregion
3191
- export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_DEPTH, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentTag, agentToolShape, answerToolShape, clampQuery, columnKindShape, columnShape, columnSpecShape, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowFunctions, createWorkflowTool, createWorkspaceTool, databaseToolCode, databaseToolShape, deriveWorkflowDepth, describeToolShape, expandInclude, expandSteps, expandTables, extendLineage, includeShape, inferToolShape, isAgentFunction, isColumnKind, isColumnSpec, isDatabaseDefinition, isToolboxError, isWorkflowLineage, keyShape, kindShape, lineageOf, managerShape, orderShape, phaseDraftShape, promptToolShape, queryOf, queryShape, relationKeyShape, relationManagerOf, relationModelOf, relationToolCode, relationToolShape, rowShape, rowsShape, singleKeyShape, stepShape, tableSpecShape, taskDraftShape, terminalToolCode, workflowDraftShape, workflowStepsShape, workflowTag, workflowToolSummary, workspaceToolShape };
3381
+ export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_CHAIN, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentToolShape, answerToolShape, clampQuery, columnPrimitiveShape, columnSpecShape, compileColumn, compileColumnPrimitive, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowFunctions, createWorkflowTool, createWorkspaceTool, databaseToolShape, deriveWorkflowDepth, describeToolShape, expandInclude, expandSteps, expandTables, extendLineage, includeShape, inferDatabaseCode, inferRelationCode, inferTerminalCode, inferToolShape, isAgentFunction, isColumnPrimitive, isColumnSpec, isDatabaseDefinition, isToolboxError, isWorkflowLineage, keyShape, managerShape, normalizeLineage, normalizeQuery, orderShape, phaseDraftShape, promptToolShape, queryShape, relationToolShape, resolveLimit, resolveRelationManager, resolveRelationModel, rowShape, rowsShape, singleKeyShape, stepShape, summarizeWorkflow, tableSpecShape, tagAgent, tagWorkflow, taskDraftShape, workflowDraftShape, workflowStepsShape, workspaceToolShape };
3192
3382
 
3193
3383
  //# sourceMappingURL=index.js.map