@orkestrel/toolbox 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/src/core/index.cjs +497 -407
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +579 -474
- package/dist/src/core/index.d.ts +579 -474
- package/dist/src/core/index.js +482 -392
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +19 -24
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +11 -37
- package/dist/src/server/index.d.ts +11 -37
- package/dist/src/server/index.js +21 -25
- package/dist/src/server/index.js.map +1 -1
- package/package.json +19 -20
package/dist/src/core/index.cjs
CHANGED
|
@@ -10,17 +10,17 @@ let _orkestrel_tool = require("@orkestrel/tool");
|
|
|
10
10
|
let _orkestrel_form = require("@orkestrel/form");
|
|
11
11
|
//#region src/core/constants.ts
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Holds the name {@link import('./factories.js').createAgentTool} advertises by default — the key a
|
|
14
14
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
15
15
|
*/
|
|
16
16
|
var AGENT_TOOL_NAME = "agent";
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Holds the maximum nesting depth a delegation chain (agent tool → sub-agent → agent tool → …) may
|
|
19
19
|
* reach — the bound {@link import('./factories.js').createAgentTool}'s depth/cycle guard
|
|
20
20
|
* enforces.
|
|
21
21
|
*
|
|
22
22
|
* @remarks
|
|
23
|
-
* Deliberately a SEPARATE constant from {@link
|
|
23
|
+
* Deliberately a SEPARATE constant from {@link MAX_WORKFLOW_CHAIN} (rather than the two guards
|
|
24
24
|
* sharing one reference): the two guards bound DIFFERENT chains (workflow nesting vs. agent
|
|
25
25
|
* delegation) that happen to share a value today, and keeping this bound decoupled means a
|
|
26
26
|
* future change to one never silently shifts the other. Same numeric value by convention, not
|
|
@@ -28,22 +28,22 @@ var AGENT_TOOL_NAME = "agent";
|
|
|
28
28
|
*/
|
|
29
29
|
var AGENT_TOOL_DEPTH = 8;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createAgentTool}
|
|
32
32
|
* advertises in place of {@link AGENT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
33
33
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
34
|
-
* for the full teaching description; the full text stays retrievable
|
|
34
|
+
* for the full teaching description; the full text stays retrievable through
|
|
35
35
|
* {@link import('./factories.js').createDescribeTool}.
|
|
36
36
|
*/
|
|
37
37
|
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.";
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Holds the description {@link import('./factories.js').createAgentTool} advertises — a short guide
|
|
40
40
|
* covering the required task and optional provider, tools, and system overrides.
|
|
41
41
|
*/
|
|
42
42
|
var AGENT_TOOL_DESCRIPTION = [
|
|
43
43
|
"Delegate a task to a sub-agent and return its result. Every call runs ONE sub-agent turn to completion.",
|
|
44
44
|
"",
|
|
45
45
|
"Required:",
|
|
46
|
-
" task - the instructions the sub-agent
|
|
46
|
+
" task - the instructions the sub-agent carries out.",
|
|
47
47
|
"Optional overrides (default to the values this tool was configured with):",
|
|
48
48
|
" provider - the registry key of the model/provider the sub-agent runs against.",
|
|
49
49
|
" tools - registry keys of the tools loaded into the sub-agent (replaces the default list, not merged).",
|
|
@@ -52,7 +52,7 @@ var AGENT_TOOL_DESCRIPTION = [
|
|
|
52
52
|
JSON.stringify({ task: "Summarize the attached notes in three bullet points." })
|
|
53
53
|
].join("\n");
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* Holds the maximum nesting depth a workflow → agent → workflow chain may reach — the bound
|
|
56
56
|
* {@link import('./factories.js').createAgentFunction} and
|
|
57
57
|
* {@link import('./factories.js').createWorkflowTool}'s depth/cycle guards enforce.
|
|
58
58
|
*
|
|
@@ -61,9 +61,9 @@ var AGENT_TOOL_DESCRIPTION = [
|
|
|
61
61
|
* workflows is allowed, while a ninth nested workflow is rejected with this package's typed
|
|
62
62
|
* `DEPTH` `ToolboxError`.
|
|
63
63
|
*/
|
|
64
|
-
var
|
|
64
|
+
var MAX_WORKFLOW_CHAIN = 8;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Holds the name {@link import('./factories.js').createWorkflowTool} advertises by default — the key a
|
|
67
67
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under, and the name
|
|
68
68
|
* {@link import('./factories.js').createAgentFunction} binds the depth/cycle-aware workflow tool
|
|
69
69
|
* under onto a wrapped agent's `context.tools`.
|
|
@@ -72,17 +72,17 @@ var MAX_WORKFLOW_DEPTH = 8;
|
|
|
72
72
|
* The propagation seam's well-known key: when
|
|
73
73
|
* `createAgentFunction`'s `runner` option is supplied, it adds a `createWorkflowTool`-built tool
|
|
74
74
|
* under this name to the agent's `context.tools`, so it can author + run a NESTED workflow
|
|
75
|
-
* (bounded by {@link
|
|
75
|
+
* (bounded by {@link MAX_WORKFLOW_CHAIN}).
|
|
76
76
|
*/
|
|
77
77
|
var WORKFLOW_TOOL_NAME = "workflow";
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Holds a complete FLAT authoring example — the PRIMARY way a small model authors a workflow through
|
|
80
80
|
* {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
|
|
81
81
|
*
|
|
82
82
|
* @remarks
|
|
83
83
|
* Each step becomes a one-task phase, in
|
|
84
84
|
* order; a step's `name` is a REGISTERED behavior name (not a label) — the registry key its
|
|
85
|
-
* task's `
|
|
85
|
+
* task's `behavior` resolves against. The tool expands this
|
|
86
86
|
* ({@link import('./helpers.js').expandSteps}) into a valid `WorkflowDefinition`
|
|
87
87
|
* (`@orkestrel/workflow`). It is embedded VERBATIM in {@link WORKFLOW_TOOL_DESCRIPTION}.
|
|
88
88
|
*/
|
|
@@ -91,7 +91,7 @@ var WORKFLOW_TOOL_FLAT_EXAMPLE = Object.freeze({
|
|
|
91
91
|
steps: Object.freeze([Object.freeze({ name: "compile" }), Object.freeze({ name: "publish" })])
|
|
92
92
|
});
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* Holds a minimal NESTED authoring example — the ADVANCED escape-hatch form a model may use instead of
|
|
95
95
|
* the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
|
|
96
96
|
*
|
|
97
97
|
* @remarks
|
|
@@ -107,20 +107,20 @@ var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
|
|
|
107
107
|
tasks: Object.freeze([Object.freeze({
|
|
108
108
|
id: "compile",
|
|
109
109
|
name: "Compile",
|
|
110
|
-
|
|
110
|
+
behavior: "compile"
|
|
111
111
|
})])
|
|
112
112
|
})])
|
|
113
113
|
});
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkflowTool}
|
|
116
116
|
* advertises in place of {@link WORKFLOW_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
117
117
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
118
|
-
* for the full teaching description; the full text stays retrievable
|
|
118
|
+
* for the full teaching description; the full text stays retrievable through
|
|
119
119
|
* {@link import('./factories.js').createDescribeTool}.
|
|
120
120
|
*/
|
|
121
121
|
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.";
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
123
|
+
* Holds the description {@link import('./factories.js').createWorkflowTool} advertises — the flat
|
|
124
124
|
* authoring form, its worked example, and the advanced nested definition form.
|
|
125
125
|
*/
|
|
126
126
|
var WORKFLOW_TOOL_DESCRIPTION = [
|
|
@@ -134,17 +134,17 @@ var WORKFLOW_TOOL_DESCRIPTION = [
|
|
|
134
134
|
"Example:",
|
|
135
135
|
JSON.stringify(WORKFLOW_TOOL_FLAT_EXAMPLE),
|
|
136
136
|
"",
|
|
137
|
-
"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 \"
|
|
137
|
+
"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:",
|
|
138
138
|
JSON.stringify(WORKFLOW_TOOL_NESTED_EXAMPLE),
|
|
139
139
|
"In the nested form you may omit any \"id\"/\"name\" and they are filled in positionally; a provided one is kept."
|
|
140
140
|
].join("\n");
|
|
141
141
|
/**
|
|
142
|
-
*
|
|
142
|
+
* Holds the name {@link import('./factories.js').createWorkspaceTool} advertises by default — the key a
|
|
143
143
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
144
144
|
*/
|
|
145
145
|
var WORKSPACE_TOOL_NAME = "workspace";
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* Holds a valid {@link import('./types.js').WorkspaceOperation} object — the canonical example embedded
|
|
148
148
|
* VERBATIM in {@link WORKSPACE_TOOL_DESCRIPTION}.
|
|
149
149
|
*
|
|
150
150
|
* @remarks
|
|
@@ -158,15 +158,15 @@ var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
|
|
|
158
158
|
content: "hello"
|
|
159
159
|
});
|
|
160
160
|
/**
|
|
161
|
-
*
|
|
161
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createWorkspaceTool}
|
|
162
162
|
* advertises in place of {@link WORKSPACE_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
163
163
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
164
|
-
* for the full teaching description; the full text stays retrievable
|
|
164
|
+
* for the full teaching description; the full text stays retrievable through
|
|
165
165
|
* {@link import('./factories.js').createDescribeTool}.
|
|
166
166
|
*/
|
|
167
167
|
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.";
|
|
168
168
|
/**
|
|
169
|
-
*
|
|
169
|
+
* Holds the description {@link import('./factories.js').createWorkspaceTool} advertises — the
|
|
170
170
|
* operation-keyed workspace protocol, all supported operations, and worked examples.
|
|
171
171
|
*
|
|
172
172
|
* @remarks
|
|
@@ -198,24 +198,24 @@ var WORKSPACE_TOOL_DESCRIPTION = [
|
|
|
198
198
|
JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
|
|
199
199
|
].join("\n");
|
|
200
200
|
/**
|
|
201
|
-
*
|
|
201
|
+
* Holds the name {@link import('./factories.js').createDescribeTool} advertises by default — the key a
|
|
202
202
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
203
203
|
*
|
|
204
204
|
* @remarks
|
|
205
|
-
*
|
|
206
|
-
* {@link
|
|
205
|
+
* Pairs with the lean {@link AGENT_TOOL_SUMMARY} / {@link WORKFLOW_TOOL_SUMMARY} /
|
|
206
|
+
* {@link WORKSPACE_TOOL_SUMMARY} — a model that reads only the
|
|
207
207
|
* advertised summary can call `describe` with that tool's registered name to get its full
|
|
208
208
|
* teaching description back.
|
|
209
209
|
*/
|
|
210
210
|
var DESCRIBE_TOOL_NAME = "describe";
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
212
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createDescribeTool}
|
|
213
213
|
* advertises — this tool needs no teaching of its own, so its summary and description are both
|
|
214
214
|
* short.
|
|
215
215
|
*/
|
|
216
216
|
var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered tool.";
|
|
217
217
|
/**
|
|
218
|
-
*
|
|
218
|
+
* Holds the DESCRIPTION {@link import('./factories.js').createDescribeTool} advertises.
|
|
219
219
|
*
|
|
220
220
|
* @remarks
|
|
221
221
|
* Deliberately short — unlike the workflow / workspace / agent tools, this one has no authoring
|
|
@@ -223,19 +223,19 @@ var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered t
|
|
|
223
223
|
*/
|
|
224
224
|
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).";
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
226
|
+
* Holds the name {@link import('./factories.js').createPromptTool} advertises by default — the key a
|
|
227
227
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
228
228
|
*/
|
|
229
229
|
var PROMPT_TOOL_NAME = "ask";
|
|
230
230
|
/**
|
|
231
|
-
*
|
|
231
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createPromptTool}
|
|
232
232
|
* advertises in place of {@link PROMPT_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
233
233
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
234
|
-
* for the full teaching description; the full text stays retrievable
|
|
234
|
+
* for the full teaching description; the full text stays retrievable through
|
|
235
235
|
* {@link import('./factories.js').createDescribeTool}.
|
|
236
236
|
*/
|
|
237
237
|
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.";
|
|
238
|
-
/**
|
|
238
|
+
/** Holds the full form protocol {@link import('./factories.js').createPromptTool} advertises. */
|
|
239
239
|
var PROMPT_TOOL_DESCRIPTION = [
|
|
240
240
|
"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.",
|
|
241
241
|
"",
|
|
@@ -261,24 +261,24 @@ var PROMPT_TOOL_DESCRIPTION = [
|
|
|
261
261
|
})
|
|
262
262
|
].join("\n");
|
|
263
263
|
/**
|
|
264
|
-
*
|
|
264
|
+
* Holds the name {@link import('./factories.js').createAnswerTool} advertises by default — the key a
|
|
265
265
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
266
266
|
*/
|
|
267
267
|
var ANSWER_TOOL_NAME = "answer";
|
|
268
268
|
/**
|
|
269
|
-
*
|
|
269
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createAnswerTool}
|
|
270
270
|
* advertises in place of {@link ANSWER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
271
271
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
272
|
-
* for the full teaching description; the full text stays retrievable
|
|
272
|
+
* for the full teaching description; the full text stays retrievable through
|
|
273
273
|
* {@link import('./factories.js').createDescribeTool}.
|
|
274
274
|
*/
|
|
275
275
|
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.";
|
|
276
|
-
/**
|
|
276
|
+
/** Holds the pending/answer protocol {@link import('./factories.js').createAnswerTool} advertises. */
|
|
277
277
|
var ANSWER_TOOL_DESCRIPTION = [
|
|
278
|
-
"List the forms
|
|
278
|
+
"List the forms addressed to this terminal, or answer one of them by id. Every call is ONE operation, chosen by the \"operation\" field.",
|
|
279
279
|
"",
|
|
280
280
|
"Operations:",
|
|
281
|
-
"- pending { \"operation\": \"pending\" } — list every form
|
|
281
|
+
"- pending { \"operation\": \"pending\" } — list every form addressed to this terminal (id, from, schema).",
|
|
282
282
|
"- answer { \"operation\": \"answer\", \"id\": \"<form id>\", \"values\": { \"<field name>\": <field value> } } — answer every required field with the value its control accepts.",
|
|
283
283
|
"Example — list pending forms:",
|
|
284
284
|
JSON.stringify({ operation: "pending" }),
|
|
@@ -293,24 +293,20 @@ var ANSWER_TOOL_DESCRIPTION = [
|
|
|
293
293
|
})
|
|
294
294
|
].join("\n");
|
|
295
295
|
/**
|
|
296
|
-
*
|
|
297
|
-
* calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
298
|
-
*
|
|
299
|
-
* @remarks
|
|
300
|
-
* SRC-1 of a 3-unit spine: this unit lands the persistence + schema foundation
|
|
301
|
-
* ({@link import('./types.js').DatabaseDefinition}, {@link import('./types.js').DefinitionStoreInterface},
|
|
302
|
-
* {@link import('./helpers.js').expandTables}); `createDatabaseTool` itself is built in a later unit.
|
|
296
|
+
* Holds the name {@link import('./factories.js').createDatabaseTool} advertises by default — the key
|
|
297
|
+
* a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
303
298
|
*/
|
|
304
299
|
var DATABASE_TOOL_NAME = "database";
|
|
305
300
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
301
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
302
|
+
* {@link import('./factories.js').createDatabaseTool} advertises in place of
|
|
303
|
+
* {@link DATABASE_TOOL_DESCRIPTION}.
|
|
308
304
|
*/
|
|
309
305
|
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.";
|
|
310
306
|
/**
|
|
311
|
-
*
|
|
312
|
-
* small model the operation list, the SERIALIZED query form, and
|
|
313
|
-
* column DSL.
|
|
307
|
+
* Holds the description {@link import('./factories.js').createDatabaseTool} advertises — a
|
|
308
|
+
* multi-line guide that teaches a small model the operation list, the SERIALIZED query form, and
|
|
309
|
+
* the {@link import('./types.js').TableSpec} column DSL.
|
|
314
310
|
*
|
|
315
311
|
* @remarks
|
|
316
312
|
* The query form is deliberately SERIALIZED (never fluent) — every condition is a flat object
|
|
@@ -322,7 +318,7 @@ var DATABASE_TOOL_DESCRIPTION = [
|
|
|
322
318
|
"Create and query a database. Every call is ONE operation, chosen by the \"operation\" field.",
|
|
323
319
|
"",
|
|
324
320
|
"Operations (each takes the fields listed):",
|
|
325
|
-
"- create { \"operation\": \"create\", \"id\": \"<database id>\", \"tables\": { \"<table>\": { \"columns\": { \"<column>\": \"string\" | \"integer\" | \"number\" | \"boolean\" | { \"
|
|
321
|
+
"- 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.",
|
|
326
322
|
"- tables { \"operation\": \"tables\", \"id\": \"<database id>\" } — list a database's table names.",
|
|
327
323
|
"- get { \"operation\": \"get\", \"id\": \"<database id>\", \"table\": \"<table>\", \"key\": \"<row key>\" } — fetch one row by its primary key.",
|
|
328
324
|
"- records { \"operation\": \"records\", \"id\": \"<database id>\", \"table\": \"<table>\", \"query\"?: <Query> } — list rows matching a query.",
|
|
@@ -339,7 +335,7 @@ var DATABASE_TOOL_DESCRIPTION = [
|
|
|
339
335
|
" operators: equals, not, above, below, from, to, between, like, glob, starts, ends, any, none, absent, present.",
|
|
340
336
|
" \"connector\" joins this condition to the next (\"and\" | \"or\"); omit on the last condition.",
|
|
341
337
|
"",
|
|
342
|
-
"Column DSL (used by \"create\" \"tables\"): a column is either a bare
|
|
338
|
+
"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.",
|
|
343
339
|
"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.",
|
|
344
340
|
"Example — create a database:",
|
|
345
341
|
JSON.stringify({
|
|
@@ -349,7 +345,7 @@ var DATABASE_TOOL_DESCRIPTION = [
|
|
|
349
345
|
name: "string",
|
|
350
346
|
price: "number",
|
|
351
347
|
notes: {
|
|
352
|
-
|
|
348
|
+
primitive: "string",
|
|
353
349
|
optional: true
|
|
354
350
|
}
|
|
355
351
|
} } }
|
|
@@ -366,9 +362,9 @@ var DATABASE_TOOL_DESCRIPTION = [
|
|
|
366
362
|
}] }
|
|
367
363
|
})
|
|
368
364
|
].join("\n");
|
|
369
|
-
/**
|
|
365
|
+
/** Holds the default cap on rows a `records` call returns when the caller omits `query.limit` — the database tool's default row ceiling. */
|
|
370
366
|
var DATABASE_TOOL_LIMIT = 1e3;
|
|
371
|
-
/**
|
|
367
|
+
/** Lists the runtime-frozen database-tool mutation names disabled by `DatabaseToolOptions.readonly`. */
|
|
372
368
|
var DATABASE_TOOL_MUTATIONS = Object.freeze([
|
|
373
369
|
"create",
|
|
374
370
|
"add",
|
|
@@ -378,17 +374,17 @@ var DATABASE_TOOL_MUTATIONS = Object.freeze([
|
|
|
378
374
|
"destroy"
|
|
379
375
|
]);
|
|
380
376
|
/**
|
|
381
|
-
*
|
|
377
|
+
* Holds the name `createRelationTool` advertises by default — the key a model calls and the
|
|
382
378
|
* `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
383
379
|
*/
|
|
384
380
|
var RELATION_TOOL_NAME = "relation";
|
|
385
381
|
/**
|
|
386
|
-
*
|
|
382
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} the relation tool advertises
|
|
387
383
|
* in place of {@link RELATION_TOOL_DESCRIPTION}.
|
|
388
384
|
*/
|
|
389
385
|
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.";
|
|
390
386
|
/**
|
|
391
|
-
*
|
|
387
|
+
* Holds the DESCRIPTION the relation tool advertises — a multi-line guide that teaches a small model
|
|
392
388
|
* the operation list and the flat dot-path `include` syntax.
|
|
393
389
|
*
|
|
394
390
|
* @remarks
|
|
@@ -414,24 +410,24 @@ var RELATION_TOOL_DESCRIPTION = [
|
|
|
414
410
|
include: ["contacts.account"]
|
|
415
411
|
})
|
|
416
412
|
].join("\n");
|
|
417
|
-
/**
|
|
413
|
+
/** Holds the default cap on rows a `find` / `links` call returns when the caller omits `limit` — the relation tool's default row ceiling. */
|
|
418
414
|
var RELATION_TOOL_LIMIT = 1e3;
|
|
419
|
-
/**
|
|
415
|
+
/** Holds the default cap on how many `include` path segments deep a `load` / `find` call may traverse — the relation tool's default include-depth ceiling. */
|
|
420
416
|
var RELATION_TOOL_DEPTH = 3;
|
|
421
417
|
/**
|
|
422
|
-
*
|
|
418
|
+
* Holds the name {@link import('./factories.js').createInferTool} advertises by default — the key a
|
|
423
419
|
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
424
420
|
*/
|
|
425
421
|
var INFER_TOOL_NAME = "infer";
|
|
426
422
|
/**
|
|
427
|
-
*
|
|
423
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary} {@link import('./factories.js').createInferTool}
|
|
428
424
|
* advertises in place of {@link INFER_TOOL_DESCRIPTION} — a `ToolManagerInterface.definitions()`
|
|
429
425
|
* (`@orkestrel/tool`) advertises `summary ?? description`, so this one-sentence text stands in
|
|
430
|
-
* for the full teaching description; the full text stays retrievable
|
|
426
|
+
* for the full teaching description; the full text stays retrievable through
|
|
431
427
|
* {@link import('./factories.js').createDescribeTool}.
|
|
432
428
|
*/
|
|
433
429
|
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.";
|
|
434
|
-
/**
|
|
430
|
+
/** Holds the schema-inference protocol {@link import('./factories.js').createInferTool} advertises. */
|
|
435
431
|
var INFER_TOOL_DESCRIPTION = [
|
|
436
432
|
"Infer a JSON Schema from example values, returned in the same shape a tool advertises its parameters.",
|
|
437
433
|
"",
|
|
@@ -444,7 +440,7 @@ var INFER_TOOL_DESCRIPTION = [
|
|
|
444
440
|
" is wrapped as { parameters, checks } instead of the bare parameters record, one",
|
|
445
441
|
" check per candidate (same index). Every check has the uniform shape",
|
|
446
442
|
" { index, valid, coercible, faults? }. `valid` is a STRICT verdict (no coercion)",
|
|
447
|
-
" —
|
|
443
|
+
" — for example the number 7 is NOT valid against a string slot. `coercible` answers a",
|
|
448
444
|
" separate question: would the SAME value be accepted by an endpoint tool call,",
|
|
449
445
|
" whose enforcement NORMALIZES args (7 coerces to '7')? So 7 against a string slot",
|
|
450
446
|
" yields { valid: false, coercible: true, faults: [] } — a strict mismatch that",
|
|
@@ -524,7 +520,7 @@ var INFER_TOOL_DESCRIPTION = [
|
|
|
524
520
|
//#endregion
|
|
525
521
|
//#region src/core/errors.ts
|
|
526
522
|
/**
|
|
527
|
-
*
|
|
523
|
+
* Represents a package-owned tool-call failure: malformed input or unresolved configuration (`TOOL`),
|
|
528
524
|
* delegation depth/cycle rejection (`DEPTH`), prompt failure (`DEADLOCK` / `EXPIRE` / `ANSWER`),
|
|
529
525
|
* or a translated upstream database/relation failure (`DATABASE` / `RELATION`).
|
|
530
526
|
*
|
|
@@ -556,10 +552,10 @@ var ToolboxError = class extends Error {
|
|
|
556
552
|
}
|
|
557
553
|
};
|
|
558
554
|
/**
|
|
559
|
-
*
|
|
555
|
+
* Narrows an unknown caught value to an {@link ToolboxError}.
|
|
560
556
|
*
|
|
561
557
|
* @param value - The value to test (typically a `catch` binding)
|
|
562
|
-
* @returns
|
|
558
|
+
* @returns True if `value` is an {@link ToolboxError}; false otherwise
|
|
563
559
|
*
|
|
564
560
|
* @example
|
|
565
561
|
* ```ts
|
|
@@ -576,9 +572,102 @@ function isToolboxError(value) {
|
|
|
576
572
|
return value instanceof ToolboxError;
|
|
577
573
|
}
|
|
578
574
|
//#endregion
|
|
575
|
+
//#region src/core/validators.ts
|
|
576
|
+
/**
|
|
577
|
+
* Narrows an unknown value to a valid alternating workflow lineage.
|
|
578
|
+
*
|
|
579
|
+
* @param value - The value to inspect
|
|
580
|
+
* @returns True if `value` is a unique, nonempty-tagged workflow/agent chain; false otherwise
|
|
581
|
+
*/
|
|
582
|
+
function isWorkflowLineage(value) {
|
|
583
|
+
const inspected = (0, _orkestrel_contract.attempt)(() => {
|
|
584
|
+
if (!Array.isArray(value)) return false;
|
|
585
|
+
const seen = /* @__PURE__ */ new Set();
|
|
586
|
+
for (const [index, tag] of value.entries()) {
|
|
587
|
+
if (!(0, _orkestrel_contract.isString)(tag)) return false;
|
|
588
|
+
const prefix = index % 2 === 0 ? "workflow:" : "agent:";
|
|
589
|
+
if (!tag.startsWith(prefix) || tag.length === prefix.length || seen.has(tag)) return false;
|
|
590
|
+
seen.add(tag);
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
});
|
|
594
|
+
return inspected.success && inspected.value;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Narrows an unknown callable to Toolbox's frozen contextual agent adapter metadata.
|
|
598
|
+
*
|
|
599
|
+
* @param value - The value to inspect
|
|
600
|
+
* @returns True if it is a frozen {@link AgentFunction} with a frozen valid lineage; false otherwise
|
|
601
|
+
*/
|
|
602
|
+
function isAgentFunction(value) {
|
|
603
|
+
const inspected = (0, _orkestrel_contract.attempt)(() => {
|
|
604
|
+
if (typeof value !== "function" || !Object.isFrozen(value)) return false;
|
|
605
|
+
const category = Reflect.getOwnPropertyDescriptor(value, "category");
|
|
606
|
+
const lineage = Reflect.getOwnPropertyDescriptor(value, "lineage");
|
|
607
|
+
return category?.value === "agent" && lineage !== void 0 && "value" in lineage && Object.isFrozen(lineage.value) && isWorkflowLineage(lineage.value);
|
|
608
|
+
});
|
|
609
|
+
return inspected.success && inspected.value;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Narrows an unknown value to a {@link ColumnSpec}.
|
|
613
|
+
*
|
|
614
|
+
* @param value - The value to inspect
|
|
615
|
+
* @returns True if `value` is a valid {@link ColumnPrimitive} shorthand or a `{ primitive, optional }` record with a valid `primitive`; false otherwise
|
|
616
|
+
*/
|
|
617
|
+
function isColumnSpec(value) {
|
|
618
|
+
if (isColumnPrimitive(value)) return true;
|
|
619
|
+
if (!(0, _orkestrel_contract.isRecord)(value)) return false;
|
|
620
|
+
return isColumnPrimitive(value.primitive) && (value.optional === void 0 || typeof value.optional === "boolean");
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Narrows an unknown value to a {@link ColumnPrimitive}.
|
|
624
|
+
*
|
|
625
|
+
* @param value - The value to inspect
|
|
626
|
+
* @returns True if `value` is `'string'`, `'integer'`, `'number'`, or `'boolean'`; false otherwise
|
|
627
|
+
*/
|
|
628
|
+
function isColumnPrimitive(value) {
|
|
629
|
+
return value === "string" || value === "integer" || value === "number" || value === "boolean";
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id` + `driver`, a
|
|
633
|
+
* `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
|
|
634
|
+
* `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
|
|
635
|
+
* {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
|
|
636
|
+
* before trusting it as a definition (never an `as`).
|
|
637
|
+
*
|
|
638
|
+
* @param value - The value to inspect
|
|
639
|
+
* @returns True if `value` is a complete {@link DatabaseDefinition}; false otherwise
|
|
640
|
+
*/
|
|
641
|
+
function isDatabaseDefinition(value) {
|
|
642
|
+
if (!(0, _orkestrel_contract.isRecord)(value)) return false;
|
|
643
|
+
if ("keys" in value) return false;
|
|
644
|
+
if (!(0, _orkestrel_contract.isNonEmptyString)(value.id) || !(0, _orkestrel_contract.isNonEmptyString)(value.driver)) return false;
|
|
645
|
+
if (!(0, _orkestrel_contract.isRecord)(value.tables)) return false;
|
|
646
|
+
for (const table of Object.values(value.tables)) {
|
|
647
|
+
if (!(0, _orkestrel_contract.isRecord)(table) || !(0, _orkestrel_contract.isRecord)(table.columns)) return false;
|
|
648
|
+
for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
|
|
649
|
+
}
|
|
650
|
+
if (value.primary !== void 0) {
|
|
651
|
+
if (!(0, _orkestrel_contract.isRecord)(value.primary)) return false;
|
|
652
|
+
for (const key of Object.values(value.primary)) if (!(0, _orkestrel_contract.isNonEmptyString)(key)) return false;
|
|
653
|
+
}
|
|
654
|
+
if (value.indexes !== void 0) {
|
|
655
|
+
if (!(0, _orkestrel_contract.isRecord)(value.indexes)) return false;
|
|
656
|
+
for (const groups of Object.values(value.indexes)) {
|
|
657
|
+
if (!Array.isArray(groups)) return false;
|
|
658
|
+
for (const group of groups) {
|
|
659
|
+
if (!Array.isArray(group) || group.length === 0) return false;
|
|
660
|
+
for (const column of group) if (!(0, _orkestrel_contract.isNonEmptyString)(column)) return false;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (value.version !== void 0 && !(0, _orkestrel_contract.isFiniteNumber)(value.version)) return false;
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
//#endregion
|
|
579
668
|
//#region src/core/shapers.ts
|
|
580
669
|
/**
|
|
581
|
-
*
|
|
670
|
+
* Describes the shape of {@link import('./factories.js').createPromptTool}'s call arguments — `to` names the
|
|
582
671
|
* terminal identity and `schema` carries the complete multi-field form document.
|
|
583
672
|
*
|
|
584
673
|
* @remarks
|
|
@@ -593,7 +682,7 @@ var promptToolShape = (0, _orkestrel_contract.objectShape)({
|
|
|
593
682
|
schema: (0, _orkestrel_contract.jsonShape)({ description: "The @orkestrel/form schema to ask, with form metadata and one or more typed fields." })
|
|
594
683
|
});
|
|
595
684
|
/**
|
|
596
|
-
*
|
|
685
|
+
* Describes the shape of {@link import('./factories.js').createAnswerTool}'s call arguments — discriminated
|
|
597
686
|
* by `operation`: `'pending'` lists the forms addressed to this tool's terminal, while `'answer'`
|
|
598
687
|
* resolves one by `id` with a complete `values` record.
|
|
599
688
|
*
|
|
@@ -601,7 +690,7 @@ var promptToolShape = (0, _orkestrel_contract.objectShape)({
|
|
|
601
690
|
* `values` is first bounded to exact JSON. The handler then narrows it with the originating Form
|
|
602
691
|
* package's `isFormValues` guard before the terminal manager applies it to the parked live form.
|
|
603
692
|
*/
|
|
604
|
-
var answerToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.objectShape)({ operation: (0, _orkestrel_contract.literalShape)(["pending"], { description: "List the forms
|
|
693
|
+
var answerToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.objectShape)({ operation: (0, _orkestrel_contract.literalShape)(["pending"], { description: "List the forms addressed to this terminal." }) }), (0, _orkestrel_contract.objectShape)({
|
|
605
694
|
operation: (0, _orkestrel_contract.literalShape)(["answer"], { description: "Answer one pending form by id." }),
|
|
606
695
|
id: (0, _orkestrel_contract.stringShape)({
|
|
607
696
|
min: 1,
|
|
@@ -610,7 +699,7 @@ var answerToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contrac
|
|
|
610
699
|
values: (0, _orkestrel_contract.jsonShape)({ description: "The form answers keyed by field name." })
|
|
611
700
|
}));
|
|
612
701
|
/**
|
|
613
|
-
*
|
|
702
|
+
* Describes the shape of {@link import('./types.js').AgentToolArguments} —
|
|
614
703
|
* {@link import('./factories.js').createAgentTool}'s advertised `parameters`.
|
|
615
704
|
*
|
|
616
705
|
* @remarks
|
|
@@ -620,7 +709,7 @@ var answerToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contrac
|
|
|
620
709
|
var agentToolShape = (0, _orkestrel_contract.objectShape)({
|
|
621
710
|
task: (0, _orkestrel_contract.stringShape)({
|
|
622
711
|
min: 1,
|
|
623
|
-
description: "The instructions the sub-agent
|
|
712
|
+
description: "The instructions the sub-agent carries out."
|
|
624
713
|
}),
|
|
625
714
|
provider: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
|
|
626
715
|
min: 1,
|
|
@@ -630,7 +719,7 @@ var agentToolShape = (0, _orkestrel_contract.objectShape)({
|
|
|
630
719
|
system: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({ description: "A system prompt seeding the sub-agent's context (overrides the default)." }))
|
|
631
720
|
});
|
|
632
721
|
/**
|
|
633
|
-
*
|
|
722
|
+
* Describes the shape of {@link import('./types.js').DescribeToolArguments} —
|
|
634
723
|
* {@link import('./factories.js').createDescribeTool}'s advertised `parameters`.
|
|
635
724
|
*
|
|
636
725
|
* @remarks
|
|
@@ -641,7 +730,7 @@ var describeToolShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkest
|
|
|
641
730
|
description: "The registered name of the tool whose full description to return."
|
|
642
731
|
}) });
|
|
643
732
|
/**
|
|
644
|
-
*
|
|
733
|
+
* Describes the shape of a {@link import('./types.js').TaskDraft} — identical to a strict task shape
|
|
645
734
|
* EXCEPT `id` and `name` are OPTIONAL.
|
|
646
735
|
*/
|
|
647
736
|
var taskDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
@@ -654,7 +743,7 @@ var taskDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
654
743
|
description: "Task name; defaults to the id when omitted."
|
|
655
744
|
})),
|
|
656
745
|
description: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({ description: "Optional task description." })),
|
|
657
|
-
|
|
746
|
+
behavior: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
|
|
658
747
|
min: 1,
|
|
659
748
|
description: "The registered behavior name to invoke (a registry key, not a label); omitted completes with JSON null."
|
|
660
749
|
})),
|
|
@@ -669,7 +758,7 @@ var taskDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
669
758
|
}))
|
|
670
759
|
});
|
|
671
760
|
/**
|
|
672
|
-
*
|
|
761
|
+
* Describes the shape of a PHASE in a draft workflow — identical to a strict phase shape EXCEPT `id` and
|
|
673
762
|
* `name` are OPTIONAL, and its tasks are {@link taskDraftShape}s.
|
|
674
763
|
*/
|
|
675
764
|
var phaseDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
@@ -690,13 +779,13 @@ var phaseDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
690
779
|
bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Per-phase failure-policy override; omitted inherits the workflow bail." }))
|
|
691
780
|
});
|
|
692
781
|
/**
|
|
693
|
-
*
|
|
782
|
+
* Describes the shape of a DRAFT workflow — identical to a strict workflow shape EXCEPT `id` and `name`
|
|
694
783
|
* are OPTIONAL at all three levels (workflow / phase / task), so a small model can omit the six
|
|
695
784
|
* identity strings and let the tool synthesize them positionally.
|
|
696
785
|
*
|
|
697
786
|
* @remarks
|
|
698
787
|
* The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract} compiles.
|
|
699
|
-
* `
|
|
788
|
+
* `behavior` stays optional like the strict form; omission is the deliberate JSON `null` no-op. A
|
|
700
789
|
* provided `id` / `name` still has `minLength: 1` (so
|
|
701
790
|
* an explicitly-empty `id: ''` is REJECTED, not auto-filled). After
|
|
702
791
|
* {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
|
|
@@ -717,18 +806,18 @@ var workflowDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
717
806
|
bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Failure policy: false (default) continues gracefully, true halts on the first failure." }))
|
|
718
807
|
});
|
|
719
808
|
/**
|
|
720
|
-
*
|
|
809
|
+
* Describes the shape of ONE flat step — `{ name }` — the building block of {@link workflowStepsShape}.
|
|
721
810
|
*
|
|
722
811
|
* @remarks
|
|
723
|
-
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `
|
|
812
|
+
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `behavior`). The tool
|
|
724
813
|
* expands each step into a one-task phase, in order ({@link import('./helpers.js').expandSteps}).
|
|
725
814
|
*/
|
|
726
815
|
var stepShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkestrel_contract.stringShape)({
|
|
727
816
|
min: 1,
|
|
728
|
-
description: "The registered behavior name this step runs (becomes the task
|
|
817
|
+
description: "The registered behavior name this step runs (becomes the task behavior)."
|
|
729
818
|
}) });
|
|
730
819
|
/**
|
|
731
|
-
*
|
|
820
|
+
* Describes the FLAT authoring shape {@link import('./factories.js').createWorkflowTool} advertises as its
|
|
732
821
|
* `parameters` — the simplest surface a small model can fill: `{ name?, steps: [{ name }] }`.
|
|
733
822
|
*
|
|
734
823
|
* @remarks
|
|
@@ -748,10 +837,10 @@ var workflowStepsShape = (0, _orkestrel_contract.objectShape)({
|
|
|
748
837
|
steps: (0, _orkestrel_contract.arrayShape)(stepShape, { description: "The ordered steps to run, one after another (each becomes a one-task phase)." })
|
|
749
838
|
});
|
|
750
839
|
/**
|
|
751
|
-
*
|
|
752
|
-
* over the
|
|
753
|
-
* literal (never a bare `kind
|
|
754
|
-
* discriminant then its FLAT fields, every field
|
|
840
|
+
* Describes the shape of a {@link import('./types.js').WorkspaceOperation} — a descriptive tagged union
|
|
841
|
+
* over the workspace edit, read, and navigation operations, discriminated by the `operation`
|
|
842
|
+
* literal (never a bare `kind`). Each variant leads with its `operation`
|
|
843
|
+
* discriminant then its FLAT fields, every field through `stringShape` / `optionalShape` /
|
|
755
844
|
* `integerShape({ min: 1 })` / `booleanShape`, each carrying a strong field-level `description`.
|
|
756
845
|
*
|
|
757
846
|
* @remarks
|
|
@@ -831,27 +920,27 @@ var workspaceToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_cont
|
|
|
831
920
|
operation: (0, _orkestrel_contract.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." }),
|
|
832
921
|
id: (0, _orkestrel_contract.stringShape)({ description: "The id of the workspace to make active (from the \"workspaces\" listing)." })
|
|
833
922
|
}));
|
|
834
|
-
/**
|
|
835
|
-
var
|
|
923
|
+
/** Describes a {@link import('./types.js').ColumnPrimitive} literal — the leaf {@link columnSpecShape} wraps. */
|
|
924
|
+
var columnPrimitiveShape = (0, _orkestrel_contract.literalShape)([
|
|
836
925
|
"string",
|
|
837
926
|
"integer",
|
|
838
927
|
"number",
|
|
839
928
|
"boolean"
|
|
840
|
-
], { description: "A column
|
|
841
|
-
/**
|
|
842
|
-
var columnSpecShape = (0, _orkestrel_contract.unionShape)(
|
|
843
|
-
|
|
929
|
+
], { description: "A column primitive: \"string\" | \"integer\" | \"number\" | \"boolean\"." });
|
|
930
|
+
/** Describes a {@link import('./types.js').ColumnSpec} — a bare {@link columnPrimitiveShape}, or `{ primitive, optional }`. */
|
|
931
|
+
var columnSpecShape = (0, _orkestrel_contract.unionShape)(columnPrimitiveShape, (0, _orkestrel_contract.objectShape)({
|
|
932
|
+
primitive: columnPrimitiveShape,
|
|
844
933
|
optional: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.booleanShape)({ description: "Whether the column may be absent from a row." }))
|
|
845
934
|
}));
|
|
846
|
-
/**
|
|
847
|
-
var tableSpecShape = (0, _orkestrel_contract.recordShape)((0, _orkestrel_contract.objectShape)({ columns: (0, _orkestrel_contract.recordShape)(columnSpecShape, { description: "Column name to its
|
|
848
|
-
/**
|
|
935
|
+
/** Describes a {@link import('./types.js').TableSpec} — table name to `{ columns }`, each column a {@link columnSpecShape}. */
|
|
936
|
+
var tableSpecShape = (0, _orkestrel_contract.recordShape)((0, _orkestrel_contract.objectShape)({ columns: (0, _orkestrel_contract.recordShape)(columnSpecShape, { description: "Column name to its primitive or its { primitive, optional } spec." }) }), { description: "Table name to its column layout." });
|
|
937
|
+
/** Describes one key value for the database tool and the relation tool — a string or number; the array form (multiple keys, positional) resolves FIRST, so an array argument is read as many keys rather than one. */
|
|
849
938
|
var keyShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.stringShape)(), (0, _orkestrel_contract.numberShape)()), { description: "Multiple row keys, positional — a miss at an index is undefined there." }), (0, _orkestrel_contract.stringShape)({ description: "One row key." }), (0, _orkestrel_contract.numberShape)({ description: "One row key." }));
|
|
850
|
-
/**
|
|
939
|
+
/** Describes a loose row — a flat object of column name to JSON value; the array form (multiple rows) resolves FIRST, so an array argument is read as many rows rather than one. */
|
|
851
940
|
var rowShape = (0, _orkestrel_contract.recordShape)((0, _orkestrel_contract.jsonShape)(), { description: "A row as a flat object of column name to value." });
|
|
852
|
-
/**
|
|
941
|
+
/** Describes one or many loose rows — the array form resolves FIRST, so an array argument is read as many rows rather than one. */
|
|
853
942
|
var rowsShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.arrayShape)(rowShape, { description: "Multiple rows." }), rowShape);
|
|
854
|
-
/**
|
|
943
|
+
/** Describes one SERIALIZED WHERE condition — `values` is ALWAYS an array, even for a single-value operator. */
|
|
855
944
|
var conditionShape = (0, _orkestrel_contract.objectShape)({
|
|
856
945
|
column: (0, _orkestrel_contract.stringShape)({ description: "The column this condition applies to." }),
|
|
857
946
|
operator: (0, _orkestrel_contract.literalShape)([
|
|
@@ -874,12 +963,12 @@ var conditionShape = (0, _orkestrel_contract.objectShape)({
|
|
|
874
963
|
values: (0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.jsonShape)(), { description: "The operand values the operator needs (always an array, even for one value)." }),
|
|
875
964
|
connector: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)(["and", "or"], { description: "Joins this condition to the next; omit on the last condition." }))
|
|
876
965
|
});
|
|
877
|
-
/**
|
|
966
|
+
/** Describes one sort term. */
|
|
878
967
|
var orderShape = (0, _orkestrel_contract.objectShape)({
|
|
879
968
|
column: (0, _orkestrel_contract.stringShape)({ description: "The column to sort by." }),
|
|
880
969
|
direction: (0, _orkestrel_contract.literalShape)(["ascending", "descending"], { description: "The sort direction." })
|
|
881
970
|
});
|
|
882
|
-
/**
|
|
971
|
+
/** Describes the SERIALIZED query form — conditions, order, and pagination. */
|
|
883
972
|
var queryShape = (0, _orkestrel_contract.objectShape)({
|
|
884
973
|
conditions: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(conditionShape, { description: "The WHERE conditions, folded left to right." })),
|
|
885
974
|
order: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(orderShape, { description: "The sort terms, applied in order." })),
|
|
@@ -893,15 +982,15 @@ var queryShape = (0, _orkestrel_contract.objectShape)({
|
|
|
893
982
|
}))
|
|
894
983
|
});
|
|
895
984
|
/**
|
|
896
|
-
*
|
|
985
|
+
* Describes the shape of {@link import('./factories.js').createDatabaseTool}'s call arguments —
|
|
897
986
|
* discriminated by `operation` into the 11 database operations (`'create'` / `'tables'` /
|
|
898
987
|
* `'get'` / `'records'` / `'count'` / `'aggregate'` / `'add'` / `'set'` / `'update'` /
|
|
899
988
|
* `'remove'` / `'destroy'`).
|
|
900
989
|
*
|
|
901
990
|
* @remarks
|
|
902
991
|
* Every arm carries `id` (the database id). `'create'` carries `tables` (the
|
|
903
|
-
* {@link import('./types.js').TableSpec} column DSL, compiled
|
|
904
|
-
* {@link import('./
|
|
992
|
+
* {@link import('./types.js').TableSpec} column DSL, compiled through
|
|
993
|
+
* {@link import('./compilers.js').expandTables}); `'get'` / `'update'` / `'remove'` carry `key`
|
|
905
994
|
* (one key or an array of keys, positional); `'add'` / `'set'` carry `row` (one row or an array of
|
|
906
995
|
* rows); `'update'` also carries `changes` (a loose partial row); `'records'` / `'count'` /
|
|
907
996
|
* `'aggregate'` carry an optional `query` (the SERIALIZED form — `values` is ALWAYS an array,
|
|
@@ -1037,19 +1126,17 @@ var databaseToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contr
|
|
|
1037
1126
|
description: "The database id."
|
|
1038
1127
|
})
|
|
1039
1128
|
}));
|
|
1040
|
-
/**
|
|
1041
|
-
var relationKeyShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.stringShape)(), (0, _orkestrel_contract.numberShape)()), { description: "Multiple row keys, positional — a miss at an index is undefined there." }), (0, _orkestrel_contract.stringShape)({ description: "One row key." }), (0, _orkestrel_contract.numberShape)({ description: "One row key." }));
|
|
1042
|
-
/** A single row key (not an array) — used by `'link'` / `'unlink'` / `'links'`, which address exactly one owning row. */
|
|
1129
|
+
/** Describes a single row key (not an array) — used by `'link'` / `'unlink'` / `'links'`, which address exactly one owning row. */
|
|
1043
1130
|
var singleKeyShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.stringShape)({ description: "The owning row key." }), (0, _orkestrel_contract.numberShape)({ description: "The owning row key." }));
|
|
1044
|
-
/**
|
|
1045
|
-
var includeShape = (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)({ description: "A dot-separated chain of relation names,
|
|
1046
|
-
/**
|
|
1131
|
+
/** Describes the flat dot-path relation include list, expanded through {@link import('./helpers.js').expandInclude}. */
|
|
1132
|
+
var includeShape = (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.stringShape)({ description: "A dot-separated chain of relation names, for example \"contacts.account\"." }), { description: "Which relations to attach, as flat dot-paths." }));
|
|
1133
|
+
/** Describes which registered relation manager to address — omitted resolves to the sole registered manager. */
|
|
1047
1134
|
var managerShape = (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
|
|
1048
1135
|
min: 1,
|
|
1049
1136
|
description: "Which registered relation manager to address."
|
|
1050
1137
|
}));
|
|
1051
1138
|
/**
|
|
1052
|
-
*
|
|
1139
|
+
* Describes the shape of {@link import('./factories.js').createRelationTool}'s call arguments —
|
|
1053
1140
|
* discriminated by `operation` into the 5 relation operations (`'load'` / `'find'` / `'link'` /
|
|
1054
1141
|
* `'unlink'` / `'links'`).
|
|
1055
1142
|
*
|
|
@@ -1065,7 +1152,7 @@ var relationToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contr
|
|
|
1065
1152
|
min: 1,
|
|
1066
1153
|
description: "The model (table) name."
|
|
1067
1154
|
}),
|
|
1068
|
-
key:
|
|
1155
|
+
key: keyShape,
|
|
1069
1156
|
include: includeShape
|
|
1070
1157
|
}), (0, _orkestrel_contract.objectShape)({
|
|
1071
1158
|
operation: (0, _orkestrel_contract.literalShape)(["find"], { description: "List rows, with related rows attached." }),
|
|
@@ -1128,7 +1215,7 @@ var relationToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contr
|
|
|
1128
1215
|
})
|
|
1129
1216
|
}));
|
|
1130
1217
|
/**
|
|
1131
|
-
*
|
|
1218
|
+
* Describes the shape of {@link import('./factories.js').createInferTool}'s call arguments — one or more
|
|
1132
1219
|
* example `samples` to infer a JSON Schema from, plus per-call `format` / `enum` toggles and an
|
|
1133
1220
|
* optional `candidates` array to check against the inferred schema.
|
|
1134
1221
|
*
|
|
@@ -1152,24 +1239,24 @@ var inferToolShape = (0, _orkestrel_contract.objectShape)({
|
|
|
1152
1239
|
//#endregion
|
|
1153
1240
|
//#region src/core/helpers.ts
|
|
1154
1241
|
/**
|
|
1155
|
-
*
|
|
1242
|
+
* Returns the ancestry identifier of a workflow in a run chain — `workflow:<id>`.
|
|
1156
1243
|
*
|
|
1157
1244
|
* @remarks
|
|
1158
|
-
* Namespacing keeps a workflow id and
|
|
1245
|
+
* Namespacing keeps a workflow id and a {@link tagAgent} agent name in ONE set without
|
|
1159
1246
|
* collision, so re-entering a workflow OR an agent already in the chain is a single `includes`
|
|
1160
1247
|
* check.
|
|
1161
1248
|
*
|
|
1162
1249
|
* @param id - The workflow definition's `id`
|
|
1163
1250
|
* @returns The namespaced ancestry tag (`workflow:<id>`)
|
|
1164
1251
|
*/
|
|
1165
|
-
function
|
|
1252
|
+
function tagWorkflow(id) {
|
|
1166
1253
|
return `workflow:${id}`;
|
|
1167
1254
|
}
|
|
1168
1255
|
/**
|
|
1169
|
-
*
|
|
1256
|
+
* Returns the ancestry identifier of an agent in a run chain — `agent:<name>`.
|
|
1170
1257
|
*
|
|
1171
1258
|
* @remarks
|
|
1172
|
-
* The agent counterpart of {@link
|
|
1259
|
+
* The agent counterpart of {@link tagWorkflow}: {@link import('./factories.js').createAgentFunction}
|
|
1173
1260
|
* / {@link import('./factories.js').createWorkflowTool} guard against re-entering an agent or
|
|
1174
1261
|
* workflow already in the chain (this package's typed `DEPTH` `ToolboxError`). The
|
|
1175
1262
|
* `agent:` namespace keeps it distinct from a same-string workflow id.
|
|
@@ -1177,78 +1264,43 @@ function workflowTag(id) {
|
|
|
1177
1264
|
* @param name - The agent's identifier / registry name
|
|
1178
1265
|
* @returns The namespaced ancestry tag (`agent:<name>`)
|
|
1179
1266
|
*/
|
|
1180
|
-
function
|
|
1267
|
+
function tagAgent(name) {
|
|
1181
1268
|
return `agent:${name}`;
|
|
1182
1269
|
}
|
|
1183
1270
|
/**
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
* @param value - The value to inspect
|
|
1187
|
-
* @returns Whether `value` is a unique, nonempty-tagged workflow/agent chain
|
|
1188
|
-
*/
|
|
1189
|
-
function isWorkflowLineage(value) {
|
|
1190
|
-
const inspected = (0, _orkestrel_contract.attempt)(() => {
|
|
1191
|
-
if (!Array.isArray(value)) return false;
|
|
1192
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1193
|
-
for (const [index, tag] of value.entries()) {
|
|
1194
|
-
if (!(0, _orkestrel_contract.isString)(tag)) return false;
|
|
1195
|
-
const prefix = index % 2 === 0 ? "workflow:" : "agent:";
|
|
1196
|
-
if (!tag.startsWith(prefix) || tag.length === prefix.length || seen.has(tag)) return false;
|
|
1197
|
-
seen.add(tag);
|
|
1198
|
-
}
|
|
1199
|
-
return true;
|
|
1200
|
-
});
|
|
1201
|
-
return inspected.success && inspected.value;
|
|
1202
|
-
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Build a validated, copied, and frozen workflow lineage value.
|
|
1271
|
+
* Returns the canonical workflow lineage — validated, copied, and frozen.
|
|
1205
1272
|
*
|
|
1206
1273
|
* @param lineage - The configured chain; omitted means a direct root
|
|
1207
1274
|
* @returns An immutable caller-isolated lineage
|
|
1208
1275
|
*/
|
|
1209
|
-
function
|
|
1276
|
+
function normalizeLineage(lineage = []) {
|
|
1210
1277
|
if (!isWorkflowLineage(lineage)) throw new ToolboxError("TOOL", "workflow lineage must alternate unique workflow and agent tags");
|
|
1211
1278
|
return Object.freeze([...lineage]);
|
|
1212
1279
|
}
|
|
1213
1280
|
/**
|
|
1214
|
-
*
|
|
1281
|
+
* Appends one tag to a workflow lineage and returns a frozen copy.
|
|
1215
1282
|
*
|
|
1216
1283
|
* @param lineage - The valid chain to extend
|
|
1217
1284
|
* @param tag - The workflow or agent tag to append
|
|
1218
1285
|
* @returns The validated immutable extension
|
|
1219
1286
|
*/
|
|
1220
1287
|
function extendLineage(lineage, tag) {
|
|
1221
|
-
return
|
|
1288
|
+
return normalizeLineage([...lineage, tag]);
|
|
1222
1289
|
}
|
|
1223
1290
|
/**
|
|
1224
|
-
*
|
|
1291
|
+
* Derives the zero-based workflow nesting depth from a valid lineage.
|
|
1225
1292
|
*
|
|
1226
1293
|
* @param lineage - The workflow/agent chain
|
|
1227
1294
|
* @returns Zero for an empty/root lineage, then one per nested workflow
|
|
1228
1295
|
*/
|
|
1229
1296
|
function deriveWorkflowDepth(lineage) {
|
|
1230
|
-
const workflows =
|
|
1297
|
+
const workflows = normalizeLineage(lineage).filter((tag) => tag.startsWith("workflow:")).length;
|
|
1231
1298
|
return Math.max(0, workflows - 1);
|
|
1232
1299
|
}
|
|
1233
1300
|
/**
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
* @returns Whether it is a frozen {@link AgentFunction} with a frozen valid lineage
|
|
1238
|
-
*/
|
|
1239
|
-
function isAgentFunction(value) {
|
|
1240
|
-
const inspected = (0, _orkestrel_contract.attempt)(() => {
|
|
1241
|
-
if (typeof value !== "function" || !Object.isFrozen(value)) return false;
|
|
1242
|
-
const category = Reflect.getOwnPropertyDescriptor(value, "category");
|
|
1243
|
-
const lineage = Reflect.getOwnPropertyDescriptor(value, "lineage");
|
|
1244
|
-
return category?.value === "agent" && lineage !== void 0 && "value" in lineage && Object.isFrozen(lineage.value) && isWorkflowLineage(lineage.value);
|
|
1245
|
-
});
|
|
1246
|
-
return inspected.success && inspected.value;
|
|
1247
|
-
}
|
|
1248
|
-
/**
|
|
1249
|
-
* Build the plain success summary {@link import('./factories.js').createWorkflowTool} returns on
|
|
1250
|
-
* a completed run — the universal tool-handler contract (AGENTS §14): return a plain value on
|
|
1251
|
-
* success, appearing identically over BOTH the agent loop and MCP.
|
|
1301
|
+
* Builds the plain success summary {@link import('./factories.js').createWorkflowTool} returns on
|
|
1302
|
+
* a completed run — the universal tool-handler contract: return a plain value on success,
|
|
1303
|
+
* appearing identically over BOTH the agent loop and MCP.
|
|
1252
1304
|
*
|
|
1253
1305
|
* @remarks
|
|
1254
1306
|
* The summary is LEAN: the workflow's terminal `status`, settled-task count, and exact optional
|
|
@@ -1258,7 +1310,7 @@ function isAgentFunction(value) {
|
|
|
1258
1310
|
* @param result - The terminal `WorkflowResult` (`@orkestrel/workflow`) the run produced
|
|
1259
1311
|
* @returns The plain success summary — `{ status, count, durable?, fault? }`
|
|
1260
1312
|
*/
|
|
1261
|
-
function
|
|
1313
|
+
function summarizeWorkflow(result) {
|
|
1262
1314
|
return {
|
|
1263
1315
|
status: result.status,
|
|
1264
1316
|
count: result.results.length,
|
|
@@ -1267,8 +1319,8 @@ function workflowToolSummary(result) {
|
|
|
1267
1319
|
};
|
|
1268
1320
|
}
|
|
1269
1321
|
/**
|
|
1270
|
-
*
|
|
1271
|
-
* MISSING `id` deterministically + positionally, and
|
|
1322
|
+
* Completes a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesizes any
|
|
1323
|
+
* MISSING `id` deterministically + positionally, and defaults any MISSING `name` to its
|
|
1272
1324
|
* (now-resolved) `id`.
|
|
1273
1325
|
*
|
|
1274
1326
|
* @remarks
|
|
@@ -1276,7 +1328,7 @@ function workflowToolSummary(result) {
|
|
|
1276
1328
|
* `phase-<i>`, and task `j` of that phase is `<phaseId>-task-<j>` (so a provided phase id flows
|
|
1277
1329
|
* into its tasks' synthesized ids). A PROVIDED `id` / `name` at any level is kept VERBATIM —
|
|
1278
1330
|
* synthesis touches only the omitted ones. A missing `name` defaults to the resolved `id` (never
|
|
1279
|
-
* the other way round), so the result always has both. `
|
|
1331
|
+
* the other way round), so the result always has both. `behavior`, `description`, the per-phase
|
|
1280
1332
|
* `concurrency` / `bail`, the per-task `retries` / `timeout`, and the workflow `bail` carry over
|
|
1281
1333
|
* unchanged. The result is a complete {@link WorkflowDefinition}; the caller still validates it
|
|
1282
1334
|
* against the STRICT contract.
|
|
@@ -1295,7 +1347,7 @@ function completeDraft(draft) {
|
|
|
1295
1347
|
};
|
|
1296
1348
|
}
|
|
1297
1349
|
/**
|
|
1298
|
-
*
|
|
1350
|
+
* Completes one {@link PhaseDraft} into a strict phase definition — the per-phase step of
|
|
1299
1351
|
* {@link completeDraft} (phase `index` → `phase-<index>` when its id is omitted).
|
|
1300
1352
|
*
|
|
1301
1353
|
* @param phase - The draft phase
|
|
@@ -1314,7 +1366,7 @@ function completePhaseDraft(phase, index) {
|
|
|
1314
1366
|
};
|
|
1315
1367
|
}
|
|
1316
1368
|
/**
|
|
1317
|
-
*
|
|
1369
|
+
* Completes one {@link TaskDraft} into a strict task definition — the per-task leaf step of
|
|
1318
1370
|
* {@link completeDraft} (task `index` of phase `<phaseId>` → `<phaseId>-task-<index>` when its id
|
|
1319
1371
|
* is omitted).
|
|
1320
1372
|
*
|
|
@@ -1329,19 +1381,19 @@ function completeTaskDraft(task, phaseId, index) {
|
|
|
1329
1381
|
id,
|
|
1330
1382
|
name: task.name ?? id,
|
|
1331
1383
|
...task.description === void 0 ? {} : { description: task.description },
|
|
1332
|
-
...task.
|
|
1384
|
+
...task.behavior === void 0 ? {} : { behavior: task.behavior },
|
|
1333
1385
|
...task.retries === void 0 ? {} : { retries: task.retries },
|
|
1334
1386
|
...task.timeout === void 0 ? {} : { timeout: task.timeout }
|
|
1335
1387
|
};
|
|
1336
1388
|
}
|
|
1337
1389
|
/**
|
|
1338
|
-
*
|
|
1390
|
+
* Expands a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each step
|
|
1339
1391
|
* becomes a one-task phase, IN ORDER.
|
|
1340
1392
|
*
|
|
1341
1393
|
* @remarks
|
|
1342
1394
|
* The expansion of the tool's ADVERTISED surface: the deliberately-reduced flat form. Each
|
|
1343
1395
|
* {@link import('./types.js').WorkflowStep} maps to a phase holding exactly one task: the step's
|
|
1344
|
-
* `name` becomes the task's `
|
|
1396
|
+
* `name` becomes the task's `behavior` (the behavior-registry key). Ids/names are auto-filled
|
|
1345
1397
|
* positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates to
|
|
1346
1398
|
* {@link completeDraft}, so the two lenient surfaces share ONE synthesis path (step `i` → phase
|
|
1347
1399
|
* `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes both the workflow's
|
|
@@ -1358,12 +1410,12 @@ function expandSteps(flat) {
|
|
|
1358
1410
|
id: flat.name,
|
|
1359
1411
|
name: flat.name
|
|
1360
1412
|
},
|
|
1361
|
-
phases: flat.steps.map((step) => ({ tasks: [{
|
|
1413
|
+
phases: flat.steps.map((step) => ({ tasks: [{ behavior: step.name }] }))
|
|
1362
1414
|
});
|
|
1363
1415
|
}
|
|
1364
1416
|
/**
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1417
|
+
* Maps a caught error to the {@link ToolboxErrorCode} the terminal-tool factory throws with — the
|
|
1418
|
+
* pure classification step of that factory's error handling.
|
|
1367
1419
|
*
|
|
1368
1420
|
* @remarks
|
|
1369
1421
|
* Narrows `error` with {@link isTerminalError} (`@orkestrel/terminal`) first: a non-`TerminalError`
|
|
@@ -1376,111 +1428,38 @@ function expandSteps(flat) {
|
|
|
1376
1428
|
* @param error - The value caught from a terminal-manager operation (`ask` / `answer` / …)
|
|
1377
1429
|
* @returns The mapped {@link ToolboxErrorCode}, or `undefined` if `error` is not a `TerminalError`
|
|
1378
1430
|
*/
|
|
1379
|
-
function
|
|
1431
|
+
function inferTerminalCode(error) {
|
|
1380
1432
|
if (!(0, _orkestrel_terminal.isTerminalError)(error)) return void 0;
|
|
1381
1433
|
if (error.code === "DEADLOCK") return "DEADLOCK";
|
|
1382
1434
|
if (error.code === "EXPIRE") return "EXPIRE";
|
|
1383
1435
|
return "TOOL";
|
|
1384
1436
|
}
|
|
1385
|
-
/** Narrow an unknown value to a {@link ColumnSpec} — a valid {@link import('./types.js').ColumnKind} shorthand, or `{ type, optional }` with a valid `type`. */
|
|
1386
|
-
function isColumnSpec(value) {
|
|
1387
|
-
if (isColumnKind(value)) return true;
|
|
1388
|
-
if (!(0, _orkestrel_contract.isRecord)(value)) return false;
|
|
1389
|
-
return isColumnKind(value.type) && (value.optional === void 0 || typeof value.optional === "boolean");
|
|
1390
|
-
}
|
|
1391
|
-
/** Narrow an unknown value to a {@link import('./types.js').ColumnKind}. */
|
|
1392
|
-
function isColumnKind(value) {
|
|
1393
|
-
return value === "string" || value === "integer" || value === "number" || value === "boolean";
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Compile a {@link TableSpec} into the `@orkestrel/database` {@link TableMap} it configures —
|
|
1397
|
-
* each {@link ColumnSpec} maps to the matching primitive shaper (`'string'` → `stringShape()`,
|
|
1398
|
-
* `'integer'` → `integerShape()`, `'number'` → `numberShape()`, `'boolean'` → `booleanShape()`),
|
|
1399
|
-
* wrapped in `optionalShape` when the column declares `optional: true`. Total, pure.
|
|
1400
|
-
*
|
|
1401
|
-
* @param spec - The small-model-facing table layout
|
|
1402
|
-
* @returns The compiled `TableMap` a `@orkestrel/database` `createDatabase` call accepts
|
|
1403
|
-
*/
|
|
1404
|
-
function expandTables(spec) {
|
|
1405
|
-
const tables = {};
|
|
1406
|
-
for (const [table, definition] of Object.entries(spec)) {
|
|
1407
|
-
const columns = {};
|
|
1408
|
-
for (const [column, kind] of Object.entries(definition.columns)) columns[column] = columnShape(kind);
|
|
1409
|
-
tables[table] = columns;
|
|
1410
|
-
}
|
|
1411
|
-
return tables;
|
|
1412
|
-
}
|
|
1413
|
-
/** Compile one {@link ColumnSpec} into its `@orkestrel/database` column shape — the per-column leaf {@link expandTables} maps over. */
|
|
1414
|
-
function columnShape(spec) {
|
|
1415
|
-
const kind = (0, _orkestrel_contract.isString)(spec) ? spec : spec.type;
|
|
1416
|
-
const optional = !(0, _orkestrel_contract.isString)(spec) && spec.optional === true;
|
|
1417
|
-
const shape = kindShape(kind);
|
|
1418
|
-
return optional ? (0, _orkestrel_contract.optionalShape)(shape) : shape;
|
|
1419
|
-
}
|
|
1420
|
-
/** Map one {@link import('./types.js').ColumnKind} to its primitive `@orkestrel/database` shape — the leaf {@link columnShape} wraps. */
|
|
1421
|
-
function kindShape(kind) {
|
|
1422
|
-
if (kind === "string") return (0, _orkestrel_contract.stringShape)();
|
|
1423
|
-
if (kind === "integer") return (0, _orkestrel_contract.integerShape)();
|
|
1424
|
-
if (kind === "number") return (0, _orkestrel_contract.numberShape)();
|
|
1425
|
-
return (0, _orkestrel_contract.booleanShape)();
|
|
1426
|
-
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Narrow an unknown value to a {@link DatabaseDefinition} — a non-empty `id` + `driver`, a
|
|
1429
|
-
* `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
|
|
1430
|
-
* `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
|
|
1431
|
-
* {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
|
|
1432
|
-
* before trusting it as a definition (never an `as`).
|
|
1433
|
-
*/
|
|
1434
|
-
function isDatabaseDefinition(value) {
|
|
1435
|
-
if (!(0, _orkestrel_contract.isRecord)(value)) return false;
|
|
1436
|
-
if ("keys" in value) return false;
|
|
1437
|
-
if (!(0, _orkestrel_contract.isNonEmptyString)(value.id) || !(0, _orkestrel_contract.isNonEmptyString)(value.driver)) return false;
|
|
1438
|
-
if (!(0, _orkestrel_contract.isRecord)(value.tables)) return false;
|
|
1439
|
-
for (const table of Object.values(value.tables)) {
|
|
1440
|
-
if (!(0, _orkestrel_contract.isRecord)(table) || !(0, _orkestrel_contract.isRecord)(table.columns)) return false;
|
|
1441
|
-
for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
|
|
1442
|
-
}
|
|
1443
|
-
if (value.primary !== void 0) {
|
|
1444
|
-
if (!(0, _orkestrel_contract.isRecord)(value.primary)) return false;
|
|
1445
|
-
for (const key of Object.values(value.primary)) if (!(0, _orkestrel_contract.isNonEmptyString)(key)) return false;
|
|
1446
|
-
}
|
|
1447
|
-
if (value.indexes !== void 0) {
|
|
1448
|
-
if (!(0, _orkestrel_contract.isRecord)(value.indexes)) return false;
|
|
1449
|
-
for (const groups of Object.values(value.indexes)) {
|
|
1450
|
-
if (!Array.isArray(groups)) return false;
|
|
1451
|
-
for (const group of groups) {
|
|
1452
|
-
if (!Array.isArray(group) || group.length === 0) return false;
|
|
1453
|
-
for (const column of group) if (!(0, _orkestrel_contract.isNonEmptyString)(column)) return false;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
if (value.version !== void 0 && !(0, _orkestrel_contract.isFiniteNumber)(value.version)) return false;
|
|
1458
|
-
return true;
|
|
1459
|
-
}
|
|
1460
1437
|
/**
|
|
1461
|
-
*
|
|
1462
|
-
* with — the pure classification step
|
|
1463
|
-
* {@link
|
|
1438
|
+
* Maps a caught error to the granular {@link DatabaseErrorCode} (`@orkestrel/database`) the code
|
|
1439
|
+
* {@link import('./factories.js').createDatabaseTool} throws with — the pure classification step
|
|
1440
|
+
* of that factory's error handling, mirroring {@link inferTerminalCode}'s idiom for
|
|
1441
|
+
* `@orkestrel/database`.
|
|
1464
1442
|
*
|
|
1465
1443
|
* @param error - The value caught from a `@orkestrel/database` table operation
|
|
1466
1444
|
* @returns The granular {@link DatabaseErrorCode}, or `undefined` if `error` is not a `DatabaseError`
|
|
1467
1445
|
*/
|
|
1468
|
-
function
|
|
1446
|
+
function inferDatabaseCode(error) {
|
|
1469
1447
|
return (0, _orkestrel_database.isDatabaseError)(error) ? error.code : void 0;
|
|
1470
1448
|
}
|
|
1471
1449
|
/**
|
|
1472
|
-
*
|
|
1473
|
-
* with — the pure classification step
|
|
1474
|
-
* {@link
|
|
1450
|
+
* Maps a caught error to the granular {@link RelationErrorCode} (`@orkestrel/relation`) the code
|
|
1451
|
+
* {@link import('./factories.js').createRelationTool} throws with — the pure classification step
|
|
1452
|
+
* of that factory's error handling, mirroring {@link inferTerminalCode}'s idiom for
|
|
1453
|
+
* `@orkestrel/relation`.
|
|
1475
1454
|
*
|
|
1476
1455
|
* @param error - The value caught from a `@orkestrel/relation` operation
|
|
1477
1456
|
* @returns The granular {@link RelationErrorCode}, or `undefined` if `error` is not a `RelationError`
|
|
1478
1457
|
*/
|
|
1479
|
-
function
|
|
1458
|
+
function inferRelationCode(error) {
|
|
1480
1459
|
return (0, _orkestrel_relation.isRelationError)(error) ? error.code : void 0;
|
|
1481
1460
|
}
|
|
1482
1461
|
/**
|
|
1483
|
-
*
|
|
1462
|
+
* Expands the relation tool's FLAT dot-path `include` list into a live `@orkestrel/relation`
|
|
1484
1463
|
* {@link Include} tree — the pure leaf {@link import('./factories.js').createRelationTool} calls
|
|
1485
1464
|
* before a `'load'` / `'find'` call.
|
|
1486
1465
|
*
|
|
@@ -1551,7 +1530,7 @@ function expandInclude(paths, depth) {
|
|
|
1551
1530
|
return include;
|
|
1552
1531
|
}
|
|
1553
1532
|
/**
|
|
1554
|
-
*
|
|
1533
|
+
* Resolves which registered {@link RelationManagerInterface} a relation-tool call addresses — the
|
|
1555
1534
|
* pure manager-resolution leaf {@link import('./factories.js').createRelationTool} calls on
|
|
1556
1535
|
* every operation.
|
|
1557
1536
|
*
|
|
@@ -1564,7 +1543,7 @@ function expandInclude(paths, depth) {
|
|
|
1564
1543
|
* @param name - The call's optional `manager` field
|
|
1565
1544
|
* @returns The resolved {@link RelationManagerInterface}
|
|
1566
1545
|
*/
|
|
1567
|
-
function
|
|
1546
|
+
function resolveRelationManager(managers, name) {
|
|
1568
1547
|
if (name !== void 0) {
|
|
1569
1548
|
const manager = managers[name];
|
|
1570
1549
|
if (manager === void 0) throw new ToolboxError("TOOL", `unknown relation manager '${name}'`, {
|
|
@@ -1582,24 +1561,24 @@ function relationManagerOf(managers, name) {
|
|
|
1582
1561
|
throw new ToolboxError("TOOL", "no relation manager resolved for the call", { managers: names });
|
|
1583
1562
|
}
|
|
1584
1563
|
/**
|
|
1585
|
-
*
|
|
1564
|
+
* Resolves a `model` name against a live {@link RelationManagerInterface} — the pure model-lookup
|
|
1586
1565
|
* leaf {@link import('./factories.js').createRelationTool} calls on every operation, mirroring
|
|
1587
|
-
* {@link
|
|
1566
|
+
* {@link resolveRelationManager}'s guard shape.
|
|
1588
1567
|
*
|
|
1589
1568
|
* @param manager - The resolved {@link RelationManagerInterface}
|
|
1590
1569
|
* @param name - The call's `model` field
|
|
1591
1570
|
* @returns The model's {@link ModelInterface}
|
|
1592
1571
|
*/
|
|
1593
|
-
function
|
|
1572
|
+
function resolveRelationModel(manager, name) {
|
|
1594
1573
|
if (!manager.has(name)) throw new ToolboxError("TOOL", `unknown model '${name}'`, {
|
|
1595
1574
|
model: name,
|
|
1596
|
-
models: manager.
|
|
1575
|
+
models: manager.names()
|
|
1597
1576
|
});
|
|
1598
1577
|
return manager.model(name);
|
|
1599
1578
|
}
|
|
1600
1579
|
/**
|
|
1601
|
-
*
|
|
1602
|
-
*
|
|
1580
|
+
* Returns the canonical live `@orkestrel/database` {@link QueryInput} for the database tool's
|
|
1581
|
+
* parsed SERIALIZED query — each condition's OMITTED `connector` defaults to `'and'`.
|
|
1603
1582
|
*
|
|
1604
1583
|
* @remarks
|
|
1605
1584
|
* The wire form ({@link import('./shapers.js').databaseToolShape}) lets a caller drop `connector`
|
|
@@ -1610,7 +1589,7 @@ function relationModelOf(manager, name) {
|
|
|
1610
1589
|
* @param query - The parsed query (or `undefined`)
|
|
1611
1590
|
* @returns The equivalent live `QueryInput`, or `undefined` when `query` is `undefined`
|
|
1612
1591
|
*/
|
|
1613
|
-
function
|
|
1592
|
+
function normalizeQuery(query) {
|
|
1614
1593
|
if (query === void 0) return void 0;
|
|
1615
1594
|
const conditions = query.conditions?.map((condition) => ({
|
|
1616
1595
|
...condition,
|
|
@@ -1624,13 +1603,38 @@ function queryOf(query) {
|
|
|
1624
1603
|
};
|
|
1625
1604
|
}
|
|
1626
1605
|
/**
|
|
1627
|
-
*
|
|
1606
|
+
* Picks the effective row limit a tool reads with — the requested count when it sits inside the
|
|
1607
|
+
* cap, the cap when it exceeds it, and `0` when either falls below zero.
|
|
1608
|
+
*
|
|
1609
|
+
* @remarks
|
|
1610
|
+
* The one place the database tool's `'records'` clamp ({@link clampQuery}) and the relation tool's
|
|
1611
|
+
* `'find'` / `'links'` truncation both take their ceiling from, so a negative construction-time
|
|
1612
|
+
* option can never reach a slice. Pure and total.
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* ```ts
|
|
1616
|
+
* import { resolveLimit } from '@src/core'
|
|
1617
|
+
*
|
|
1618
|
+
* resolveLimit(undefined, 100) // 100 — an omitted request takes the cap
|
|
1619
|
+
* resolveLimit(500, 100) // 100 — a request over the cap is clamped down
|
|
1620
|
+
* resolveLimit(-1, 100) // 0 — a negative request floors at zero
|
|
1621
|
+
* ```
|
|
1622
|
+
*
|
|
1623
|
+
* @param requested - The caller's requested row count (or `undefined`)
|
|
1624
|
+
* @param cap - The row-count ceiling
|
|
1625
|
+
* @returns The effective row limit, never below `0` and never above `cap`
|
|
1626
|
+
*/
|
|
1627
|
+
function resolveLimit(requested, cap) {
|
|
1628
|
+
return Math.max(0, Math.min(requested ?? cap, cap));
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Clamps a `'records'` call's query to a row cap, and builds the PROBE query the caller reads
|
|
1628
1632
|
* with — the pure leaf {@link import('./factories.js').createDatabaseTool}'s `'records'` operation
|
|
1629
1633
|
* uses to detect truncation without a separate `count` round trip.
|
|
1630
1634
|
*
|
|
1631
1635
|
* @remarks
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1636
|
+
* {@link resolveLimit} picks the effective limit, so a caller can never exceed the configured cap
|
|
1637
|
+
* by supplying a larger `query.limit`, and can never drive it below `0`. The returned probe query
|
|
1634
1638
|
* requests ONE MORE row than the effective limit (`limit: effective + 1`) — if storage returns
|
|
1635
1639
|
* that many, the caller knows the true result was truncated (`rows.length > effective`) and slices
|
|
1636
1640
|
* back down to `effective` before returning.
|
|
@@ -1650,7 +1654,7 @@ function queryOf(query) {
|
|
|
1650
1654
|
* @returns The PROBE query (`limit` bumped by one) and the effective `limit`
|
|
1651
1655
|
*/
|
|
1652
1656
|
function clampQuery(query, cap) {
|
|
1653
|
-
const limit =
|
|
1657
|
+
const limit = resolveLimit(query?.limit, cap);
|
|
1654
1658
|
return {
|
|
1655
1659
|
query: {
|
|
1656
1660
|
...query,
|
|
@@ -1660,15 +1664,64 @@ function clampQuery(query, cap) {
|
|
|
1660
1664
|
};
|
|
1661
1665
|
}
|
|
1662
1666
|
//#endregion
|
|
1667
|
+
//#region src/core/compilers.ts
|
|
1668
|
+
/**
|
|
1669
|
+
* Compiles a {@link TableSpec} into the `@orkestrel/database` {@link TableMap} it configures —
|
|
1670
|
+
* each {@link ColumnSpec} maps to the matching primitive shaper (`'string'` → `stringShape()`,
|
|
1671
|
+
* `'integer'` → `integerShape()`, `'number'` → `numberShape()`, `'boolean'` → `booleanShape()`),
|
|
1672
|
+
* wrapped in `optionalShape` when the column declares `optional: true`. Total, pure.
|
|
1673
|
+
*
|
|
1674
|
+
* @param spec - The small-model-facing table layout
|
|
1675
|
+
* @returns The compiled `TableMap` a `@orkestrel/database` `createDatabase` call accepts
|
|
1676
|
+
*/
|
|
1677
|
+
function expandTables(spec) {
|
|
1678
|
+
const tables = {};
|
|
1679
|
+
for (const [table, definition] of Object.entries(spec)) {
|
|
1680
|
+
const columns = {};
|
|
1681
|
+
for (const [column, columnSpec] of Object.entries(definition.columns)) columns[column] = compileColumn(columnSpec);
|
|
1682
|
+
tables[table] = columns;
|
|
1683
|
+
}
|
|
1684
|
+
return tables;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Compiles one {@link ColumnSpec} into its `@orkestrel/database` column shape — the per-column leaf
|
|
1688
|
+
* {@link expandTables} maps over.
|
|
1689
|
+
*
|
|
1690
|
+
* @param spec - The column spec to compile
|
|
1691
|
+
* @returns The `@orkestrel/database` column shape, wrapped in `optionalShape` when the spec declares `optional: true`
|
|
1692
|
+
*/
|
|
1693
|
+
function compileColumn(spec) {
|
|
1694
|
+
const primitive = (0, _orkestrel_contract.isString)(spec) ? spec : spec.primitive;
|
|
1695
|
+
const optional = !(0, _orkestrel_contract.isString)(spec) && spec.optional === true;
|
|
1696
|
+
const shape = compileColumnPrimitive(primitive);
|
|
1697
|
+
return optional ? (0, _orkestrel_contract.optionalShape)(shape) : shape;
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Compiles one {@link import('./types.js').ColumnPrimitive} into its primitive
|
|
1701
|
+
* `@orkestrel/database` shape — the leaf {@link compileColumn} wraps.
|
|
1702
|
+
*
|
|
1703
|
+
* @param primitive - The declared column primitive
|
|
1704
|
+
* @returns The matching primitive `@orkestrel/database` shape
|
|
1705
|
+
*/
|
|
1706
|
+
function compileColumnPrimitive(primitive) {
|
|
1707
|
+
if (primitive === "string") return (0, _orkestrel_contract.stringShape)();
|
|
1708
|
+
if (primitive === "integer") return (0, _orkestrel_contract.integerShape)();
|
|
1709
|
+
if (primitive === "number") return (0, _orkestrel_contract.numberShape)();
|
|
1710
|
+
return (0, _orkestrel_contract.booleanShape)();
|
|
1711
|
+
}
|
|
1712
|
+
//#endregion
|
|
1663
1713
|
//#region src/core/stores/MemoryDefinitionStore.ts
|
|
1664
1714
|
/**
|
|
1665
|
-
*
|
|
1715
|
+
* Represents the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of
|
|
1666
1716
|
* {@link DatabaseDefinition}s keyed by database id, the DEFAULT store
|
|
1667
|
-
* {@link import('../factories.js').createMemoryDefinitionStore} builds.
|
|
1668
|
-
* {@link
|
|
1717
|
+
* {@link import('../factories.js').createMemoryDefinitionStore} builds. It implements the same
|
|
1718
|
+
* {@link DefinitionStoreInterface} contract as
|
|
1719
|
+
* {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}: this store copies on
|
|
1720
|
+
* write and on read; the table-backed store narrows an untrusted stored blob and reports
|
|
1721
|
+
* `undefined` for a malformed one.
|
|
1669
1722
|
*
|
|
1670
1723
|
* @remarks
|
|
1671
|
-
* A plain `Map<string, DatabaseDefinition>` (
|
|
1724
|
+
* A plain `Map<string, DatabaseDefinition>` (the definition is already pure,
|
|
1672
1725
|
* self-contained CONFIG-only JSON, so no encoding is needed for the memory tier). Values are
|
|
1673
1726
|
* structured-cloned on both write and read, keeping caller mutation outside the store. There is NO
|
|
1674
1727
|
* idle-TTL and NO eviction: a persisted definition lives until an explicit `delete`. A durable
|
|
@@ -1681,7 +1734,7 @@ function clampQuery(query, cap) {
|
|
|
1681
1734
|
* - **`set` inserts / replaces under the definition's OWN `id`** (no separate id param).
|
|
1682
1735
|
* - **`delete` drops a definition by id**; an absent id is a no-op (no throw).
|
|
1683
1736
|
*
|
|
1684
|
-
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the
|
|
1737
|
+
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the method
|
|
1685
1738
|
* bijection with {@link DefinitionStoreInterface}).
|
|
1686
1739
|
*
|
|
1687
1740
|
* @example
|
|
@@ -1696,14 +1749,32 @@ function clampQuery(query, cap) {
|
|
|
1696
1749
|
*/
|
|
1697
1750
|
var MemoryDefinitionStore = class {
|
|
1698
1751
|
#definitions = /* @__PURE__ */ new Map();
|
|
1752
|
+
/**
|
|
1753
|
+
* Resolves the persisted definition for `id`, copied out of the backing `Map`.
|
|
1754
|
+
*
|
|
1755
|
+
* @param id - The database id to read
|
|
1756
|
+
* @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored
|
|
1757
|
+
*/
|
|
1699
1758
|
get(id) {
|
|
1700
1759
|
const definition = this.#definitions.get(id);
|
|
1701
1760
|
return Promise.resolve(definition === void 0 ? void 0 : structuredClone(definition));
|
|
1702
1761
|
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Inserts or replaces a definition under its OWN `id`, copied into the backing `Map`.
|
|
1764
|
+
*
|
|
1765
|
+
* @param definition - The config to persist; its own `id` is the key (no separate id param)
|
|
1766
|
+
* @returns A promise settling once the definition is stored
|
|
1767
|
+
*/
|
|
1703
1768
|
set(definition) {
|
|
1704
1769
|
this.#definitions.set(definition.id, structuredClone(definition));
|
|
1705
1770
|
return Promise.resolve();
|
|
1706
1771
|
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Drops the definition stored under `id`.
|
|
1774
|
+
*
|
|
1775
|
+
* @param id - The database id to drop; an absent id is a no-op, never a throw
|
|
1776
|
+
* @returns A promise settling once no definition is stored under `id`
|
|
1777
|
+
*/
|
|
1707
1778
|
delete(id) {
|
|
1708
1779
|
this.#definitions.delete(id);
|
|
1709
1780
|
return Promise.resolve();
|
|
@@ -1712,7 +1783,7 @@ var MemoryDefinitionStore = class {
|
|
|
1712
1783
|
//#endregion
|
|
1713
1784
|
//#region src/core/stores/DatabaseDefinitionStore.ts
|
|
1714
1785
|
/**
|
|
1715
|
-
*
|
|
1786
|
+
* Represents a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database` layer — a
|
|
1716
1787
|
* database's durable CONFIG state IS a row, so persistence reduces to keyed point-access
|
|
1717
1788
|
* (`get` / `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the
|
|
1718
1789
|
* plain-`Map` {@link import('./MemoryDefinitionStore.js').MemoryDefinitionStore}.
|
|
@@ -1734,12 +1805,12 @@ var MemoryDefinitionStore = class {
|
|
|
1734
1805
|
* - **`set(definition)` upserts under the definition's OWN `id`** (no separate id param) — it
|
|
1735
1806
|
* writes the row `{ id: definition.id, definition }`.
|
|
1736
1807
|
* - **`get(id)` resolves the stored definition for an id**, narrowing the opaque JSON column back
|
|
1737
|
-
* to a {@link DatabaseDefinition} ({@link import('../
|
|
1738
|
-
*
|
|
1808
|
+
* to a {@link DatabaseDefinition} ({@link import('../validators.js').isDatabaseDefinition} — the
|
|
1809
|
+
* boundary narrow for an untrusted storage read), or `undefined` if none is stored
|
|
1739
1810
|
* or the stored blob is malformed.
|
|
1740
1811
|
* - **`delete(id)` drops a definition by id**; an absent id is a no-op (no throw).
|
|
1741
1812
|
*
|
|
1742
|
-
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the
|
|
1813
|
+
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the method
|
|
1743
1814
|
* bijection with {@link DefinitionStoreInterface}).
|
|
1744
1815
|
*
|
|
1745
1816
|
* @example
|
|
@@ -1755,7 +1826,7 @@ var MemoryDefinitionStore = class {
|
|
|
1755
1826
|
var DatabaseDefinitionStore = class {
|
|
1756
1827
|
#table;
|
|
1757
1828
|
/**
|
|
1758
|
-
*
|
|
1829
|
+
* Wraps a table as a definition store.
|
|
1759
1830
|
*
|
|
1760
1831
|
* @param table - The {@link TableInterface} holding the definitions — its row is the
|
|
1761
1832
|
* {@link DatabaseDefinitionRow} `{ id; definition }` shape (the definition one opaque JSON column)
|
|
@@ -1763,20 +1834,37 @@ var DatabaseDefinitionStore = class {
|
|
|
1763
1834
|
constructor(table) {
|
|
1764
1835
|
this.#table = table;
|
|
1765
1836
|
}
|
|
1766
|
-
/**
|
|
1837
|
+
/**
|
|
1838
|
+
* Resolves the persisted definition for `id`, narrowing the opaque JSON column back to a
|
|
1839
|
+
* {@link DatabaseDefinition}.
|
|
1840
|
+
*
|
|
1841
|
+
* @param id - The database id to read
|
|
1842
|
+
* @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored or the
|
|
1843
|
+
* stored blob is malformed
|
|
1844
|
+
*/
|
|
1767
1845
|
async get(id) {
|
|
1768
1846
|
const row = await this.#table.get(id);
|
|
1769
1847
|
if (row === void 0) return void 0;
|
|
1770
1848
|
return isDatabaseDefinition(row.definition) ? row.definition : void 0;
|
|
1771
1849
|
}
|
|
1772
|
-
/**
|
|
1850
|
+
/**
|
|
1851
|
+
* Inserts or replaces a definition under its OWN `id` — the written row is `{ id, definition }`.
|
|
1852
|
+
*
|
|
1853
|
+
* @param definition - The config to persist; its own `id` is the row key (no separate id param)
|
|
1854
|
+
* @returns A promise settling once the row is written
|
|
1855
|
+
*/
|
|
1773
1856
|
async set(definition) {
|
|
1774
1857
|
await this.#table.set({
|
|
1775
1858
|
id: definition.id,
|
|
1776
1859
|
definition
|
|
1777
1860
|
});
|
|
1778
1861
|
}
|
|
1779
|
-
/**
|
|
1862
|
+
/**
|
|
1863
|
+
* Drops the definition stored under `id`.
|
|
1864
|
+
*
|
|
1865
|
+
* @param id - The database id to drop; an absent id is a no-op, never a throw
|
|
1866
|
+
* @returns A promise settling once no row is stored under `id`
|
|
1867
|
+
*/
|
|
1780
1868
|
async delete(id) {
|
|
1781
1869
|
await this.#table.remove(id);
|
|
1782
1870
|
}
|
|
@@ -1784,7 +1872,7 @@ var DatabaseDefinitionStore = class {
|
|
|
1784
1872
|
//#endregion
|
|
1785
1873
|
//#region src/core/databases/DatabaseResolver.ts
|
|
1786
1874
|
/**
|
|
1787
|
-
*
|
|
1875
|
+
* Resolves database definitions into cached live handles for database tools.
|
|
1788
1876
|
*
|
|
1789
1877
|
* @example
|
|
1790
1878
|
* ```ts
|
|
@@ -1800,7 +1888,7 @@ var DatabaseResolver = class {
|
|
|
1800
1888
|
#generator;
|
|
1801
1889
|
#store;
|
|
1802
1890
|
/**
|
|
1803
|
-
*
|
|
1891
|
+
* Creates a database resolver over the tool's live state and optional definition store.
|
|
1804
1892
|
*
|
|
1805
1893
|
* @param handles - Initial live database handles cached by id
|
|
1806
1894
|
* @param drivers - Driver factories keyed by definition driver name
|
|
@@ -1814,16 +1902,16 @@ var DatabaseResolver = class {
|
|
|
1814
1902
|
this.#store = store;
|
|
1815
1903
|
}
|
|
1816
1904
|
/**
|
|
1817
|
-
*
|
|
1905
|
+
* Determines whether a live database is cached by id.
|
|
1818
1906
|
*
|
|
1819
1907
|
* @param id - Database id
|
|
1820
|
-
* @returns
|
|
1908
|
+
* @returns True if a live handle is cached; false otherwise
|
|
1821
1909
|
*/
|
|
1822
1910
|
has(id) {
|
|
1823
1911
|
return this.#handles.has(id);
|
|
1824
1912
|
}
|
|
1825
1913
|
/**
|
|
1826
|
-
*
|
|
1914
|
+
* Reads a cached database without consulting the definition store.
|
|
1827
1915
|
*
|
|
1828
1916
|
* @param id - Database id
|
|
1829
1917
|
* @returns The cached live database, or `undefined`
|
|
@@ -1832,7 +1920,7 @@ var DatabaseResolver = class {
|
|
|
1832
1920
|
return this.#handles.get(id);
|
|
1833
1921
|
}
|
|
1834
1922
|
/**
|
|
1835
|
-
*
|
|
1923
|
+
* Caches a live database by id.
|
|
1836
1924
|
*
|
|
1837
1925
|
* @param id - Database id
|
|
1838
1926
|
* @param database - Live database handle
|
|
@@ -1842,7 +1930,7 @@ var DatabaseResolver = class {
|
|
|
1842
1930
|
this.#handles.set(id, database);
|
|
1843
1931
|
}
|
|
1844
1932
|
/**
|
|
1845
|
-
*
|
|
1933
|
+
* Removes a cached live database by id.
|
|
1846
1934
|
*
|
|
1847
1935
|
* @param id - Database id
|
|
1848
1936
|
* @returns Nothing
|
|
@@ -1851,7 +1939,7 @@ var DatabaseResolver = class {
|
|
|
1851
1939
|
this.#handles.delete(id);
|
|
1852
1940
|
}
|
|
1853
1941
|
/**
|
|
1854
|
-
*
|
|
1942
|
+
* Resolves a cached or stored database by id.
|
|
1855
1943
|
*
|
|
1856
1944
|
* @param id - Database definition id
|
|
1857
1945
|
* @returns The cached or newly constructed live database
|
|
@@ -1886,7 +1974,7 @@ var DatabaseResolver = class {
|
|
|
1886
1974
|
//#endregion
|
|
1887
1975
|
//#region src/core/factories.ts
|
|
1888
1976
|
/**
|
|
1889
|
-
*
|
|
1977
|
+
* Wraps a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the OPT-IN
|
|
1890
1978
|
* adapter that lets a `function`-form task run a `@orkestrel/tool` tool BY NAME.
|
|
1891
1979
|
*
|
|
1892
1980
|
* @remarks
|
|
@@ -1925,7 +2013,7 @@ function createToolFunction(tools, name) {
|
|
|
1925
2013
|
};
|
|
1926
2014
|
}
|
|
1927
2015
|
/**
|
|
1928
|
-
*
|
|
2016
|
+
* Wraps a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction}
|
|
1929
2017
|
* (`@orkestrel/workflow`) — the opt-in adapter that runs the agent to a settled result and carries
|
|
1930
2018
|
* immutable lineage metadata for contextual Toolbox composition.
|
|
1931
2019
|
*
|
|
@@ -1933,7 +2021,7 @@ function createToolFunction(tools, name) {
|
|
|
1933
2021
|
* Composes into a caller's registry like any other behavior; the pure workflow runner has no
|
|
1934
2022
|
* knowledge of agents itself. A root adapter derives its workflow lineage from the controller,
|
|
1935
2023
|
* while a contextual adapter requires an exact match. Repeated agents, repeated workflows, and
|
|
1936
|
-
* targets deeper than {@link import('./constants.js').
|
|
2024
|
+
* targets deeper than {@link import('./constants.js').MAX_WORKFLOW_CHAIN} are rejected before
|
|
1937
2025
|
* agent or runner activity. When {@link import('./types.js').AgentFunctionOptions.runner} is
|
|
1938
2026
|
* supplied, the adapter binds a recursion-safe {@link createWorkflowTool} onto the agent's
|
|
1939
2027
|
* `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The
|
|
@@ -1961,12 +2049,12 @@ function createToolFunction(tools, name) {
|
|
|
1961
2049
|
* ```
|
|
1962
2050
|
*/
|
|
1963
2051
|
function createAgentFunction(agent, options) {
|
|
1964
|
-
const lineage =
|
|
2052
|
+
const lineage = normalizeLineage(options?.lineage);
|
|
1965
2053
|
if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "agent function lineage must end with a workflow tag");
|
|
1966
2054
|
const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
|
|
1967
2055
|
const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
|
|
1968
2056
|
return Object.freeze(Object.assign(async (controller) => {
|
|
1969
|
-
const workflow =
|
|
2057
|
+
const workflow = tagWorkflow(controller.task.phase.workflow.id);
|
|
1970
2058
|
const current = lineage.length === 0 ? extendLineage(lineage, workflow) : lineage;
|
|
1971
2059
|
if (current.at(-1) !== workflow) throw new ToolboxError("DEPTH", "agent function workflow does not match its lineage", {
|
|
1972
2060
|
workflow,
|
|
@@ -1978,7 +2066,7 @@ function createAgentFunction(agent, options) {
|
|
|
1978
2066
|
depth,
|
|
1979
2067
|
max: 8
|
|
1980
2068
|
});
|
|
1981
|
-
const tag =
|
|
2069
|
+
const tag = tagAgent(agent.id);
|
|
1982
2070
|
if (current.includes(tag)) throw new ToolboxError("DEPTH", `agent '${agent.id}' is already an ancestor (cycle)`, {
|
|
1983
2071
|
agent: agent.id,
|
|
1984
2072
|
lineage: [...current]
|
|
@@ -2010,7 +2098,7 @@ function createAgentFunction(agent, options) {
|
|
|
2010
2098
|
}));
|
|
2011
2099
|
}
|
|
2012
2100
|
/**
|
|
2013
|
-
*
|
|
2101
|
+
* Composes opaque host functions and raw agents into one immutable workflow registry.
|
|
2014
2102
|
*
|
|
2015
2103
|
* @remarks
|
|
2016
2104
|
* This is the target-aware composition boundary used by direct runner consumers and
|
|
@@ -2037,7 +2125,7 @@ function createAgentFunction(agent, options) {
|
|
|
2037
2125
|
* ```
|
|
2038
2126
|
*/
|
|
2039
2127
|
function createWorkflowFunctions(runner, options) {
|
|
2040
|
-
const lineage =
|
|
2128
|
+
const lineage = normalizeLineage(options?.lineage);
|
|
2041
2129
|
if (lineage.length > 0 && !lineage.at(-1)?.startsWith("workflow:")) throw new ToolboxError("TOOL", "workflow functions lineage must end with a workflow tag");
|
|
2042
2130
|
const functions = options?.functions === void 0 ? Object.freeze({}) : Object.freeze({ ...options.functions });
|
|
2043
2131
|
const agents = options?.agents === void 0 ? Object.freeze({}) : Object.freeze({ ...options.agents });
|
|
@@ -2061,7 +2149,7 @@ function createWorkflowFunctions(runner, options) {
|
|
|
2061
2149
|
return Object.freeze(composed);
|
|
2062
2150
|
}
|
|
2063
2151
|
/**
|
|
2064
|
-
*
|
|
2152
|
+
* Compiles the lenient workflow draft contract used by {@link createWorkflowTool}.
|
|
2065
2153
|
*
|
|
2066
2154
|
* @returns The compiled {@link import('./types.js').WorkflowDraft} contract
|
|
2067
2155
|
*/
|
|
@@ -2069,7 +2157,7 @@ function createWorkflowDraftContract() {
|
|
|
2069
2157
|
return (0, _orkestrel_contract.createContract)(workflowDraftShape);
|
|
2070
2158
|
}
|
|
2071
2159
|
/**
|
|
2072
|
-
*
|
|
2160
|
+
* Wraps a {@link WorkflowDefinition} as an LLM-callable tool — it ADVERTISES the SIMPLE flat
|
|
2073
2161
|
* authoring shape (`{ name?, steps: [{ name }] }`) as its `parameters` so even a small model can
|
|
2074
2162
|
* author a complete tree, and its handler EXPANDS / COMPLETES the authored blob, validates it
|
|
2075
2163
|
* against the STRICT contract, and runs it through `runner`, forwarding the caller's optional
|
|
@@ -2095,7 +2183,7 @@ function createWorkflowDraftContract() {
|
|
|
2095
2183
|
* - the full nested {@link WorkflowDefinition} — the advanced escape-hatch, accepted as the draft
|
|
2096
2184
|
* super-set.
|
|
2097
2185
|
*
|
|
2098
|
-
* The universal tool-handler contract
|
|
2186
|
+
* The universal tool-handler contract: returns the plain run summary
|
|
2099
2187
|
* (`{ status, count, durable?, fault? }`) on success, THROWS a typed `ToolboxError` on Toolbox
|
|
2100
2188
|
* boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
|
|
2101
2189
|
* (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
|
|
@@ -2127,7 +2215,7 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2127
2215
|
const strict = (0, _orkestrel_workflow.createWorkflowContract)();
|
|
2128
2216
|
const draft = createWorkflowDraftContract();
|
|
2129
2217
|
const steps = (0, _orkestrel_contract.createContract)(workflowStepsShape);
|
|
2130
|
-
const lineage =
|
|
2218
|
+
const lineage = normalizeLineage(options?.lineage);
|
|
2131
2219
|
if (lineage.length > 0 && !lineage.at(-1)?.startsWith("agent:")) throw new ToolboxError("TOOL", "workflow tool lineage must end with an agent tag");
|
|
2132
2220
|
const functions = options?.functions === void 0 ? void 0 : Object.freeze({ ...options.functions });
|
|
2133
2221
|
const agents = options?.agents === void 0 ? void 0 : Object.freeze({ ...options.agents });
|
|
@@ -2152,7 +2240,7 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2152
2240
|
target = parsed === void 0 ? void 0 : completeDraft(parsed);
|
|
2153
2241
|
}
|
|
2154
2242
|
if (target === void 0 || !strict.is(target)) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
|
|
2155
|
-
const tag =
|
|
2243
|
+
const tag = tagWorkflow(target.id);
|
|
2156
2244
|
if (lineage.includes(tag)) throw new ToolboxError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
|
|
2157
2245
|
workflow: target.id,
|
|
2158
2246
|
lineage: [...lineage]
|
|
@@ -2170,7 +2258,7 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2170
2258
|
...agents === void 0 ? {} : { agents },
|
|
2171
2259
|
...store === void 0 ? {} : { store }
|
|
2172
2260
|
});
|
|
2173
|
-
return
|
|
2261
|
+
return summarizeWorkflow(await runner.execute(target, {
|
|
2174
2262
|
...registry === void 0 ? {} : { functions: registry },
|
|
2175
2263
|
...store === void 0 ? {} : { store }
|
|
2176
2264
|
}));
|
|
@@ -2178,15 +2266,15 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2178
2266
|
});
|
|
2179
2267
|
}
|
|
2180
2268
|
/**
|
|
2181
|
-
*
|
|
2182
|
-
*
|
|
2269
|
+
* Builds an LLM-callable workspace-editing tool — it ADVERTISES the `operation`-discriminated
|
|
2270
|
+
* union ({@link import('./shapers.js').workspaceToolShape}) as its `parameters`, and its
|
|
2183
2271
|
* handler PARSES the model-supplied args against that contract and DISPATCHES the matched
|
|
2184
2272
|
* operation against the manager's ACTIVE workspace (the registry ops drive the manager itself),
|
|
2185
2273
|
* returning the plain result. A malformed operation throws this package's `ToolboxError`; a
|
|
2186
2274
|
* genuine workspace-domain failure propagates `@orkestrel/workspace`'s typed `WorkspaceError`.
|
|
2187
2275
|
* EITHER drives a caller-supplied {@link WorkspaceToolOptions.manager} directly, OR
|
|
2188
2276
|
* constructs a fresh `WorkspaceManagerInterface` (`@orkestrel/workspace`) over
|
|
2189
|
-
* {@link import('./types.js').WorkspaceToolOptions.store} (
|
|
2277
|
+
* {@link import('./types.js').WorkspaceToolOptions.store} (through `@orkestrel/workspace`'s
|
|
2190
2278
|
* `createWorkspaceManager`); neither given constructs a manager backed by
|
|
2191
2279
|
* `@orkestrel/workspace`'s in-memory store default.
|
|
2192
2280
|
*
|
|
@@ -2201,15 +2289,15 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2201
2289
|
*
|
|
2202
2290
|
* NO-ACTIVE RULE (the ergonomic seam): a WRITING op (write / splice / prepend / append / move /
|
|
2203
2291
|
* remove / replace) run when `manager.active` is `undefined` AUTO-CREATES + activates a default
|
|
2204
|
-
* workspace (`manager.add()`) so the model can
|
|
2292
|
+
* workspace (`manager.add()`) so the model can start writing; a pure-READ op (read / list /
|
|
2205
2293
|
* has / search) against no active workspace returns the EMPTY result (`undefined` / `[]` /
|
|
2206
2294
|
* `false`), never creating one and never throwing.
|
|
2207
2295
|
*
|
|
2208
|
-
* The handler conforms to the universal tool-handler contract
|
|
2296
|
+
* The handler conforms to the universal tool-handler contract: it `contract.parse`s
|
|
2209
2297
|
* the args, THROWS a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
|
|
2210
2298
|
* operation), else `switch`es on `op.operation` and RETURNS the plain result — letting a
|
|
2211
|
-
* `WorkspaceError` raised by the live workspace (`MODALITY` / `PATTERN` / `RANGE`)
|
|
2212
|
-
* uncaught. The range edit is the FLAT `'splice'` op: its four flat caret integers are
|
|
2299
|
+
* `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` / `RANGE`)
|
|
2300
|
+
* PROPAGATE uncaught. The range edit is the FLAT `'splice'` op: its four flat caret integers are
|
|
2213
2301
|
* reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
|
|
2214
2302
|
* ranged `write`.
|
|
2215
2303
|
*
|
|
@@ -2331,14 +2419,14 @@ function createWorkspaceTool(options) {
|
|
|
2331
2419
|
});
|
|
2332
2420
|
}
|
|
2333
2421
|
/**
|
|
2334
|
-
*
|
|
2422
|
+
* Builds an LLM-callable sub-agent delegation tool — resolves a live, seeded `AgentInterface`
|
|
2335
2423
|
* from `registry` and runs it to completion for ONE delegated `task`.
|
|
2336
2424
|
*
|
|
2337
2425
|
* @remarks
|
|
2338
|
-
* The universal tool-handler contract
|
|
2426
|
+
* The universal tool-handler contract: validates the call args against
|
|
2339
2427
|
* {@link import('./shapers.js').agentToolShape}, assembles an `AgentJobInput` (`task` seeds the
|
|
2340
2428
|
* sub-agent's conversation as a single `user` message; `provider` / `tools` / `system` fall
|
|
2341
|
-
* back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent
|
|
2429
|
+
* back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent through
|
|
2342
2430
|
* `registry.build`, runs it with `agent.generate()`, and returns the settled
|
|
2343
2431
|
* `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, THROWS a typed `TOOL`
|
|
2344
2432
|
* {@link import('./errors.js').ToolboxError}; a delegation that would exceed
|
|
@@ -2389,7 +2477,7 @@ function createAgentTool(registry, options) {
|
|
|
2389
2477
|
depth,
|
|
2390
2478
|
max: 8
|
|
2391
2479
|
});
|
|
2392
|
-
const tag =
|
|
2480
|
+
const tag = tagAgent(provider);
|
|
2393
2481
|
if (ancestry.includes(tag)) throw new ToolboxError("DEPTH", `agent '${provider}' is already an ancestor (cycle)`, {
|
|
2394
2482
|
provider,
|
|
2395
2483
|
ancestry: [...ancestry]
|
|
@@ -2415,7 +2503,7 @@ function createAgentTool(registry, options) {
|
|
|
2415
2503
|
});
|
|
2416
2504
|
}
|
|
2417
2505
|
/**
|
|
2418
|
-
*
|
|
2506
|
+
* Builds an LLM-callable tool that returns the FULL `description` of another registered tool by
|
|
2419
2507
|
* name — the counterpart to the lean `summary` the other tools in this package advertise
|
|
2420
2508
|
* (`AGENT_TOOL_SUMMARY` / `WORKFLOW_TOOL_SUMMARY` / `WORKSPACE_TOOL_SUMMARY`).
|
|
2421
2509
|
*
|
|
@@ -2424,10 +2512,10 @@ function createAgentTool(registry, options) {
|
|
|
2424
2512
|
* tool.description` — a lean one-sentence summary stands in for a tool's full teaching
|
|
2425
2513
|
* description when `summary` is set, keeping the advertised tool list compact for a small model.
|
|
2426
2514
|
* This tool is the on-demand expansion seam: given a registered tool's `name`, it looks the tool
|
|
2427
|
-
* up
|
|
2515
|
+
* up through `tools.tool(name)` and returns its full `description` (falling back to `summary` when a
|
|
2428
2516
|
* tool has no `description` of its own, then a placeholder when it has neither).
|
|
2429
2517
|
*
|
|
2430
|
-
* The universal tool-handler contract
|
|
2518
|
+
* The universal tool-handler contract: validates the call args against
|
|
2431
2519
|
* {@link import('./shapers.js').describeToolShape}, RETURNS the plain description string on
|
|
2432
2520
|
* success, THROWS a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
|
|
2433
2521
|
* or an unknown tool name.
|
|
@@ -2466,11 +2554,11 @@ function createDescribeTool(tools) {
|
|
|
2466
2554
|
});
|
|
2467
2555
|
}
|
|
2468
2556
|
/**
|
|
2469
|
-
*
|
|
2557
|
+
* Builds an LLM-callable form tool — the ASK side of the terminal seam. Asks
|
|
2470
2558
|
* a multi-field form and BLOCKS until it answers, returning the resolved values record.
|
|
2471
2559
|
*
|
|
2472
2560
|
* @remarks
|
|
2473
|
-
* The universal tool-handler contract
|
|
2561
|
+
* The universal tool-handler contract: validates the call args against
|
|
2474
2562
|
* {@link import('./shapers.js').promptToolShape}, parses the call's schema through
|
|
2475
2563
|
* `@orkestrel/form`, constructs the live form, and passes it to `TerminalManagerInterface.ask`.
|
|
2476
2564
|
* `from` is FIXED at construction
|
|
@@ -2479,7 +2567,7 @@ function createDescribeTool(tools) {
|
|
|
2479
2567
|
* `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
|
|
2480
2568
|
* {@link import('./errors.js').ToolboxError}; an expired form re-surfaces as `EXPIRE`; an
|
|
2481
2569
|
* unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
|
|
2482
|
-
*
|
|
2570
|
+
* and how many brokers the manager holds (`manager.count`).
|
|
2483
2571
|
*
|
|
2484
2572
|
* @param options - The live manager, the fixed `from` identity, and advertised overrides (see
|
|
2485
2573
|
* {@link import('./types.js').PromptToolOptions})
|
|
@@ -2520,7 +2608,7 @@ function createPromptTool(options) {
|
|
|
2520
2608
|
return await options.manager.ask(options.from, call.to, (0, _orkestrel_form.createForm)(schema));
|
|
2521
2609
|
} catch (error) {
|
|
2522
2610
|
if ((0, _orkestrel_form.isFormError)(error) && error.code === "ABANDONED") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
|
|
2523
|
-
const code =
|
|
2611
|
+
const code = inferTerminalCode(error);
|
|
2524
2612
|
if (code === void 0) throw error;
|
|
2525
2613
|
if (code === "DEADLOCK") throw new ToolboxError("DEADLOCK", `asking '${call.to}' would create a form cycle`, (0, _orkestrel_terminal.isTerminalError)(error) ? error.context : {
|
|
2526
2614
|
from: options.from,
|
|
@@ -2529,7 +2617,7 @@ function createPromptTool(options) {
|
|
|
2529
2617
|
if (code === "EXPIRE") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
|
|
2530
2618
|
if ((0, _orkestrel_terminal.isTerminalError)(error) && error.code === "TARGET") throw new ToolboxError("TOOL", `unknown terminal '${call.to}'`, {
|
|
2531
2619
|
to: call.to,
|
|
2532
|
-
|
|
2620
|
+
count: options.manager.count
|
|
2533
2621
|
});
|
|
2534
2622
|
throw new ToolboxError("TOOL", `asking '${call.to}' failed`, { to: call.to });
|
|
2535
2623
|
}
|
|
@@ -2537,16 +2625,16 @@ function createPromptTool(options) {
|
|
|
2537
2625
|
});
|
|
2538
2626
|
}
|
|
2539
2627
|
/**
|
|
2540
|
-
*
|
|
2541
|
-
*
|
|
2628
|
+
* Builds an LLM-callable answer tool — the ANSWER side of the terminal seam. Lists the forms
|
|
2629
|
+
* addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of
|
|
2542
2630
|
* them by id.
|
|
2543
2631
|
*
|
|
2544
2632
|
* @remarks
|
|
2545
|
-
* The universal tool-handler contract
|
|
2633
|
+
* The universal tool-handler contract: validates the call args against
|
|
2546
2634
|
* {@link import('./shapers.js').answerToolShape} (discriminated by `operation`). `'pending'`
|
|
2547
|
-
* returns a compact list (`{ id, from, schema }`) of every form
|
|
2635
|
+
* returns a compact list (`{ id, from, schema }`) of every form addressed to `to`.
|
|
2548
2636
|
* `'answer'` looks the form up by `id`, narrows the supplied `values` through `@orkestrel/form`,
|
|
2549
|
-
* and applies it
|
|
2637
|
+
* and applies it through
|
|
2550
2638
|
* `TerminalManagerInterface.answer` — a rejected / unknown / unresolvable outcome
|
|
2551
2639
|
* (`TerminalAnswerResult.error`) re-surfaces as a typed `ANSWER` `ToolboxError`; success returns
|
|
2552
2640
|
* `{ answered: id }`. `to` is FIXED at construction
|
|
@@ -2604,8 +2692,8 @@ function createAnswerTool(options) {
|
|
|
2604
2692
|
});
|
|
2605
2693
|
}
|
|
2606
2694
|
/**
|
|
2607
|
-
*
|
|
2608
|
-
* definitions, the DEFAULT store the
|
|
2695
|
+
* Creates the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
|
|
2696
|
+
* definitions, the DEFAULT store the database and relation tools persist their
|
|
2609
2697
|
* `DatabaseDefinition` configs through.
|
|
2610
2698
|
*
|
|
2611
2699
|
* @returns A {@link DefinitionStoreInterface}
|
|
@@ -2621,7 +2709,7 @@ function createMemoryDefinitionStore() {
|
|
|
2621
2709
|
return new MemoryDefinitionStore();
|
|
2622
2710
|
}
|
|
2623
2711
|
/**
|
|
2624
|
-
*
|
|
2712
|
+
* Creates a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database`
|
|
2625
2713
|
* layer — the driver-pluggable twin of {@link createMemoryDefinitionStore}, storing each
|
|
2626
2714
|
* database's definition as one opaque JSON column.
|
|
2627
2715
|
*
|
|
@@ -2646,15 +2734,15 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2646
2734
|
}).table("definitions"));
|
|
2647
2735
|
}
|
|
2648
2736
|
/**
|
|
2649
|
-
*
|
|
2650
|
-
* databases through one `operation`-discriminated call (
|
|
2737
|
+
* Builds an LLM-callable database tool — it creates, queries, and mutates `@orkestrel/database`
|
|
2738
|
+
* databases through one `operation`-discriminated call (matching
|
|
2651
2739
|
* {@link createWorkspaceTool}'s single-tool-many-operations shape).
|
|
2652
2740
|
*
|
|
2653
2741
|
* @remarks
|
|
2654
|
-
* The universal tool-handler contract
|
|
2742
|
+
* The universal tool-handler contract: validates the call args against
|
|
2655
2743
|
* {@link import('./shapers.js').databaseToolShape}, dispatches to the matching operation, and
|
|
2656
2744
|
* RETURNS a plain result on success. A database is resolved lazily and cached for the tool's
|
|
2657
|
-
* lifetime — `'create'` mints one from `tables` ({@link import('./
|
|
2745
|
+
* lifetime — `'create'` mints one from `tables` ({@link import('./compilers.js').expandTables}) and
|
|
2658
2746
|
* a registered `driver` key ({@link import('./types.js').DatabaseToolOptions.drivers}, default
|
|
2659
2747
|
* `{ memory: () => createMemoryDriver() }`); any other operation addressing an uncached id falls
|
|
2660
2748
|
* back to {@link import('./types.js').DatabaseToolOptions.store} (an unknown id throws a typed
|
|
@@ -2663,10 +2751,10 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2663
2751
|
*
|
|
2664
2752
|
* `'records'` clamps its `query` to
|
|
2665
2753
|
* {@link import('./types.js').DatabaseToolOptions.limit} (default
|
|
2666
|
-
* {@link import('./constants.js').DATABASE_TOOL_LIMIT})
|
|
2754
|
+
* {@link import('./constants.js').DATABASE_TOOL_LIMIT}) through
|
|
2667
2755
|
* {@link import('./helpers.js').clampQuery}, reporting `truncated` when storage held more rows
|
|
2668
|
-
* than the cap. Every operation's `query` is normalized
|
|
2669
|
-
* {@link import('./helpers.js').
|
|
2756
|
+
* than the cap. Every operation's `query` is normalized through
|
|
2757
|
+
* {@link import('./helpers.js').normalizeQuery} (defaults an omitted condition `connector` to `'and'`).
|
|
2670
2758
|
* When {@link import('./types.js').DatabaseToolOptions.readonly} is `true`, every mutating
|
|
2671
2759
|
* operation throws a typed `TOOL` `ToolboxError` before doing anything. A configured
|
|
2672
2760
|
* {@link import('./types.js').DatabaseToolOptions.timeout} is passed as a fresh
|
|
@@ -2675,7 +2763,7 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2675
2763
|
* not an outer deadline for resolution, construction, schema inspection, `get`, or `close`. A typed `@orkestrel/database`
|
|
2676
2764
|
* failure (`DatabaseError`) re-surfaces as a typed `DATABASE` `ToolboxError` carrying the
|
|
2677
2765
|
* original {@link import('@orkestrel/database').DatabaseErrorCode} in `context.code`
|
|
2678
|
-
* ({@link import('./helpers.js').
|
|
2766
|
+
* ({@link import('./helpers.js').inferDatabaseCode}); an `ToolboxError` thrown by this tool's own
|
|
2679
2767
|
* guards passes through unwrapped.
|
|
2680
2768
|
*
|
|
2681
2769
|
* A lazily re-minted database over the DEFAULT in-memory driver yields an EMPTY database — only
|
|
@@ -2783,7 +2871,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2783
2871
|
}
|
|
2784
2872
|
case "records": {
|
|
2785
2873
|
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2786
|
-
const { query: probe, limit } = clampQuery(
|
|
2874
|
+
const { query: probe, limit } = clampQuery(normalizeQuery(call.query), cap);
|
|
2787
2875
|
const rows = await table.records(probe, read);
|
|
2788
2876
|
const truncated = rows.length > limit;
|
|
2789
2877
|
const sliced = rows.slice(0, limit);
|
|
@@ -2794,8 +2882,8 @@ function createDatabaseTool(options = {}) {
|
|
|
2794
2882
|
limit
|
|
2795
2883
|
};
|
|
2796
2884
|
}
|
|
2797
|
-
case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(
|
|
2798
|
-
case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column,
|
|
2885
|
+
case "count": return { count: await (await resolver.resolve(call.id)).table(call.table).count(normalizeQuery(call.query), read) };
|
|
2886
|
+
case "aggregate": return { value: await (await resolver.resolve(call.id)).table(call.table).aggregate(call.function, call.column, normalizeQuery(call.query), read) };
|
|
2799
2887
|
case "add": {
|
|
2800
2888
|
const table = (await resolver.resolve(call.id)).table(call.table);
|
|
2801
2889
|
const many = Array.isArray(call.row);
|
|
@@ -2841,7 +2929,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2841
2929
|
}
|
|
2842
2930
|
} catch (error) {
|
|
2843
2931
|
if (isToolboxError(error)) throw error;
|
|
2844
|
-
const code =
|
|
2932
|
+
const code = inferDatabaseCode(error);
|
|
2845
2933
|
if (code === void 0) throw error;
|
|
2846
2934
|
throw new ToolboxError("DATABASE", error instanceof Error ? error.message : String(error), {
|
|
2847
2935
|
code,
|
|
@@ -2854,32 +2942,33 @@ function createDatabaseTool(options = {}) {
|
|
|
2854
2942
|
});
|
|
2855
2943
|
}
|
|
2856
2944
|
/**
|
|
2857
|
-
*
|
|
2858
|
-
* through one `operation`-discriminated call (
|
|
2945
|
+
* Builds an LLM-callable relation tool — it traverses and edits `@orkestrel/relation` relationships
|
|
2946
|
+
* through one `operation`-discriminated call (matching {@link createDatabaseTool}'s
|
|
2859
2947
|
* single-tool-many-operations shape).
|
|
2860
2948
|
*
|
|
2861
2949
|
* @remarks
|
|
2862
|
-
* The universal tool-handler contract
|
|
2950
|
+
* The universal tool-handler contract: validates the call args against
|
|
2863
2951
|
* {@link import('./shapers.js').relationToolShape}, resolves the addressed
|
|
2864
2952
|
* {@link import('@orkestrel/relation').RelationManagerInterface} — an explicit `manager` field
|
|
2865
2953
|
* must match a key of {@link import('./types.js').RelationToolOptions.managers}, an OMITTED one
|
|
2866
2954
|
* resolves to the SOLE registered manager, either miss throwing a typed `TOOL`
|
|
2867
2955
|
* {@link import('./errors.js').ToolboxError}
|
|
2868
|
-
* ({@link import('./helpers.js').
|
|
2869
|
-
* ({@link import('./helpers.js').
|
|
2956
|
+
* ({@link import('./helpers.js').resolveRelationManager}) — then resolves `model` against it
|
|
2957
|
+
* ({@link import('./helpers.js').resolveRelationModel}, same typed-`TOOL`-on-miss shape), and
|
|
2870
2958
|
* dispatches to the matched operation, RETURNING a plain result on success.
|
|
2871
2959
|
*
|
|
2872
2960
|
* `'load'` / `'find'` expand the call's FLAT dot-path `include` list into a live
|
|
2873
|
-
* `@orkestrel/relation` `Include` tree
|
|
2961
|
+
* `@orkestrel/relation` `Include` tree through {@link import('./helpers.js').expandInclude}, capped
|
|
2874
2962
|
* at {@link import('./types.js').RelationToolOptions.depth} (default
|
|
2875
2963
|
* {@link import('./constants.js').RELATION_TOOL_DEPTH}) — a path exceeding the cap, or carrying an
|
|
2876
2964
|
* empty segment, throws a typed `TOOL` error. `'load'` dispatches on whether `key` is an array
|
|
2877
|
-
* (positional many-key form
|
|
2965
|
+
* (positional many-key form — an array key is read as many keys rather than one) or a single key.
|
|
2966
|
+
* `'find'` and `'links'` clamp their
|
|
2878
2967
|
* result to {@link import('./types.js').RelationToolOptions.limit} (default
|
|
2879
|
-
* {@link import('./constants.js').RELATION_TOOL_LIMIT})
|
|
2880
|
-
*
|
|
2881
|
-
* `truncated`; `'links'` (which has no upstream pagination) fetches the FULL linked-key
|
|
2882
|
-
* slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
|
|
2968
|
+
* {@link import('./constants.js').RELATION_TOOL_LIMIT}), taken through
|
|
2969
|
+
* {@link import('./helpers.js').resolveLimit} — `'find'` probes one row past the effective limit to
|
|
2970
|
+
* report `truncated`; `'links'` (which has no upstream pagination) fetches the FULL linked-key
|
|
2971
|
+
* list and slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
|
|
2883
2972
|
* row.
|
|
2884
2973
|
*
|
|
2885
2974
|
* A typed `@orkestrel/relation` failure (`RelationError`) re-surfaces as a typed `RELATION`
|
|
@@ -2914,7 +3003,7 @@ function createRelationTool(options) {
|
|
|
2914
3003
|
const call = contract.parse(args);
|
|
2915
3004
|
if (call === void 0) throw new ToolboxError("TOOL", "malformed relation call", { args });
|
|
2916
3005
|
try {
|
|
2917
|
-
const model =
|
|
3006
|
+
const model = resolveRelationModel(resolveRelationManager(options.managers, call.manager), call.model);
|
|
2918
3007
|
switch (call.operation) {
|
|
2919
3008
|
case "load": {
|
|
2920
3009
|
const include = expandInclude(call.include, depth);
|
|
@@ -2923,7 +3012,7 @@ function createRelationTool(options) {
|
|
|
2923
3012
|
}
|
|
2924
3013
|
case "find": {
|
|
2925
3014
|
const include = expandInclude(call.include, depth);
|
|
2926
|
-
const effective =
|
|
3015
|
+
const effective = resolveLimit(call.limit, cap);
|
|
2927
3016
|
const rows = await model.find(include, {
|
|
2928
3017
|
limit: effective + 1,
|
|
2929
3018
|
...call.offset === void 0 ? {} : { offset: call.offset },
|
|
@@ -2946,27 +3035,28 @@ function createRelationTool(options) {
|
|
|
2946
3035
|
await model.unlink(call.key, call.relation, call.target);
|
|
2947
3036
|
return { unlinked: true };
|
|
2948
3037
|
case "links": {
|
|
3038
|
+
const effective = resolveLimit(void 0, cap);
|
|
2949
3039
|
const keys = await model.links(call.key, call.relation);
|
|
2950
|
-
const truncated = keys.length >
|
|
2951
|
-
const sliced = keys.slice(0,
|
|
3040
|
+
const truncated = keys.length > effective;
|
|
3041
|
+
const sliced = keys.slice(0, effective);
|
|
2952
3042
|
return {
|
|
2953
3043
|
keys: sliced,
|
|
2954
3044
|
count: sliced.length,
|
|
2955
3045
|
truncated,
|
|
2956
|
-
limit:
|
|
3046
|
+
limit: effective
|
|
2957
3047
|
};
|
|
2958
3048
|
}
|
|
2959
3049
|
}
|
|
2960
3050
|
} catch (error) {
|
|
2961
3051
|
if (isToolboxError(error)) throw error;
|
|
2962
|
-
const relation =
|
|
3052
|
+
const relation = inferRelationCode(error);
|
|
2963
3053
|
if (relation !== void 0) throw new ToolboxError("RELATION", error instanceof Error ? error.message : String(error), {
|
|
2964
3054
|
code: relation,
|
|
2965
3055
|
operation: call.operation,
|
|
2966
3056
|
model: call.model,
|
|
2967
3057
|
..."relation" in call ? { relation: call.relation } : {}
|
|
2968
3058
|
});
|
|
2969
|
-
const database =
|
|
3059
|
+
const database = inferDatabaseCode(error);
|
|
2970
3060
|
if (database === void 0) throw error;
|
|
2971
3061
|
throw new ToolboxError("DATABASE", error instanceof Error ? error.message : String(error), {
|
|
2972
3062
|
code: database,
|
|
@@ -2977,15 +3067,15 @@ function createRelationTool(options) {
|
|
|
2977
3067
|
});
|
|
2978
3068
|
}
|
|
2979
3069
|
/**
|
|
2980
|
-
*
|
|
3070
|
+
* Builds a standalone LLM-callable tool that infers a JSON Schema from example values — the
|
|
2981
3071
|
* utility half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
2982
3072
|
* {@link createEndpointTool}, wraps one CONCRETE endpoint).
|
|
2983
3073
|
*
|
|
2984
3074
|
* @remarks
|
|
2985
|
-
* The universal tool-handler contract
|
|
3075
|
+
* The universal tool-handler contract: validates the call args against
|
|
2986
3076
|
* {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
|
|
2987
|
-
* booleans, `candidates` an optional array), infers a schema
|
|
2988
|
-
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }`
|
|
3077
|
+
* booleans, `candidates` an optional array), infers a schema through `@orkestrel/contract`'s
|
|
3078
|
+
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` through `schemaToObject` (mirrors
|
|
2989
3079
|
* the tool-parameters convention every other `create*Tool` factory advertises), and RETURNS the
|
|
2990
3080
|
* resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
|
|
2991
3081
|
* `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
|
|
@@ -2993,7 +3083,7 @@ function createRelationTool(options) {
|
|
|
2993
3083
|
*
|
|
2994
3084
|
* When `candidates` is ABSENT, the return is the bare parameters record — unchanged from before
|
|
2995
3085
|
* this array existed. When `candidates` is PRESENT (any array, including empty), the handler
|
|
2996
|
-
* compiles a SEPARATE per-call contract from the RAW inferred schema (
|
|
3086
|
+
* compiles a SEPARATE per-call contract from the RAW inferred schema (through `@orkestrel/contract`'s
|
|
2997
3087
|
* `schemaToShape`, NOT the `schemaToObject`-wrapped parameters — a bare-value sample checks a
|
|
2998
3088
|
* bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
|
|
2999
3089
|
* index. Every entry has a UNIFORM shape — `{ index, valid, coercible }`, with `faults` added ONLY
|
|
@@ -3001,10 +3091,10 @@ function createRelationTool(options) {
|
|
|
3001
3091
|
* OPPOSITE of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
|
|
3002
3092
|
* string slot) — here a conformance report answers "does this value conform AS-IS": `7` against a
|
|
3003
3093
|
* string slot is `valid: false`, full stop. `coercible` answers a SEPARATE question — "would the
|
|
3004
|
-
* NORMALIZING parse accept this value",
|
|
3094
|
+
* NORMALIZING parse accept this value", that is would {@link createEndpointTool}'s default enforcement
|
|
3005
3095
|
* admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
|
|
3006
|
-
* `valid`; by the house parse/guard round-trip
|
|
3007
|
-
* ALWAYS also `coercible: true`. `@orkestrel/contract`
|
|
3096
|
+
* `valid`; by the house parse/guard round-trip, a `valid: true` entry is
|
|
3097
|
+
* ALWAYS also `coercible: true`. `@orkestrel/contract`'s `explain` mirrors the normalizing
|
|
3008
3098
|
* `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
|
|
3009
3099
|
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: EMPTY faults, since
|
|
3010
3100
|
* the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
|
|
@@ -3012,7 +3102,7 @@ function createRelationTool(options) {
|
|
|
3012
3102
|
* (a boolean in a string slot), a missing required key, or an out-of-enum value — where
|
|
3013
3103
|
* `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
|
|
3014
3104
|
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches all three
|
|
3015
|
-
* and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (
|
|
3105
|
+
* and yields a bounded, non-throwing per-candidate verdict; a NON-JSON-safe candidate (for example a
|
|
3016
3106
|
* throwing-getter `Proxy`) never reaches the checker at all — it fails the OUTER `args` parse
|
|
3017
3107
|
* against {@link import('./shapers.js').inferToolShape} and rejects the WHOLE call with the same
|
|
3018
3108
|
* `TOOL` {@link import('./errors.js').ToolboxError} a malformed `samples`/`format`/`enum` throws,
|
|
@@ -3093,32 +3183,32 @@ function createInferTool(options) {
|
|
|
3093
3183
|
});
|
|
3094
3184
|
}
|
|
3095
3185
|
/**
|
|
3096
|
-
*
|
|
3186
|
+
* Wraps one CONCRETE endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
|
|
3097
3187
|
* `ToolInterface` — the endpoint half of the "existing API/DB → MCP tool" bridge (the other half,
|
|
3098
3188
|
* {@link createInferTool}, is a standalone inference utility).
|
|
3099
3189
|
*
|
|
3100
3190
|
* @remarks
|
|
3101
|
-
* `parameters` is inferred ONCE at construction from `definition.samples`
|
|
3191
|
+
* `parameters` is inferred ONCE at construction from `definition.samples` through
|
|
3102
3192
|
* `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
|
|
3103
|
-
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }`
|
|
3193
|
+
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` through `schemaToObject` —
|
|
3104
3194
|
* the SAME object-rooted schema is both the ADVERTISED `parameters` and, by default
|
|
3105
3195
|
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the ENFORCED contract:
|
|
3106
|
-
* `@orkestrel/contract`
|
|
3107
|
-
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.
|
|
3196
|
+
* `@orkestrel/contract`'s `schemaToShape` compiles it ONCE (through `createContract`) into a
|
|
3197
|
+
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.execute` — a
|
|
3108
3198
|
* NORMALIZING parse, not a strict type check: a scalar is COERCED to its inferred type where the
|
|
3109
3199
|
* house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
|
|
3110
|
-
* `'false'`/`1`/`0`), so `definition.
|
|
3200
|
+
* `'false'`/`1`/`0`), so `definition.execute` receives the COERCED value (for example `7` sent for a
|
|
3111
3201
|
* string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
|
|
3112
3202
|
* a record — a required key missing, or a value not coercible to its slot's type — THROWS a
|
|
3113
3203
|
* typed `TOOL` {@link import('./errors.js').ToolboxError} carrying the compiled contract's
|
|
3114
|
-
* structured `explain` faults, and `definition.
|
|
3204
|
+
* structured `explain` faults, and `definition.execute` is never called. `format` annotations are
|
|
3115
3205
|
* NEVER asserted, and a key outside the closed inferred schema is SILENTLY DROPPED rather than
|
|
3116
3206
|
* rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
|
|
3117
|
-
* `validate: false`, `execute` PASSES THROUGH the model-supplied `args` to
|
|
3118
|
-
* WITHOUT re-validation — the
|
|
3119
|
-
*
|
|
3207
|
+
* `validate: false`, the tool's `execute` PASSES THROUGH the model-supplied `args` to
|
|
3208
|
+
* `definition.execute` WITHOUT re-validation — the raw-passthrough opt-out. Either way, the
|
|
3209
|
+
* definition's return flows back as the tool call's plain result; a throw PROPAGATES uncaught,
|
|
3120
3210
|
* isolated by the `ToolManagerInterface` (`@orkestrel/tool`) into the canonical error envelope
|
|
3121
|
-
*
|
|
3211
|
+
* — never caught or re-wrapped here.
|
|
3122
3212
|
*
|
|
3123
3213
|
* @param definition - The endpoint's identity, non-empty samples, and local handler (see
|
|
3124
3214
|
* {@link import('./types.js').EndpointDefinition})
|
|
@@ -3135,12 +3225,12 @@ function createInferTool(options) {
|
|
|
3135
3225
|
* name: 'lookupUser',
|
|
3136
3226
|
* description: 'Look up a user by id.',
|
|
3137
3227
|
* samples: [{ id: '1', name: 'Ada' }, { id: '2', name: 'Bob' }],
|
|
3138
|
-
*
|
|
3228
|
+
* execute: (args) => ({ id: args.id, name: 'Ada' }),
|
|
3139
3229
|
* })
|
|
3140
3230
|
* const tools = createToolManager()
|
|
3141
3231
|
* tools.add(tool)
|
|
3142
3232
|
*
|
|
3143
|
-
* // conforming args (all required keys present) parse and reach `
|
|
3233
|
+
* // conforming args (all required keys present) parse and reach the definition's `execute`
|
|
3144
3234
|
* const result = await tools.execute({
|
|
3145
3235
|
* id: 'call-1',
|
|
3146
3236
|
* name: 'lookupUser',
|
|
@@ -3149,7 +3239,7 @@ function createInferTool(options) {
|
|
|
3149
3239
|
* // result.value -> { id: '1', name: 'Ada' }
|
|
3150
3240
|
*
|
|
3151
3241
|
* // a nonconforming call (id is not coercible to the required string) is rejected before
|
|
3152
|
-
* // `
|
|
3242
|
+
* // the definition's `execute` runs
|
|
3153
3243
|
* const rejected = await tools.execute({
|
|
3154
3244
|
* id: 'call-2',
|
|
3155
3245
|
* name: 'lookupUser',
|
|
@@ -3170,7 +3260,7 @@ function createEndpointTool(definition, options) {
|
|
|
3170
3260
|
description: definition.description,
|
|
3171
3261
|
...parameters === void 0 ? {} : { parameters },
|
|
3172
3262
|
execute(args) {
|
|
3173
|
-
return definition.
|
|
3263
|
+
return definition.execute(args);
|
|
3174
3264
|
}
|
|
3175
3265
|
});
|
|
3176
3266
|
const contract = (0, _orkestrel_contract.createContract)((0, _orkestrel_contract.schemaToShape)(objectSchema));
|
|
@@ -3184,7 +3274,7 @@ function createEndpointTool(definition, options) {
|
|
|
3184
3274
|
name: definition.name,
|
|
3185
3275
|
faults: contract.explain(args)
|
|
3186
3276
|
});
|
|
3187
|
-
return definition.
|
|
3277
|
+
return definition.execute(parsed);
|
|
3188
3278
|
}
|
|
3189
3279
|
});
|
|
3190
3280
|
}
|
|
@@ -3209,7 +3299,7 @@ exports.DatabaseResolver = DatabaseResolver;
|
|
|
3209
3299
|
exports.INFER_TOOL_DESCRIPTION = INFER_TOOL_DESCRIPTION;
|
|
3210
3300
|
exports.INFER_TOOL_NAME = INFER_TOOL_NAME;
|
|
3211
3301
|
exports.INFER_TOOL_SUMMARY = INFER_TOOL_SUMMARY;
|
|
3212
|
-
exports.
|
|
3302
|
+
exports.MAX_WORKFLOW_CHAIN = MAX_WORKFLOW_CHAIN;
|
|
3213
3303
|
exports.MemoryDefinitionStore = MemoryDefinitionStore;
|
|
3214
3304
|
exports.PROMPT_TOOL_DESCRIPTION = PROMPT_TOOL_DESCRIPTION;
|
|
3215
3305
|
exports.PROMPT_TOOL_NAME = PROMPT_TOOL_NAME;
|
|
@@ -3229,13 +3319,13 @@ exports.WORKSPACE_TOOL_DESCRIPTION = WORKSPACE_TOOL_DESCRIPTION;
|
|
|
3229
3319
|
exports.WORKSPACE_TOOL_EXAMPLE = WORKSPACE_TOOL_EXAMPLE;
|
|
3230
3320
|
exports.WORKSPACE_TOOL_NAME = WORKSPACE_TOOL_NAME;
|
|
3231
3321
|
exports.WORKSPACE_TOOL_SUMMARY = WORKSPACE_TOOL_SUMMARY;
|
|
3232
|
-
exports.agentTag = agentTag;
|
|
3233
3322
|
exports.agentToolShape = agentToolShape;
|
|
3234
3323
|
exports.answerToolShape = answerToolShape;
|
|
3235
3324
|
exports.clampQuery = clampQuery;
|
|
3236
|
-
exports.
|
|
3237
|
-
exports.columnShape = columnShape;
|
|
3325
|
+
exports.columnPrimitiveShape = columnPrimitiveShape;
|
|
3238
3326
|
exports.columnSpecShape = columnSpecShape;
|
|
3327
|
+
exports.compileColumn = compileColumn;
|
|
3328
|
+
exports.compileColumnPrimitive = compileColumnPrimitive;
|
|
3239
3329
|
exports.completeDraft = completeDraft;
|
|
3240
3330
|
exports.completePhaseDraft = completePhaseDraft;
|
|
3241
3331
|
exports.completeTaskDraft = completeTaskDraft;
|
|
@@ -3256,7 +3346,6 @@ exports.createWorkflowDraftContract = createWorkflowDraftContract;
|
|
|
3256
3346
|
exports.createWorkflowFunctions = createWorkflowFunctions;
|
|
3257
3347
|
exports.createWorkflowTool = createWorkflowTool;
|
|
3258
3348
|
exports.createWorkspaceTool = createWorkspaceTool;
|
|
3259
|
-
exports.databaseToolCode = databaseToolCode;
|
|
3260
3349
|
exports.databaseToolShape = databaseToolShape;
|
|
3261
3350
|
exports.deriveWorkflowDepth = deriveWorkflowDepth;
|
|
3262
3351
|
exports.describeToolShape = describeToolShape;
|
|
@@ -3265,38 +3354,39 @@ exports.expandSteps = expandSteps;
|
|
|
3265
3354
|
exports.expandTables = expandTables;
|
|
3266
3355
|
exports.extendLineage = extendLineage;
|
|
3267
3356
|
exports.includeShape = includeShape;
|
|
3357
|
+
exports.inferDatabaseCode = inferDatabaseCode;
|
|
3358
|
+
exports.inferRelationCode = inferRelationCode;
|
|
3359
|
+
exports.inferTerminalCode = inferTerminalCode;
|
|
3268
3360
|
exports.inferToolShape = inferToolShape;
|
|
3269
3361
|
exports.isAgentFunction = isAgentFunction;
|
|
3270
|
-
exports.
|
|
3362
|
+
exports.isColumnPrimitive = isColumnPrimitive;
|
|
3271
3363
|
exports.isColumnSpec = isColumnSpec;
|
|
3272
3364
|
exports.isDatabaseDefinition = isDatabaseDefinition;
|
|
3273
3365
|
exports.isToolboxError = isToolboxError;
|
|
3274
3366
|
exports.isWorkflowLineage = isWorkflowLineage;
|
|
3275
3367
|
exports.keyShape = keyShape;
|
|
3276
|
-
exports.kindShape = kindShape;
|
|
3277
|
-
exports.lineageOf = lineageOf;
|
|
3278
3368
|
exports.managerShape = managerShape;
|
|
3369
|
+
exports.normalizeLineage = normalizeLineage;
|
|
3370
|
+
exports.normalizeQuery = normalizeQuery;
|
|
3279
3371
|
exports.orderShape = orderShape;
|
|
3280
3372
|
exports.phaseDraftShape = phaseDraftShape;
|
|
3281
3373
|
exports.promptToolShape = promptToolShape;
|
|
3282
|
-
exports.queryOf = queryOf;
|
|
3283
3374
|
exports.queryShape = queryShape;
|
|
3284
|
-
exports.relationKeyShape = relationKeyShape;
|
|
3285
|
-
exports.relationManagerOf = relationManagerOf;
|
|
3286
|
-
exports.relationModelOf = relationModelOf;
|
|
3287
|
-
exports.relationToolCode = relationToolCode;
|
|
3288
3375
|
exports.relationToolShape = relationToolShape;
|
|
3376
|
+
exports.resolveLimit = resolveLimit;
|
|
3377
|
+
exports.resolveRelationManager = resolveRelationManager;
|
|
3378
|
+
exports.resolveRelationModel = resolveRelationModel;
|
|
3289
3379
|
exports.rowShape = rowShape;
|
|
3290
3380
|
exports.rowsShape = rowsShape;
|
|
3291
3381
|
exports.singleKeyShape = singleKeyShape;
|
|
3292
3382
|
exports.stepShape = stepShape;
|
|
3383
|
+
exports.summarizeWorkflow = summarizeWorkflow;
|
|
3293
3384
|
exports.tableSpecShape = tableSpecShape;
|
|
3385
|
+
exports.tagAgent = tagAgent;
|
|
3386
|
+
exports.tagWorkflow = tagWorkflow;
|
|
3294
3387
|
exports.taskDraftShape = taskDraftShape;
|
|
3295
|
-
exports.terminalToolCode = terminalToolCode;
|
|
3296
3388
|
exports.workflowDraftShape = workflowDraftShape;
|
|
3297
3389
|
exports.workflowStepsShape = workflowStepsShape;
|
|
3298
|
-
exports.workflowTag = workflowTag;
|
|
3299
|
-
exports.workflowToolSummary = workflowToolSummary;
|
|
3300
3390
|
exports.workspaceToolShape = workspaceToolShape;
|
|
3301
3391
|
|
|
3302
3392
|
//# sourceMappingURL=index.cjs.map
|