@orkestrel/toolbox 0.0.12 → 0.0.14
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 +1 -4
- package/dist/src/core/index.cjs +397 -287
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +844 -714
- package/dist/src/core/index.d.ts +844 -714
- package/dist/src/core/index.js +397 -287
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +8 -5
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +22 -18
- package/dist/src/server/index.d.ts +22 -18
- package/dist/src/server/index.js +8 -5
- package/dist/src/server/index.js.map +1 -1
- package/package.json +22 -23
package/dist/src/core/index.cjs
CHANGED
|
@@ -10,29 +10,33 @@ let _orkestrel_tool = require("@orkestrel/tool");
|
|
|
10
10
|
let _orkestrel_form = require("@orkestrel/form");
|
|
11
11
|
//#region src/core/constants.ts
|
|
12
12
|
/**
|
|
13
|
-
* Holds the name {@link import('./factories.js').createAgentTool} advertises by default
|
|
14
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
13
|
+
* Holds the name {@link import('./factories.js').createAgentTool} advertises by default, `'agent'`
|
|
14
|
+
* — the key a 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
|
-
* reach — the bound {@link import('./factories.js').createAgentTool}'s depth/cycle guard
|
|
19
|
+
* reach, `8` — the bound {@link import('./factories.js').createAgentTool}'s depth/cycle guard
|
|
20
20
|
* enforces.
|
|
21
21
|
*
|
|
22
22
|
* @remarks
|
|
23
|
-
* Deliberately a
|
|
24
|
-
* sharing one reference):
|
|
23
|
+
* Deliberately a separate constant from {@link MAX_WORKFLOW_CHAIN} (rather than the guards
|
|
24
|
+
* sharing one reference): they bound different chains (workflow nesting against 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
|
|
27
27
|
* by shared reference.
|
|
28
28
|
*/
|
|
29
29
|
var AGENT_TOOL_DEPTH = 8;
|
|
30
30
|
/**
|
|
31
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* for the
|
|
35
|
-
*
|
|
31
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
32
|
+
* {@link import('./factories.js').createAgentTool} advertises in place of
|
|
33
|
+
* {@link AGENT_TOOL_DESCRIPTION} — one sentence offering sub-agent delegation and pointing at
|
|
34
|
+
* `describe` for the optional overrides.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
38
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
39
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
36
40
|
*/
|
|
37
41
|
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
42
|
/**
|
|
@@ -52,7 +56,7 @@ var AGENT_TOOL_DESCRIPTION = [
|
|
|
52
56
|
JSON.stringify({ task: "Summarize the attached notes in three bullet points." })
|
|
53
57
|
].join("\n");
|
|
54
58
|
/**
|
|
55
|
-
* Holds the maximum nesting depth a workflow → agent → workflow chain may reach — the bound
|
|
59
|
+
* Holds the maximum nesting depth a workflow → agent → workflow chain may reach, `8` — the bound
|
|
56
60
|
* {@link import('./factories.js').createAgentFunction} and
|
|
57
61
|
* {@link import('./factories.js').createWorkflowTool}'s depth/cycle guards enforce.
|
|
58
62
|
*
|
|
@@ -63,40 +67,40 @@ var AGENT_TOOL_DESCRIPTION = [
|
|
|
63
67
|
*/
|
|
64
68
|
var MAX_WORKFLOW_CHAIN = 8;
|
|
65
69
|
/**
|
|
66
|
-
* Holds the name {@link import('./factories.js').createWorkflowTool} advertises by default
|
|
67
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
68
|
-
* {@link import('./factories.js').createAgentFunction} binds the
|
|
69
|
-
* under onto a wrapped agent's `context.tools`.
|
|
70
|
+
* Holds the name {@link import('./factories.js').createWorkflowTool} advertises by default,
|
|
71
|
+
* `'workflow'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
72
|
+
* under, and the name {@link import('./factories.js').createAgentFunction} binds the
|
|
73
|
+
* depth/cycle-aware workflow tool under onto a wrapped agent's `context.tools`.
|
|
70
74
|
*
|
|
71
75
|
* @remarks
|
|
72
76
|
* The propagation seam's well-known key: when
|
|
73
77
|
* `createAgentFunction`'s `runner` option is supplied, it adds a `createWorkflowTool`-built tool
|
|
74
|
-
* under this name to the agent's `context.tools`, so it can author
|
|
78
|
+
* under this name to the agent's `context.tools`, so it can author and run a nested workflow
|
|
75
79
|
* (bounded by {@link MAX_WORKFLOW_CHAIN}).
|
|
76
80
|
*/
|
|
77
81
|
var WORKFLOW_TOOL_NAME = "workflow";
|
|
78
82
|
/**
|
|
79
|
-
* Holds a complete
|
|
80
|
-
* {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
|
|
83
|
+
* Holds a complete flat authoring example — the primary way a small model authors a workflow
|
|
84
|
+
* through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
|
|
81
85
|
*
|
|
82
86
|
* @remarks
|
|
83
87
|
* Each step becomes a one-task phase, in
|
|
84
|
-
* order; a step's `name` is a
|
|
88
|
+
* order; a step's `name` is a registered behavior name (not a label) — the registry key its
|
|
85
89
|
* task's `behavior` resolves against. The tool expands this
|
|
86
90
|
* ({@link import('./helpers.js').expandSteps}) into a valid `WorkflowDefinition`
|
|
87
|
-
* (`@orkestrel/workflow`). It is embedded
|
|
91
|
+
* (`@orkestrel/workflow`). It is embedded verbatim in {@link WORKFLOW_TOOL_DESCRIPTION}.
|
|
88
92
|
*/
|
|
89
93
|
var WORKFLOW_TOOL_FLAT_EXAMPLE = Object.freeze({
|
|
90
94
|
name: "release",
|
|
91
95
|
steps: Object.freeze([Object.freeze({ name: "compile" }), Object.freeze({ name: "publish" })])
|
|
92
96
|
});
|
|
93
97
|
/**
|
|
94
|
-
* Holds a minimal
|
|
95
|
-
* the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
|
|
98
|
+
* Holds a minimal nested authoring example — the advanced escape-hatch form a model can use instead
|
|
99
|
+
* of the flat shape: a full `WorkflowDefinition` (`@orkestrel/workflow`).
|
|
96
100
|
*
|
|
97
101
|
* @remarks
|
|
98
|
-
* The full
|
|
99
|
-
* {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced alternative. It is embedded
|
|
102
|
+
* The full nested form, documented in
|
|
103
|
+
* {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced alternative. It is embedded verbatim.
|
|
100
104
|
*/
|
|
101
105
|
var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
|
|
102
106
|
id: "release",
|
|
@@ -112,11 +116,15 @@ var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
|
|
|
112
116
|
})])
|
|
113
117
|
});
|
|
114
118
|
/**
|
|
115
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* for the
|
|
119
|
-
*
|
|
119
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
120
|
+
* {@link import('./factories.js').createWorkflowTool} advertises in place of
|
|
121
|
+
* {@link WORKFLOW_TOOL_DESCRIPTION} — one sentence offering multi-phase authoring and pointing at
|
|
122
|
+
* `describe` for the authoring schema and its examples.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
126
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
127
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
120
128
|
*/
|
|
121
129
|
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
130
|
/**
|
|
@@ -139,13 +147,14 @@ var WORKFLOW_TOOL_DESCRIPTION = [
|
|
|
139
147
|
"In the nested form you may omit any \"id\"/\"name\" and they are filled in positionally; a provided one is kept."
|
|
140
148
|
].join("\n");
|
|
141
149
|
/**
|
|
142
|
-
* Holds the name {@link import('./factories.js').createWorkspaceTool} advertises by default
|
|
143
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`)
|
|
150
|
+
* Holds the name {@link import('./factories.js').createWorkspaceTool} advertises by default,
|
|
151
|
+
* `'workspace'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`)
|
|
152
|
+
* registers under.
|
|
144
153
|
*/
|
|
145
154
|
var WORKSPACE_TOOL_NAME = "workspace";
|
|
146
155
|
/**
|
|
147
|
-
* Holds a valid {@link import('./types.js').WorkspaceOperation} object — the canonical example
|
|
148
|
-
*
|
|
156
|
+
* Holds a valid {@link import('./types.js').WorkspaceOperation} object — the canonical example
|
|
157
|
+
* embedded verbatim in {@link WORKSPACE_TOOL_DESCRIPTION}.
|
|
149
158
|
*
|
|
150
159
|
* @remarks
|
|
151
160
|
* A `'write'` op (the most common authoring
|
|
@@ -158,11 +167,15 @@ var WORKSPACE_TOOL_EXAMPLE = Object.freeze({
|
|
|
158
167
|
content: "hello"
|
|
159
168
|
});
|
|
160
169
|
/**
|
|
161
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
170
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
171
|
+
* {@link import('./factories.js').createWorkspaceTool} advertises in place of
|
|
172
|
+
* {@link WORKSPACE_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed file editing and
|
|
173
|
+
* pointing at `describe` for the operation list and its fields.
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
177
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
178
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
166
179
|
*/
|
|
167
180
|
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
181
|
/**
|
|
@@ -198,8 +211,9 @@ var WORKSPACE_TOOL_DESCRIPTION = [
|
|
|
198
211
|
JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
|
|
199
212
|
].join("\n");
|
|
200
213
|
/**
|
|
201
|
-
* Holds the name {@link import('./factories.js').createDescribeTool} advertises by default
|
|
202
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
214
|
+
* Holds the name {@link import('./factories.js').createDescribeTool} advertises by default,
|
|
215
|
+
* `'describe'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
216
|
+
* under.
|
|
203
217
|
*
|
|
204
218
|
* @remarks
|
|
205
219
|
* Pairs with the lean {@link AGENT_TOOL_SUMMARY} / {@link WORKFLOW_TOOL_SUMMARY} /
|
|
@@ -215,7 +229,8 @@ var DESCRIBE_TOOL_NAME = "describe";
|
|
|
215
229
|
*/
|
|
216
230
|
var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered tool.";
|
|
217
231
|
/**
|
|
218
|
-
* Holds the
|
|
232
|
+
* Holds the description {@link import('./factories.js').createDescribeTool} advertises — the
|
|
233
|
+
* registered tool `name` it requires, and the full description of that tool it returns.
|
|
219
234
|
*
|
|
220
235
|
* @remarks
|
|
221
236
|
* Deliberately short — unlike the workflow / workspace / agent tools, this one has no authoring
|
|
@@ -223,19 +238,27 @@ var DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered t
|
|
|
223
238
|
*/
|
|
224
239
|
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
240
|
/**
|
|
226
|
-
* Holds the name {@link import('./factories.js').createPromptTool} advertises by default —
|
|
227
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
241
|
+
* Holds the name {@link import('./factories.js').createPromptTool} advertises by default, `'ask'` —
|
|
242
|
+
* the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
228
243
|
*/
|
|
229
244
|
var PROMPT_TOOL_NAME = "ask";
|
|
230
245
|
/**
|
|
231
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
246
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
247
|
+
* {@link import('./factories.js').createPromptTool} advertises in place of
|
|
248
|
+
* {@link PROMPT_TOOL_DESCRIPTION} — one sentence offering the blocking multi-field ask and pointing
|
|
249
|
+
* at `describe` for the schema.
|
|
250
|
+
*
|
|
251
|
+
* @remarks
|
|
252
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
253
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
254
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
236
255
|
*/
|
|
237
256
|
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
|
-
/**
|
|
257
|
+
/**
|
|
258
|
+
* Holds the form protocol {@link import('./factories.js').createPromptTool} advertises — the
|
|
259
|
+
* required `to` and `schema`, how a field declares its control and its rules, and a worked
|
|
260
|
+
* example.
|
|
261
|
+
*/
|
|
239
262
|
var PROMPT_TOOL_DESCRIPTION = [
|
|
240
263
|
"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
264
|
"",
|
|
@@ -261,19 +284,27 @@ var PROMPT_TOOL_DESCRIPTION = [
|
|
|
261
284
|
})
|
|
262
285
|
].join("\n");
|
|
263
286
|
/**
|
|
264
|
-
* Holds the name {@link import('./factories.js').createAnswerTool} advertises by default
|
|
265
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
287
|
+
* Holds the name {@link import('./factories.js').createAnswerTool} advertises by default,
|
|
288
|
+
* `'answer'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
289
|
+
* under.
|
|
266
290
|
*/
|
|
267
291
|
var ANSWER_TOOL_NAME = "answer";
|
|
268
292
|
/**
|
|
269
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
293
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
294
|
+
* {@link import('./factories.js').createAnswerTool} advertises in place of
|
|
295
|
+
* {@link ANSWER_TOOL_DESCRIPTION} — one sentence offering the pending listing and the answer call,
|
|
296
|
+
* and pointing at `describe` for the fields each takes.
|
|
297
|
+
*
|
|
298
|
+
* @remarks
|
|
299
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
300
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
301
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
274
302
|
*/
|
|
275
303
|
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
|
-
/**
|
|
304
|
+
/**
|
|
305
|
+
* Holds the protocol {@link import('./factories.js').createAnswerTool} advertises — the `pending`
|
|
306
|
+
* and `answer` operations and the `values` record an answer supplies, each with a worked example.
|
|
307
|
+
*/
|
|
277
308
|
var ANSWER_TOOL_DESCRIPTION = [
|
|
278
309
|
"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
310
|
"",
|
|
@@ -293,24 +324,31 @@ var ANSWER_TOOL_DESCRIPTION = [
|
|
|
293
324
|
})
|
|
294
325
|
].join("\n");
|
|
295
326
|
/**
|
|
296
|
-
* Holds the name {@link import('./factories.js').createDatabaseTool} advertises by default
|
|
297
|
-
* a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
327
|
+
* Holds the name {@link import('./factories.js').createDatabaseTool} advertises by default,
|
|
328
|
+
* `'database'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
329
|
+
* under.
|
|
298
330
|
*/
|
|
299
331
|
var DATABASE_TOOL_NAME = "database";
|
|
300
332
|
/**
|
|
301
333
|
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
302
334
|
* {@link import('./factories.js').createDatabaseTool} advertises in place of
|
|
303
|
-
* {@link DATABASE_TOOL_DESCRIPTION}
|
|
335
|
+
* {@link DATABASE_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed database call and
|
|
336
|
+
* pointing at `describe` for the operation list, the query form, and the column DSL.
|
|
337
|
+
*
|
|
338
|
+
* @remarks
|
|
339
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
340
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
341
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
304
342
|
*/
|
|
305
343
|
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.";
|
|
306
344
|
/**
|
|
307
345
|
* Holds the description {@link import('./factories.js').createDatabaseTool} advertises — a
|
|
308
|
-
* multi-line guide that teaches a small model the operation list, the
|
|
346
|
+
* multi-line guide that teaches a small model the operation list, the serialized query form, and
|
|
309
347
|
* the {@link import('./types.js').TableSpec} column DSL.
|
|
310
348
|
*
|
|
311
349
|
* @remarks
|
|
312
|
-
* The query form is deliberately
|
|
313
|
-
* `{ column, operator, values, connector? }` where `values` is
|
|
350
|
+
* The query form is deliberately serialized (never fluent) — every condition is a flat object
|
|
351
|
+
* `{ column, operator, values, connector? }` where `values` is always an array, even for a
|
|
314
352
|
* single-value operator (`{ column: 'age', operator: 'from', values: [18] }`), so a small model
|
|
315
353
|
* never has to chain method calls or guess whether a value is scalar or a list.
|
|
316
354
|
*/
|
|
@@ -362,7 +400,10 @@ var DATABASE_TOOL_DESCRIPTION = [
|
|
|
362
400
|
}] }
|
|
363
401
|
})
|
|
364
402
|
].join("\n");
|
|
365
|
-
/**
|
|
403
|
+
/**
|
|
404
|
+
* Holds the default cap on rows a `records` call returns when the caller omits `query.limit`,
|
|
405
|
+
* `1000` — the database tool's default row ceiling.
|
|
406
|
+
*/
|
|
366
407
|
var DATABASE_TOOL_LIMIT = 1e3;
|
|
367
408
|
/** Lists the runtime-frozen database-tool mutation names disabled by `DatabaseToolOptions.readonly`. */
|
|
368
409
|
var DATABASE_TOOL_MUTATIONS = Object.freeze([
|
|
@@ -374,21 +415,30 @@ var DATABASE_TOOL_MUTATIONS = Object.freeze([
|
|
|
374
415
|
"destroy"
|
|
375
416
|
]);
|
|
376
417
|
/**
|
|
377
|
-
* Holds the name
|
|
378
|
-
* `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
418
|
+
* Holds the name {@link import('./factories.js').createRelationTool} advertises by default,
|
|
419
|
+
* `'relation'` — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers
|
|
420
|
+
* under.
|
|
379
421
|
*/
|
|
380
422
|
var RELATION_TOOL_NAME = "relation";
|
|
381
423
|
/**
|
|
382
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
383
|
-
* in place of
|
|
424
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
425
|
+
* {@link import('./factories.js').createRelationTool} advertises in place of
|
|
426
|
+
* {@link RELATION_TOOL_DESCRIPTION} — one sentence offering the `operation`-keyed relationship
|
|
427
|
+
* traversal and pointing at `describe` for the include-path syntax.
|
|
428
|
+
*
|
|
429
|
+
* @remarks
|
|
430
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
431
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
432
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
384
433
|
*/
|
|
385
434
|
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.";
|
|
386
435
|
/**
|
|
387
|
-
* Holds the
|
|
388
|
-
* the operation list and the flat dot-path `include`
|
|
436
|
+
* Holds the description {@link import('./factories.js').createRelationTool} advertises — a
|
|
437
|
+
* multi-line guide that teaches a small model the operation list and the flat dot-path `include`
|
|
438
|
+
* syntax.
|
|
389
439
|
*
|
|
390
440
|
* @remarks
|
|
391
|
-
* An include path is a
|
|
441
|
+
* An include path is a flat dot-separated string (`'contacts.account'`), never a nested object —
|
|
392
442
|
* the same small-model ergonomic lever the other tools in this package use for flat args.
|
|
393
443
|
*/
|
|
394
444
|
var RELATION_TOOL_DESCRIPTION = [
|
|
@@ -410,24 +460,38 @@ var RELATION_TOOL_DESCRIPTION = [
|
|
|
410
460
|
include: ["contacts.account"]
|
|
411
461
|
})
|
|
412
462
|
].join("\n");
|
|
413
|
-
/**
|
|
463
|
+
/**
|
|
464
|
+
* Holds the default cap on rows a `find` or `links` call returns when the caller omits `limit`,
|
|
465
|
+
* `1000` — the relation tool's default row ceiling.
|
|
466
|
+
*/
|
|
414
467
|
var RELATION_TOOL_LIMIT = 1e3;
|
|
415
|
-
/**
|
|
468
|
+
/**
|
|
469
|
+
* Holds the default cap on how many `include` path segments deep a `load` or `find` call may
|
|
470
|
+
* traverse, `3` — the relation tool's default include-depth ceiling.
|
|
471
|
+
*/
|
|
416
472
|
var RELATION_TOOL_DEPTH = 3;
|
|
417
473
|
/**
|
|
418
|
-
* Holds the name {@link import('./factories.js').createInferTool} advertises by default
|
|
419
|
-
* model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
474
|
+
* Holds the name {@link import('./factories.js').createInferTool} advertises by default, `'infer'`
|
|
475
|
+
* — the key a model calls and the `ToolManagerInterface` (`@orkestrel/tool`) registers under.
|
|
420
476
|
*/
|
|
421
477
|
var INFER_TOOL_NAME = "infer";
|
|
422
478
|
/**
|
|
423
|
-
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
479
|
+
* Holds the lean {@link import('@orkestrel/tool').ToolInterface.summary}
|
|
480
|
+
* {@link import('./factories.js').createInferTool} advertises in place of
|
|
481
|
+
* {@link INFER_TOOL_DESCRIPTION} — one sentence offering schema inference from example values and
|
|
482
|
+
* pointing at `describe` for the fields it takes.
|
|
483
|
+
*
|
|
484
|
+
* @remarks
|
|
485
|
+
* A `ToolManagerInterface.definitions()` call (`@orkestrel/tool`) advertises
|
|
486
|
+
* `summary ?? description`, so this one-sentence text stands in for the full teaching description;
|
|
487
|
+
* the full text stays retrievable through {@link import('./factories.js').createDescribeTool}.
|
|
428
488
|
*/
|
|
429
489
|
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.";
|
|
430
|
-
/**
|
|
490
|
+
/**
|
|
491
|
+
* Holds the schema-inference protocol {@link import('./factories.js').createInferTool} advertises —
|
|
492
|
+
* the required `samples`, the optional `format`, `enum`, and `candidates` arguments, and a worked
|
|
493
|
+
* example of the bare return and of the `candidates`-wrapped return.
|
|
494
|
+
*/
|
|
431
495
|
var INFER_TOOL_DESCRIPTION = [
|
|
432
496
|
"Infer a JSON Schema from example values, returned in the same shape a tool advertises its parameters.",
|
|
433
497
|
"",
|
|
@@ -527,7 +591,7 @@ var INFER_TOOL_DESCRIPTION = [
|
|
|
527
591
|
* @remarks
|
|
528
592
|
* Carries a machine-readable `code` (see {@link import('./types.js').ToolboxErrorCode}) and
|
|
529
593
|
* an optional `context` bag for structured diagnostics. They are readable through an in-process
|
|
530
|
-
* catch, including around a direct `tool.execute(args)` call. The `ToolManagerInterface`
|
|
594
|
+
* catch, including around a direct `tool.execute(args, context)` call. The `ToolManagerInterface`
|
|
531
595
|
* (`@orkestrel/tool`) instead flattens every throw to the canonical tool result's top-level
|
|
532
596
|
* `error` message string under AGENTS' “narrow untrusted input with guards” rule.
|
|
533
597
|
*
|
|
@@ -552,7 +616,7 @@ var ToolboxError = class extends Error {
|
|
|
552
616
|
}
|
|
553
617
|
};
|
|
554
618
|
/**
|
|
555
|
-
* Narrows an unknown caught value to
|
|
619
|
+
* Narrows an unknown caught value to a {@link ToolboxError}.
|
|
556
620
|
*
|
|
557
621
|
* @param value - The value to test (typically a `catch` binding)
|
|
558
622
|
* @returns True if `value` is an {@link ToolboxError}; false otherwise
|
|
@@ -629,7 +693,7 @@ function isColumnPrimitive(value) {
|
|
|
629
693
|
return value === "string" || value === "integer" || value === "number" || value === "boolean";
|
|
630
694
|
}
|
|
631
695
|
/**
|
|
632
|
-
* Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id`
|
|
696
|
+
* Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id` and `driver`, a
|
|
633
697
|
* `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
|
|
634
698
|
* `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
|
|
635
699
|
* {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
|
|
@@ -730,8 +794,8 @@ var describeToolShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkest
|
|
|
730
794
|
description: "The registered name of the tool whose full description to return."
|
|
731
795
|
}) });
|
|
732
796
|
/**
|
|
733
|
-
* Describes the shape of a {@link import('./types.js').TaskDraft} — identical to a strict task
|
|
734
|
-
*
|
|
797
|
+
* Describes the shape of a {@link import('./types.js').TaskDraft} — identical to a strict task
|
|
798
|
+
* shape except that `id` and `name` are optional.
|
|
735
799
|
*/
|
|
736
800
|
var taskDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
737
801
|
id: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
|
|
@@ -758,8 +822,8 @@ var taskDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
758
822
|
}))
|
|
759
823
|
});
|
|
760
824
|
/**
|
|
761
|
-
* Describes the shape of a
|
|
762
|
-
* `name` are
|
|
825
|
+
* Describes the shape of a phase in a draft workflow — identical to a strict phase shape except
|
|
826
|
+
* that `id` and `name` are optional and each task takes {@link taskDraftShape}.
|
|
763
827
|
*/
|
|
764
828
|
var phaseDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
765
829
|
id: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
|
|
@@ -779,17 +843,17 @@ var phaseDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
779
843
|
bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Per-phase failure-policy override; omitted inherits the workflow bail." }))
|
|
780
844
|
});
|
|
781
845
|
/**
|
|
782
|
-
* Describes the shape of a
|
|
783
|
-
* are
|
|
784
|
-
* identity
|
|
846
|
+
* Describes the shape of a draft workflow — identical to a strict workflow shape except that `id`
|
|
847
|
+
* and `name` are optional at the workflow, phase, and task levels, so a small model can omit every
|
|
848
|
+
* identity string and let the tool synthesize them positionally.
|
|
785
849
|
*
|
|
786
850
|
* @remarks
|
|
787
851
|
* The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract} compiles.
|
|
788
852
|
* `behavior` stays optional like the strict form; omission is the deliberate JSON `null` no-op. A
|
|
789
853
|
* provided `id` / `name` still has `minLength: 1` (so
|
|
790
|
-
* an explicitly-empty `id: ''` is
|
|
854
|
+
* an explicitly-empty `id: ''` is rejected, not auto-filled). After
|
|
791
855
|
* {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
|
|
792
|
-
* validated against the
|
|
856
|
+
* validated against the strict `createWorkflowContract` (`@orkestrel/workflow`) gate before
|
|
793
857
|
* running.
|
|
794
858
|
*/
|
|
795
859
|
var workflowDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
@@ -806,10 +870,11 @@ var workflowDraftShape = (0, _orkestrel_contract.objectShape)({
|
|
|
806
870
|
bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Failure policy: false (default) continues gracefully, true halts on the first failure." }))
|
|
807
871
|
});
|
|
808
872
|
/**
|
|
809
|
-
* Describes the shape of
|
|
873
|
+
* Describes the shape of one flat step — `{ name }` — the building block of
|
|
874
|
+
* {@link workflowStepsShape}.
|
|
810
875
|
*
|
|
811
876
|
* @remarks
|
|
812
|
-
* `name` is the
|
|
877
|
+
* `name` is the registered behavior name the step runs (it becomes the task's `behavior`). The tool
|
|
813
878
|
* expands each step into a one-task phase, in order ({@link import('./helpers.js').expandSteps}).
|
|
814
879
|
*/
|
|
815
880
|
var stepShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkestrel_contract.stringShape)({
|
|
@@ -817,16 +882,17 @@ var stepShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkestrel_cont
|
|
|
817
882
|
description: "The registered behavior name this step runs (becomes the task behavior)."
|
|
818
883
|
}) });
|
|
819
884
|
/**
|
|
820
|
-
* Describes the
|
|
821
|
-
* `parameters` — the simplest surface a small model can fill:
|
|
885
|
+
* Describes the flat authoring shape {@link import('./factories.js').createWorkflowTool} advertises
|
|
886
|
+
* as its `parameters` — the simplest surface a small model can fill:
|
|
887
|
+
* `{ name?, steps: [{ name }] }`.
|
|
822
888
|
*
|
|
823
889
|
* @remarks
|
|
824
890
|
* A deliberately-reduced surface: a flat ordered list of steps, each a `{ name }`. The tool
|
|
825
|
-
*
|
|
891
|
+
* expands it ({@link import('./helpers.js').expandSteps}) into a full
|
|
826
892
|
* {@link import('./types.js').WorkflowDefinition} — one one-task phase per step, in order —
|
|
827
|
-
* then validates against the
|
|
828
|
-
* full nested form is
|
|
829
|
-
* documented as the advanced escape-hatch in the tool's description — but
|
|
893
|
+
* then validates against the strict `createWorkflowContract` (`@orkestrel/workflow`) gate. The
|
|
894
|
+
* full nested form is still accepted by the tool (it branches on the args' shape) and is
|
|
895
|
+
* documented as the advanced escape-hatch in the tool's description — but this is what
|
|
830
896
|
* `parameters` advertises.
|
|
831
897
|
*/
|
|
832
898
|
var workflowStepsShape = (0, _orkestrel_contract.objectShape)({
|
|
@@ -840,17 +906,17 @@ var workflowStepsShape = (0, _orkestrel_contract.objectShape)({
|
|
|
840
906
|
* Describes the shape of a {@link import('./types.js').WorkspaceOperation} — a descriptive tagged union
|
|
841
907
|
* over the workspace edit, read, and navigation operations, discriminated by the `operation`
|
|
842
908
|
* literal (never a bare `kind`). Each variant leads with its `operation`
|
|
843
|
-
* discriminant then its
|
|
909
|
+
* discriminant then its flat fields, every field through `stringShape` / `optionalShape` /
|
|
844
910
|
* `integerShape({ min: 1 })` / `booleanShape`, each carrying a strong field-level `description`.
|
|
845
911
|
*
|
|
846
912
|
* @remarks
|
|
847
|
-
* The union compiles to an `anyOf` JSON Schema
|
|
913
|
+
* The union compiles to an `anyOf` JSON Schema, a `unionOf` guard, and a first-match parser
|
|
848
914
|
* automatically ({@link import('./factories.js').createWorkspaceTool} types the result to the
|
|
849
|
-
* hand-written {@link import('./types.js').WorkspaceOperation}). `limit` and the
|
|
850
|
-
* caret components are
|
|
851
|
-
* are `optionalShape(booleanShape(...))`. The
|
|
915
|
+
* hand-written {@link import('./types.js').WorkspaceOperation}). `limit` and the `'splice'`
|
|
916
|
+
* caret components are positive integers (`integerShape({ min: 1 })`); `regex` / `sensitive`
|
|
917
|
+
* are `optionalShape(booleanShape(...))`. The registry arms — `workspaces` (list the
|
|
852
918
|
* workspaces the model can move between) and `switch` (re-point the active one by `id`) — let a
|
|
853
|
-
* model
|
|
919
|
+
* model discover then choose which workspace the edit / read arms target.
|
|
854
920
|
*/
|
|
855
921
|
var workspaceToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.objectShape)({
|
|
856
922
|
operation: (0, _orkestrel_contract.literalShape)(["read"], { description: "Read a whole text file's text by path." }),
|
|
@@ -934,13 +1000,23 @@ var columnSpecShape = (0, _orkestrel_contract.unionShape)(columnPrimitiveShape,
|
|
|
934
1000
|
}));
|
|
935
1001
|
/** Describes a {@link import('./types.js').TableSpec} — table name to `{ columns }`, each column a {@link columnSpecShape}. */
|
|
936
1002
|
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
|
-
/**
|
|
1003
|
+
/**
|
|
1004
|
+
* Describes one key value for the database tool and the relation tool — a string or number; the
|
|
1005
|
+
* array form (multiple keys, positional) resolves first, so an array argument is read as many keys
|
|
1006
|
+
* rather than one.
|
|
1007
|
+
*/
|
|
938
1008
|
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." }));
|
|
939
|
-
/** Describes a loose row — a flat object of column name to JSON value
|
|
1009
|
+
/** Describes a loose row — a flat object of column name to JSON value. */
|
|
940
1010
|
var rowShape = (0, _orkestrel_contract.recordShape)((0, _orkestrel_contract.jsonShape)(), { description: "A row as a flat object of column name to value." });
|
|
941
|
-
/**
|
|
1011
|
+
/**
|
|
1012
|
+
* Describes one or many loose rows — the array form resolves first, so an array argument is read as
|
|
1013
|
+
* many rows rather than one.
|
|
1014
|
+
*/
|
|
942
1015
|
var rowsShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.arrayShape)(rowShape, { description: "Multiple rows." }), rowShape);
|
|
943
|
-
/**
|
|
1016
|
+
/**
|
|
1017
|
+
* Describes one serialized where condition — `values` is always an array, even for a single-value
|
|
1018
|
+
* operator.
|
|
1019
|
+
*/
|
|
944
1020
|
var conditionShape = (0, _orkestrel_contract.objectShape)({
|
|
945
1021
|
column: (0, _orkestrel_contract.stringShape)({ description: "The column this condition applies to." }),
|
|
946
1022
|
operator: (0, _orkestrel_contract.literalShape)([
|
|
@@ -963,12 +1039,12 @@ var conditionShape = (0, _orkestrel_contract.objectShape)({
|
|
|
963
1039
|
values: (0, _orkestrel_contract.arrayShape)((0, _orkestrel_contract.jsonShape)(), { description: "The operand values the operator needs (always an array, even for one value)." }),
|
|
964
1040
|
connector: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)(["and", "or"], { description: "Joins this condition to the next; omit on the last condition." }))
|
|
965
1041
|
});
|
|
966
|
-
/** Describes one sort term. */
|
|
1042
|
+
/** Describes one sort term — a `column` to sort by and the `direction` to sort it in. */
|
|
967
1043
|
var orderShape = (0, _orkestrel_contract.objectShape)({
|
|
968
1044
|
column: (0, _orkestrel_contract.stringShape)({ description: "The column to sort by." }),
|
|
969
1045
|
direction: (0, _orkestrel_contract.literalShape)(["ascending", "descending"], { description: "The sort direction." })
|
|
970
1046
|
});
|
|
971
|
-
/** Describes the
|
|
1047
|
+
/** Describes the serialized query form — conditions, order, and pagination. */
|
|
972
1048
|
var queryShape = (0, _orkestrel_contract.objectShape)({
|
|
973
1049
|
conditions: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(conditionShape, { description: "The WHERE conditions, folded left to right." })),
|
|
974
1050
|
order: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.arrayShape)(orderShape, { description: "The sort terms, applied in order." })),
|
|
@@ -983,9 +1059,8 @@ var queryShape = (0, _orkestrel_contract.objectShape)({
|
|
|
983
1059
|
});
|
|
984
1060
|
/**
|
|
985
1061
|
* Describes the shape of {@link import('./factories.js').createDatabaseTool}'s call arguments —
|
|
986
|
-
* discriminated by `operation` into the
|
|
987
|
-
* `'
|
|
988
|
-
* `'remove'` / `'destroy'`).
|
|
1062
|
+
* discriminated by `operation` into the database operations `'create'`, `'tables'`, `'get'`,
|
|
1063
|
+
* `'records'`, `'count'`, `'aggregate'`, `'add'`, `'set'`, `'update'`, `'remove'`, and `'destroy'`.
|
|
989
1064
|
*
|
|
990
1065
|
* @remarks
|
|
991
1066
|
* Every arm carries `id` (the database id). `'create'` carries `tables` (the
|
|
@@ -993,7 +1068,7 @@ var queryShape = (0, _orkestrel_contract.objectShape)({
|
|
|
993
1068
|
* {@link import('./compilers.js').expandTables}); `'get'` / `'update'` / `'remove'` carry `key`
|
|
994
1069
|
* (one key or an array of keys, positional); `'add'` / `'set'` carry `row` (one row or an array of
|
|
995
1070
|
* rows); `'update'` also carries `changes` (a loose partial row); `'records'` / `'count'` /
|
|
996
|
-
* `'aggregate'` carry an optional `query` (the
|
|
1071
|
+
* `'aggregate'` carry an optional `query` (the serialized form — `values` is always an array,
|
|
997
1072
|
* even for a single-value operator, so a caller never chains method calls or guesses arity).
|
|
998
1073
|
*/
|
|
999
1074
|
var databaseToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contract.objectShape)({
|
|
@@ -1137,8 +1212,8 @@ var managerShape = (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contrac
|
|
|
1137
1212
|
}));
|
|
1138
1213
|
/**
|
|
1139
1214
|
* Describes the shape of {@link import('./factories.js').createRelationTool}'s call arguments —
|
|
1140
|
-
* discriminated by `operation` into the
|
|
1141
|
-
* `'unlink'
|
|
1215
|
+
* discriminated by `operation` into the relation operations `'load'`, `'find'`, `'link'`,
|
|
1216
|
+
* `'unlink'`, and `'links'`.
|
|
1142
1217
|
*
|
|
1143
1218
|
* @remarks
|
|
1144
1219
|
* `'load'` fetches one or more rows (positional key/array) with `include` attached. `'find'`
|
|
@@ -1223,8 +1298,8 @@ var relationToolShape = (0, _orkestrel_contract.unionShape)((0, _orkestrel_contr
|
|
|
1223
1298
|
* `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
|
|
1224
1299
|
* surfaced by the handler as a typed `TOOL` {@link import('./errors.js').ToolboxError}. When
|
|
1225
1300
|
* `candidates` is present (any array, including empty), the handler compiles a contract from the
|
|
1226
|
-
* freshly inferred schema and checks each candidate against it with a
|
|
1227
|
-
* coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s
|
|
1301
|
+
* freshly inferred schema and checks each candidate against it with a strict guard (`.is`, no
|
|
1302
|
+
* coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s normalizing
|
|
1228
1303
|
* `.parse` enforcement.
|
|
1229
1304
|
*/
|
|
1230
1305
|
var inferToolShape = (0, _orkestrel_contract.objectShape)({
|
|
@@ -1242,8 +1317,8 @@ var inferToolShape = (0, _orkestrel_contract.objectShape)({
|
|
|
1242
1317
|
* Returns the ancestry identifier of a workflow in a run chain — `workflow:<id>`.
|
|
1243
1318
|
*
|
|
1244
1319
|
* @remarks
|
|
1245
|
-
* Namespacing keeps a workflow id and a {@link tagAgent} agent name in
|
|
1246
|
-
* collision, so re-entering a workflow
|
|
1320
|
+
* Namespacing keeps a workflow id and a {@link tagAgent} agent name in one set without
|
|
1321
|
+
* collision, so re-entering a workflow or an agent already in the chain is a single `includes`
|
|
1247
1322
|
* check.
|
|
1248
1323
|
*
|
|
1249
1324
|
* @param id - The workflow definition's `id`
|
|
@@ -1298,12 +1373,12 @@ function deriveWorkflowDepth(lineage) {
|
|
|
1298
1373
|
return Math.max(0, workflows - 1);
|
|
1299
1374
|
}
|
|
1300
1375
|
/**
|
|
1301
|
-
* Builds the plain success summary {@link import('./factories.js').createWorkflowTool} returns on
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1376
|
+
* Builds the plain success summary {@link import('./factories.js').createWorkflowTool} returns on a
|
|
1377
|
+
* completed run — the universal tool-handler contract: return a plain value on success, appearing
|
|
1378
|
+
* identically over both the agent loop and MCP.
|
|
1304
1379
|
*
|
|
1305
1380
|
* @remarks
|
|
1306
|
-
* The summary is
|
|
1381
|
+
* The summary is lean: the workflow's terminal `status`, settled-task count, and exact optional
|
|
1307
1382
|
* native persistence outcome. It carries no synthetic `id` / `name`: a tool handler has no call
|
|
1308
1383
|
* id; the `ToolManagerInterface` (`@orkestrel/tool`) supplies the canonical envelope's identity.
|
|
1309
1384
|
*
|
|
@@ -1319,21 +1394,21 @@ function summarizeWorkflow(result) {
|
|
|
1319
1394
|
};
|
|
1320
1395
|
}
|
|
1321
1396
|
/**
|
|
1322
|
-
* Completes a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesizes
|
|
1323
|
-
*
|
|
1324
|
-
*
|
|
1397
|
+
* Completes a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesizes a
|
|
1398
|
+
* missing `id` deterministically and positionally, and defaults a missing `name` to its resolved
|
|
1399
|
+
* `id`.
|
|
1325
1400
|
*
|
|
1326
1401
|
* @remarks
|
|
1327
1402
|
* The positional id scheme is stable and human-legible: the workflow is `wf`, phase `i` is
|
|
1328
1403
|
* `phase-<i>`, and task `j` of that phase is `<phaseId>-task-<j>` (so a provided phase id flows
|
|
1329
|
-
* into its tasks' synthesized ids). A
|
|
1404
|
+
* into its tasks' synthesized ids). A provided `id` / `name` at any level is kept verbatim —
|
|
1330
1405
|
* synthesis touches only the omitted ones. A missing `name` defaults to the resolved `id` (never
|
|
1331
|
-
* the other way round), so the result always
|
|
1406
|
+
* the other way round), so the result always carries an `id` and a `name`. `behavior`, `description`, the per-phase
|
|
1332
1407
|
* `concurrency` / `bail`, the per-task `retries` / `timeout`, and the workflow `bail` carry over
|
|
1333
1408
|
* unchanged. The result is a complete {@link WorkflowDefinition}; the caller still validates it
|
|
1334
|
-
* against the
|
|
1409
|
+
* against the strict contract.
|
|
1335
1410
|
*
|
|
1336
|
-
* @param draft - The draft workflow (id
|
|
1411
|
+
* @param draft - The draft workflow (`id` and `name` optional at the workflow, phase, and task levels)
|
|
1337
1412
|
* @returns A complete {@link WorkflowDefinition} with every id/name filled
|
|
1338
1413
|
*/
|
|
1339
1414
|
function completeDraft(draft) {
|
|
@@ -1388,18 +1463,18 @@ function completeTaskDraft(task, phaseId, index) {
|
|
|
1388
1463
|
}
|
|
1389
1464
|
/**
|
|
1390
1465
|
* Expands a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each step
|
|
1391
|
-
* becomes a one-task phase,
|
|
1466
|
+
* becomes a one-task phase, in order.
|
|
1392
1467
|
*
|
|
1393
1468
|
* @remarks
|
|
1394
|
-
* The expansion of the tool's
|
|
1469
|
+
* The expansion of the tool's advertised surface: the deliberately-reduced flat form. Each
|
|
1395
1470
|
* {@link import('./types.js').WorkflowStep} maps to a phase holding exactly one task: the step's
|
|
1396
1471
|
* `name` becomes the task's `behavior` (the behavior-registry key). Ids/names are auto-filled
|
|
1397
1472
|
* positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates to
|
|
1398
|
-
* {@link completeDraft}, so the
|
|
1473
|
+
* {@link completeDraft}, so the lenient surfaces share one synthesis path (step `i` → phase
|
|
1399
1474
|
* `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes both the workflow's
|
|
1400
1475
|
* deterministic id and its name, so named flat workflows retain distinct persistence keys;
|
|
1401
1476
|
* omission keeps the shared draft fallback `wf`. The result is a complete definition the caller
|
|
1402
|
-
* validates against the
|
|
1477
|
+
* validates against the strict contract before running.
|
|
1403
1478
|
*
|
|
1404
1479
|
* @param flat - The flat steps blob (`{ name?, steps: [{ name }] }`)
|
|
1405
1480
|
* @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
|
|
@@ -1459,15 +1534,15 @@ function inferRelationCode(error) {
|
|
|
1459
1534
|
return (0, _orkestrel_relation.isRelationError)(error) ? error.code : void 0;
|
|
1460
1535
|
}
|
|
1461
1536
|
/**
|
|
1462
|
-
* Expands the relation tool's
|
|
1537
|
+
* Expands the relation tool's flat dot-path `include` list into a live `@orkestrel/relation`
|
|
1463
1538
|
* {@link Include} tree — the pure leaf {@link import('./factories.js').createRelationTool} calls
|
|
1464
1539
|
* before a `'load'` / `'find'` call.
|
|
1465
1540
|
*
|
|
1466
1541
|
* @remarks
|
|
1467
1542
|
* Each path splits on `'.'` into a chain of relation names, deep-merged into one nested
|
|
1468
|
-
* `Include` object with a leaf `true`. A longer path
|
|
1543
|
+
* `Include` object with a leaf `true`. A longer path subsumes a shorter sibling's bare `true` —
|
|
1469
1544
|
* `'contacts'` followed by `'contacts.account'` yields `{ contacts: { account: true } }`, never
|
|
1470
|
-
* overwriting the deeper chain. An
|
|
1545
|
+
* overwriting the deeper chain. An empty segment (`''`, from a leading/trailing/doubled `.`) or a
|
|
1471
1546
|
* path whose segment count exceeds `depth` throws a typed `TOOL` {@link ToolboxError}.
|
|
1472
1547
|
*
|
|
1473
1548
|
* @param paths - The flat dot-path `include` list (or `undefined` — yields `{}`)
|
|
@@ -1536,7 +1611,7 @@ function expandInclude(paths, depth) {
|
|
|
1536
1611
|
*
|
|
1537
1612
|
* @remarks
|
|
1538
1613
|
* An explicit `name` must match a key of `managers` (a miss throws a typed `TOOL`
|
|
1539
|
-
* {@link ToolboxError} naming the registered managers). An
|
|
1614
|
+
* {@link ToolboxError} naming the registered managers). An omitted `name` resolves to the sole
|
|
1540
1615
|
* registered manager when exactly one is registered, else throws the same typed error.
|
|
1541
1616
|
*
|
|
1542
1617
|
* @param managers - The tool's registered `RelationManagerInterface` map
|
|
@@ -1578,11 +1653,11 @@ function resolveRelationModel(manager, name) {
|
|
|
1578
1653
|
}
|
|
1579
1654
|
/**
|
|
1580
1655
|
* Returns the canonical live `@orkestrel/database` {@link QueryInput} for the database tool's
|
|
1581
|
-
* parsed
|
|
1656
|
+
* parsed serialized query — each condition's omitted `connector` defaults to `'and'`.
|
|
1582
1657
|
*
|
|
1583
1658
|
* @remarks
|
|
1584
1659
|
* The wire form ({@link import('./shapers.js').databaseToolShape}) lets a caller drop `connector`
|
|
1585
|
-
* on the last condition (it has nothing to join
|
|
1660
|
+
* on the last condition (it has nothing to join forward to); the compiled `Condition` a live
|
|
1586
1661
|
* `@orkestrel/database` table call accepts always carries one, so this fills the gap. `order` /
|
|
1587
1662
|
* `limit` / `offset` pass through unchanged. Pure and total.
|
|
1588
1663
|
*
|
|
@@ -1628,14 +1703,14 @@ function resolveLimit(requested, cap) {
|
|
|
1628
1703
|
return Math.max(0, Math.min(requested ?? cap, cap));
|
|
1629
1704
|
}
|
|
1630
1705
|
/**
|
|
1631
|
-
* Clamps a `'records'` call's query to a row cap, and builds the
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1706
|
+
* Clamps a `'records'` call's query to a row cap, and builds the probe query the caller reads with
|
|
1707
|
+
* — the pure leaf {@link import('./factories.js').createDatabaseTool}'s `'records'` operation uses
|
|
1708
|
+
* to detect truncation without a separate `count` round trip.
|
|
1634
1709
|
*
|
|
1635
1710
|
* @remarks
|
|
1636
1711
|
* {@link resolveLimit} picks the effective limit, so a caller can never exceed the configured cap
|
|
1637
1712
|
* by supplying a larger `query.limit`, and can never drive it below `0`. The returned probe query
|
|
1638
|
-
* requests
|
|
1713
|
+
* requests one more row than the effective limit (`limit: effective + 1`) — if storage returns
|
|
1639
1714
|
* that many, the caller knows the true result was truncated (`rows.length > effective`) and slices
|
|
1640
1715
|
* back down to `effective` before returning.
|
|
1641
1716
|
*
|
|
@@ -1651,7 +1726,7 @@ function resolveLimit(requested, cap) {
|
|
|
1651
1726
|
*
|
|
1652
1727
|
* @param query - The live query to clamp (or `undefined`)
|
|
1653
1728
|
* @param cap - The row-count ceiling
|
|
1654
|
-
* @returns The
|
|
1729
|
+
* @returns The probe query (`limit` bumped by one) and the effective `limit`
|
|
1655
1730
|
*/
|
|
1656
1731
|
function clampQuery(query, cap) {
|
|
1657
1732
|
const limit = resolveLimit(query?.limit, cap);
|
|
@@ -1713,7 +1788,7 @@ function compileColumnPrimitive(primitive) {
|
|
|
1713
1788
|
//#region src/core/stores/MemoryDefinitionStore.ts
|
|
1714
1789
|
/**
|
|
1715
1790
|
* Represents the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of
|
|
1716
|
-
* {@link DatabaseDefinition}s keyed by database id, the
|
|
1791
|
+
* {@link DatabaseDefinition}s keyed by database id, the default store
|
|
1717
1792
|
* {@link import('../factories.js').createMemoryDefinitionStore} builds. It implements the same
|
|
1718
1793
|
* {@link DefinitionStoreInterface} contract as
|
|
1719
1794
|
* {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}: this store copies on
|
|
@@ -1722,19 +1797,19 @@ function compileColumnPrimitive(primitive) {
|
|
|
1722
1797
|
*
|
|
1723
1798
|
* @remarks
|
|
1724
1799
|
* A plain `Map<string, DatabaseDefinition>` (the definition is already pure,
|
|
1725
|
-
* self-contained
|
|
1726
|
-
* structured-cloned on both write and read, keeping caller mutation outside the store. There is
|
|
1727
|
-
* idle-TTL and
|
|
1728
|
-
* backend (JSON / SQLite / IndexedDB) swaps in through the
|
|
1800
|
+
* self-contained config-only JSON, so no encoding is needed for the memory tier). Values are
|
|
1801
|
+
* structured-cloned on both write and read, keeping caller mutation outside the store. There is no
|
|
1802
|
+
* idle-TTL and no eviction: a persisted definition lives until an explicit `delete`. A durable
|
|
1803
|
+
* backend (JSON / SQLite / IndexedDB) swaps in through the same interface without touching a
|
|
1729
1804
|
* consumer — its driver-pluggable twin is
|
|
1730
1805
|
* {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore} (the definition as one
|
|
1731
1806
|
* opaque JSON column).
|
|
1732
1807
|
*
|
|
1733
1808
|
* - **`get` resolves the persisted definition for an id**, or `undefined` if none is stored.
|
|
1734
|
-
* - **`set` inserts / replaces under the definition's
|
|
1809
|
+
* - **`set` inserts / replaces under the definition's own `id`** (no separate id param).
|
|
1735
1810
|
* - **`delete` drops a definition by id**; an absent id is a no-op (no throw).
|
|
1736
1811
|
*
|
|
1737
|
-
* The public surface is
|
|
1812
|
+
* The public surface is exactly `get` / `set` / `delete` — no extra members (the method
|
|
1738
1813
|
* bijection with {@link DefinitionStoreInterface}).
|
|
1739
1814
|
*
|
|
1740
1815
|
* @example
|
|
@@ -1760,10 +1835,10 @@ var MemoryDefinitionStore = class {
|
|
|
1760
1835
|
return Promise.resolve(definition === void 0 ? void 0 : structuredClone(definition));
|
|
1761
1836
|
}
|
|
1762
1837
|
/**
|
|
1763
|
-
* Inserts or replaces a definition under its
|
|
1838
|
+
* Inserts or replaces a definition under its own `id`, copied into the backing `Map`.
|
|
1764
1839
|
*
|
|
1765
1840
|
* @param definition - The config to persist; its own `id` is the key (no separate id param)
|
|
1766
|
-
* @returns A promise settling
|
|
1841
|
+
* @returns A promise settling after the definition is stored
|
|
1767
1842
|
*/
|
|
1768
1843
|
set(definition) {
|
|
1769
1844
|
this.#definitions.set(definition.id, structuredClone(definition));
|
|
@@ -1773,7 +1848,7 @@ var MemoryDefinitionStore = class {
|
|
|
1773
1848
|
* Drops the definition stored under `id`.
|
|
1774
1849
|
*
|
|
1775
1850
|
* @param id - The database id to drop; an absent id is a no-op, never a throw
|
|
1776
|
-
* @returns A promise settling
|
|
1851
|
+
* @returns A promise settling after no definition is stored under `id`
|
|
1777
1852
|
*/
|
|
1778
1853
|
delete(id) {
|
|
1779
1854
|
this.#definitions.delete(id);
|
|
@@ -1784,25 +1859,25 @@ var MemoryDefinitionStore = class {
|
|
|
1784
1859
|
//#region src/core/stores/DatabaseDefinitionStore.ts
|
|
1785
1860
|
/**
|
|
1786
1861
|
* Represents a {@link DefinitionStoreInterface} backed by one table of the `@orkestrel/database` layer — a
|
|
1787
|
-
* database's durable
|
|
1862
|
+
* database's durable config state is a row, so persistence reduces to keyed point-access
|
|
1788
1863
|
* (`get` / `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the
|
|
1789
1864
|
* plain-`Map` {@link import('./MemoryDefinitionStore.js').MemoryDefinitionStore}.
|
|
1790
1865
|
*
|
|
1791
1866
|
* @remarks
|
|
1792
1867
|
* The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
|
|
1793
1868
|
* JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
|
|
1794
|
-
* IndexedDB backend swaps in
|
|
1869
|
+
* IndexedDB backend swaps in without touching a consumer — the same seam as
|
|
1795
1870
|
* {@link import('./MemoryDefinitionStore.js').MemoryDefinitionStore}. The driver defaults to
|
|
1796
1871
|
* memory ({@link import('../factories.js').createDatabaseDefinitionStore} passes
|
|
1797
|
-
* `createMemoryDriver()`), so it
|
|
1872
|
+
* `createMemoryDriver()`), so it also works in memory out of the box; you opt into the durable
|
|
1798
1873
|
* plumbing by passing a JSON / SQLite / IndexedDB driver.
|
|
1799
1874
|
*
|
|
1800
|
-
* The {@link DatabaseDefinition} is stored as
|
|
1801
|
-
* `{ id; definition }` ({@link DatabaseDefinitionRow}). The definition is already a
|
|
1802
|
-
* self-contained, pure-JSON
|
|
1803
|
-
*
|
|
1875
|
+
* The {@link DatabaseDefinition} is stored as one opaque JSON column — the table is a row of
|
|
1876
|
+
* `{ id; definition }` ({@link DatabaseDefinitionRow}). The definition is already a complete,
|
|
1877
|
+
* self-contained, pure-JSON config payload (never a live handle), so storing it whole is lossless
|
|
1878
|
+
* and keeps the row type flat (`definition` reads back as `unknown`).
|
|
1804
1879
|
*
|
|
1805
|
-
* - **`set(definition)` upserts under the definition's
|
|
1880
|
+
* - **`set(definition)` upserts under the definition's own `id`** (no separate id param) — it
|
|
1806
1881
|
* writes the row `{ id: definition.id, definition }`.
|
|
1807
1882
|
* - **`get(id)` resolves the stored definition for an id**, narrowing the opaque JSON column back
|
|
1808
1883
|
* to a {@link DatabaseDefinition} ({@link import('../validators.js').isDatabaseDefinition} — the
|
|
@@ -1810,7 +1885,7 @@ var MemoryDefinitionStore = class {
|
|
|
1810
1885
|
* or the stored blob is malformed.
|
|
1811
1886
|
* - **`delete(id)` drops a definition by id**; an absent id is a no-op (no throw).
|
|
1812
1887
|
*
|
|
1813
|
-
* The public surface is
|
|
1888
|
+
* The public surface is exactly `get` / `set` / `delete` — no extra members (the method
|
|
1814
1889
|
* bijection with {@link DefinitionStoreInterface}).
|
|
1815
1890
|
*
|
|
1816
1891
|
* @example
|
|
@@ -1848,10 +1923,10 @@ var DatabaseDefinitionStore = class {
|
|
|
1848
1923
|
return isDatabaseDefinition(row.definition) ? row.definition : void 0;
|
|
1849
1924
|
}
|
|
1850
1925
|
/**
|
|
1851
|
-
* Inserts or replaces a definition under its
|
|
1926
|
+
* Inserts or replaces a definition under its own `id` — the written row is `{ id, definition }`.
|
|
1852
1927
|
*
|
|
1853
1928
|
* @param definition - The config to persist; its own `id` is the row key (no separate id param)
|
|
1854
|
-
* @returns A promise settling
|
|
1929
|
+
* @returns A promise settling after the row is written
|
|
1855
1930
|
*/
|
|
1856
1931
|
async set(definition) {
|
|
1857
1932
|
await this.#table.set({
|
|
@@ -1863,7 +1938,7 @@ var DatabaseDefinitionStore = class {
|
|
|
1863
1938
|
* Drops the definition stored under `id`.
|
|
1864
1939
|
*
|
|
1865
1940
|
* @param id - The database id to drop; an absent id is a no-op, never a throw
|
|
1866
|
-
* @returns A promise settling
|
|
1941
|
+
* @returns A promise settling after no row is stored under `id`
|
|
1867
1942
|
*/
|
|
1868
1943
|
async delete(id) {
|
|
1869
1944
|
await this.#table.remove(id);
|
|
@@ -1872,7 +1947,8 @@ var DatabaseDefinitionStore = class {
|
|
|
1872
1947
|
//#endregion
|
|
1873
1948
|
//#region src/core/databases/DatabaseResolver.ts
|
|
1874
1949
|
/**
|
|
1875
|
-
* Resolves database
|
|
1950
|
+
* Resolves a database definition into a cached live handle for a database tool, over the tool's
|
|
1951
|
+
* live handles, its stored definitions, its driver registry, and an optional key generator.
|
|
1876
1952
|
*
|
|
1877
1953
|
* @example
|
|
1878
1954
|
* ```ts
|
|
@@ -1939,7 +2015,8 @@ var DatabaseResolver = class {
|
|
|
1939
2015
|
this.#handles.delete(id);
|
|
1940
2016
|
}
|
|
1941
2017
|
/**
|
|
1942
|
-
* Resolves a cached
|
|
2018
|
+
* Resolves a live database by id — the cached handle when one exists, otherwise a database
|
|
2019
|
+
* constructed from its stored definition.
|
|
1943
2020
|
*
|
|
1944
2021
|
* @param id - Database definition id
|
|
1945
2022
|
* @returns The cached or newly constructed live database
|
|
@@ -1974,15 +2051,17 @@ var DatabaseResolver = class {
|
|
|
1974
2051
|
//#endregion
|
|
1975
2052
|
//#region src/core/factories.ts
|
|
1976
2053
|
/**
|
|
1977
|
-
* Wraps a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the
|
|
1978
|
-
* adapter that lets a `function`-form task run a `@orkestrel/tool` tool
|
|
2054
|
+
* Wraps a registered tool as a {@link WorkflowFunction} (`@orkestrel/workflow`) — the opt-in
|
|
2055
|
+
* adapter that lets a `function`-form task run a `@orkestrel/tool` tool by name.
|
|
1979
2056
|
*
|
|
1980
2057
|
* @remarks
|
|
1981
2058
|
* Composes into a caller's `WorkflowOptions.functions` registry like any other behavior
|
|
1982
2059
|
* (`{ publish: createToolFunction(tools, 'publish') }`); the pure workflow runner has no
|
|
1983
2060
|
* knowledge of tools itself. The returned function executes `name` against `tools` with the
|
|
1984
2061
|
* task's `controller.input` as the call arguments. The adapter resolves the live tool, calls its
|
|
1985
|
-
* `execute` method directly, and deep-gates the returned unknown through `parseJSONValue`.
|
|
2062
|
+
* `execute` method directly, and deep-gates the returned unknown through `parseJSONValue`. The
|
|
2063
|
+
* execution context it passes carries the task's own `controller.signal`, so a handler that
|
|
2064
|
+
* reads `context.signal` observes the caller's cancellation.
|
|
1986
2065
|
* Genuine execution throws retain identity. An unregistered name or non-JSON return throws this
|
|
1987
2066
|
* package's typed `TOOL` `ToolboxError`.
|
|
1988
2067
|
*
|
|
@@ -2007,7 +2086,7 @@ function createToolFunction(tools, name) {
|
|
|
2007
2086
|
return async (controller) => {
|
|
2008
2087
|
const tool = tools.tool(name);
|
|
2009
2088
|
if (tool === void 0) throw new ToolboxError("TOOL", `tool '${name}' is not registered`, { tool: name });
|
|
2010
|
-
const result = (0, _orkestrel_contract.parseJSONValue)(await tool.execute(controller.input));
|
|
2089
|
+
const result = (0, _orkestrel_contract.parseJSONValue)(await tool.execute(controller.input, { signal: controller.signal }));
|
|
2011
2090
|
if (result === void 0) throw new ToolboxError("TOOL", `tool '${name}' returned a non-JSON value`, { tool: name });
|
|
2012
2091
|
return result;
|
|
2013
2092
|
};
|
|
@@ -2149,7 +2228,9 @@ function createWorkflowFunctions(runner, options) {
|
|
|
2149
2228
|
return Object.freeze(composed);
|
|
2150
2229
|
}
|
|
2151
2230
|
/**
|
|
2152
|
-
* Compiles the lenient workflow draft contract
|
|
2231
|
+
* Compiles the lenient workflow draft contract {@link createWorkflowTool} parses an authored tree
|
|
2232
|
+
* with — a `ContractInterface` over a {@link WorkflowDraft}, whose `id` and `name` are optional at
|
|
2233
|
+
* the workflow, phase, and task levels.
|
|
2153
2234
|
*
|
|
2154
2235
|
* @returns The compiled {@link import('./types.js').WorkflowDraft} contract
|
|
2155
2236
|
*/
|
|
@@ -2157,34 +2238,33 @@ function createWorkflowDraftContract() {
|
|
|
2157
2238
|
return (0, _orkestrel_contract.createContract)(workflowDraftShape);
|
|
2158
2239
|
}
|
|
2159
2240
|
/**
|
|
2160
|
-
* Wraps a {@link WorkflowDefinition} as an LLM-callable tool — it
|
|
2161
|
-
*
|
|
2162
|
-
*
|
|
2163
|
-
*
|
|
2164
|
-
* named functions and native checkpoint store.
|
|
2241
|
+
* Wraps a {@link WorkflowDefinition} as an LLM-callable tool — it advertises the flat authoring
|
|
2242
|
+
* shape (`{ name?, steps: [{ name }] }`) as its `parameters`, and its handler completes the
|
|
2243
|
+
* authored blob, validates it against the strict contract, and runs it through `runner`, forwarding
|
|
2244
|
+
* the caller's optional named functions and native checkpoint store.
|
|
2165
2245
|
*
|
|
2166
2246
|
* @remarks
|
|
2167
2247
|
* A plain `ToolManagerInterface`-compatible tool (`@orkestrel/tool`), reproducing
|
|
2168
2248
|
* Toolbox's flat / draft / full authoring contract, strict soundness gate, and lineage guard.
|
|
2169
|
-
* It is
|
|
2249
|
+
* It is also the propagation carrier
|
|
2170
2250
|
* {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
|
|
2171
2251
|
* handler receives only the model-supplied `args`, the immutable lineage is closed over at bind
|
|
2172
2252
|
* time. The authored target is appended and its zero-based depth derived from workflow tags.
|
|
2173
2253
|
*
|
|
2174
|
-
* **Widened authoring surface (additive — the canonical contract
|
|
2175
|
-
* unchanged).** A 2B model reliably
|
|
2176
|
-
*
|
|
2177
|
-
* So the tool
|
|
2254
|
+
* **Widened authoring surface (additive — the canonical contract and runner stay strict and
|
|
2255
|
+
* unchanged).** A 2B model reliably calls the tool but cannot reliably emit the full nested
|
|
2256
|
+
* {@link WorkflowDefinition}, whose every `id` and `name` is required across an all-or-nothing
|
|
2257
|
+
* tree. So the tool accepts the authoring forms that follow and converges them on the same strict
|
|
2178
2258
|
* `createWorkflowContract` gate before running (soundness preserved):
|
|
2179
|
-
* - the
|
|
2259
|
+
* - the flat shape `{ name?, steps: [{ name }] }` — the advertised `parameters` (the simplest
|
|
2180
2260
|
* form, {@link import('./helpers.js').expandSteps}'d into one one-task phase per step);
|
|
2181
|
-
* - a nested
|
|
2261
|
+
* - a nested draft with any `id`/`name` omitted — {@link createWorkflowDraftContract}-parsed then
|
|
2182
2262
|
* {@link import('./helpers.js').completeDraft}'d (missing ids synthesized positionally);
|
|
2183
2263
|
* - the full nested {@link WorkflowDefinition} — the advanced escape-hatch, accepted as the draft
|
|
2184
2264
|
* super-set.
|
|
2185
2265
|
*
|
|
2186
2266
|
* The universal tool-handler contract: returns the plain run summary
|
|
2187
|
-
* (`{ status, count, durable?, fault? }`) on success,
|
|
2267
|
+
* (`{ status, count, durable?, fault? }`) on success, throws a typed `ToolboxError` on Toolbox
|
|
2188
2268
|
* boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
|
|
2189
2269
|
* (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
|
|
2190
2270
|
* top-level `error`, so nothing escapes the run. `options.lineage` is the propagation carrier
|
|
@@ -2198,6 +2278,34 @@ function createWorkflowDraftContract() {
|
|
|
2198
2278
|
* @returns A `ToolInterface` (named {@link import('./constants.js').WORKFLOW_TOOL_NAME}) whose
|
|
2199
2279
|
* `parameters` advertise the flat authoring schema
|
|
2200
2280
|
*
|
|
2281
|
+
* @example Authoring and running a workflow through the tool with a real ToolManager
|
|
2282
|
+
* ```ts
|
|
2283
|
+
* import { createWorkflowTool } from '@orkestrel/toolbox'
|
|
2284
|
+
* import { createToolManager } from '@orkestrel/tool'
|
|
2285
|
+
* import { createWorkflowRunner } from '@orkestrel/workflow'
|
|
2286
|
+
* import type { WorkflowDefinition } from '@orkestrel/workflow'
|
|
2287
|
+
*
|
|
2288
|
+
* const definition: WorkflowDefinition = { id: 'release', name: 'Release', phases: [] }
|
|
2289
|
+
* const runner = createWorkflowRunner()
|
|
2290
|
+
* const functions = {
|
|
2291
|
+
* compile: () => 'compiled',
|
|
2292
|
+
* publish: () => 'published',
|
|
2293
|
+
* }
|
|
2294
|
+
* const tool = createWorkflowTool(definition, runner, { functions })
|
|
2295
|
+
*
|
|
2296
|
+
* const tools = createToolManager()
|
|
2297
|
+
* tools.add(tool)
|
|
2298
|
+
*
|
|
2299
|
+
* // A small model authors the simple flat shape — no ids/names required.
|
|
2300
|
+
* const result = await tools.execute({
|
|
2301
|
+
* id: 'call-1',
|
|
2302
|
+
* name: 'workflow',
|
|
2303
|
+
* arguments: { name: 'release', steps: [{ name: 'compile' }, { name: 'publish' }] },
|
|
2304
|
+
* })
|
|
2305
|
+
* if (!result.success) throw new Error(result.error)
|
|
2306
|
+
* result.value // { status: 'completed', count: 2 } — the single-level envelope; no nested { id, name, value }
|
|
2307
|
+
* ```
|
|
2308
|
+
*
|
|
2201
2309
|
* @example
|
|
2202
2310
|
* ```ts
|
|
2203
2311
|
* import { createWorkflowTool } from '@src/core'
|
|
@@ -2266,42 +2374,35 @@ function createWorkflowTool(definition, runner, options) {
|
|
|
2266
2374
|
});
|
|
2267
2375
|
}
|
|
2268
2376
|
/**
|
|
2269
|
-
* Builds an LLM-callable workspace-editing tool — it
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2273
|
-
* returning the plain result. A malformed operation throws this package's `ToolboxError`; a
|
|
2274
|
-
* genuine workspace-domain failure propagates `@orkestrel/workspace`'s typed `WorkspaceError`.
|
|
2275
|
-
* EITHER drives a caller-supplied {@link WorkspaceToolOptions.manager} directly, OR
|
|
2276
|
-
* constructs a fresh `WorkspaceManagerInterface` (`@orkestrel/workspace`) over
|
|
2277
|
-
* {@link import('./types.js').WorkspaceToolOptions.store} (through `@orkestrel/workspace`'s
|
|
2278
|
-
* `createWorkspaceManager`); neither given constructs a manager backed by
|
|
2279
|
-
* `@orkestrel/workspace`'s in-memory store default.
|
|
2377
|
+
* Builds an LLM-callable workspace-editing tool — it advertises the `operation`-discriminated union
|
|
2378
|
+
* ({@link import('./shapers.js').workspaceToolShape}) as its `parameters`, and its handler parses
|
|
2379
|
+
* the model-supplied args against that contract and dispatches the matched operation against the
|
|
2380
|
+
* manager's active workspace, returning the plain result.
|
|
2280
2381
|
*
|
|
2281
2382
|
* @remarks
|
|
2282
|
-
*
|
|
2383
|
+
* Manager-driven: every edit / read op (read / list / has / search / replace / write / splice /
|
|
2283
2384
|
* prepend / append / move / remove) targets `manager.active`, so the model edits whichever
|
|
2284
2385
|
* workspace is active and a host can re-point it (`WorkspaceManagerInterface.switch`) between
|
|
2285
|
-
* turns.
|
|
2386
|
+
* turns. The registry ops make the model self-sufficient: `workspaces` lists the registered
|
|
2286
2387
|
* workspaces (each `{ id, files, active }`) so it can discover an id, and `switch` re-points the
|
|
2287
2388
|
* active workspace by id (lenient — an unknown id is a no-op reporting `switched: false`, never a
|
|
2288
2389
|
* throw).
|
|
2289
2390
|
*
|
|
2290
|
-
*
|
|
2291
|
-
* remove / replace) run when `manager.active` is `undefined`
|
|
2292
|
-
* workspace (`manager.add()`) so the model can start writing; a pure-
|
|
2293
|
-
* has / search) against no active workspace returns the
|
|
2391
|
+
* No-active rule (the ergonomic seam): a writing op (write / splice / prepend / append / move /
|
|
2392
|
+
* remove / replace) run when `manager.active` is `undefined` auto-creates and activates a default
|
|
2393
|
+
* workspace (`manager.add()`) so the model can start writing; a pure-read op (read / list /
|
|
2394
|
+
* has / search) against no active workspace returns the empty result (`undefined` / `[]` /
|
|
2294
2395
|
* `false`), never creating one and never throwing.
|
|
2295
2396
|
*
|
|
2296
2397
|
* The handler conforms to the universal tool-handler contract: it `contract.parse`s
|
|
2297
|
-
* the args,
|
|
2298
|
-
* operation), else `switch`es on `op.operation` and
|
|
2398
|
+
* the args, throws a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
|
|
2399
|
+
* operation), else `switch`es on `op.operation` and returns the plain result — letting a
|
|
2299
2400
|
* `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` / `RANGE`)
|
|
2300
|
-
*
|
|
2401
|
+
* propagate uncaught. The range edit is the flat `'splice'` op: its flat caret integers are
|
|
2301
2402
|
* reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
|
|
2302
2403
|
* ranged `write`.
|
|
2303
2404
|
*
|
|
2304
|
-
* @param options - `manager` (drive directly)
|
|
2405
|
+
* @param options - `manager` (drive directly) or `store` (build a manager over it); neither ⇒
|
|
2305
2406
|
* an in-memory-backed manager (see {@link import('./types.js').WorkspaceToolOptions})
|
|
2306
2407
|
* @returns A `ToolInterface` (named {@link import('./constants.js').WORKSPACE_TOOL_NAME} by default)
|
|
2307
2408
|
*
|
|
@@ -2419,8 +2520,8 @@ function createWorkspaceTool(options) {
|
|
|
2419
2520
|
});
|
|
2420
2521
|
}
|
|
2421
2522
|
/**
|
|
2422
|
-
* Builds an LLM-callable sub-agent delegation tool — resolves a live, seeded `AgentInterface`
|
|
2423
|
-
* from `registry` and runs it to completion for
|
|
2523
|
+
* Builds an LLM-callable sub-agent delegation tool — it resolves a live, seeded `AgentInterface`
|
|
2524
|
+
* from `registry` and runs it to completion for one delegated `task`.
|
|
2424
2525
|
*
|
|
2425
2526
|
* @remarks
|
|
2426
2527
|
* The universal tool-handler contract: validates the call args against
|
|
@@ -2428,10 +2529,10 @@ function createWorkspaceTool(options) {
|
|
|
2428
2529
|
* sub-agent's conversation as a single `user` message; `provider` / `tools` / `system` fall
|
|
2429
2530
|
* back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent through
|
|
2430
2531
|
* `registry.build`, runs it with `agent.generate()`, and returns the settled
|
|
2431
|
-
* `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call,
|
|
2532
|
+
* `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, throws a typed `TOOL`
|
|
2432
2533
|
* {@link import('./errors.js').ToolboxError}; a delegation that would exceed
|
|
2433
2534
|
* {@link import('./constants.js').AGENT_TOOL_DEPTH}, or re-enter an already-delegated agent (a
|
|
2434
|
-
* cycle),
|
|
2535
|
+
* cycle), throws a typed `DEPTH` {@link import('./errors.js').ToolboxError}, each isolated
|
|
2435
2536
|
* by the `ToolManagerInterface` into the canonical tool result's top-level `error`.
|
|
2436
2537
|
*
|
|
2437
2538
|
* `AgentInterface` (`@orkestrel/agent`) exposes no teardown method — a bound sub-agent's
|
|
@@ -2503,9 +2604,8 @@ function createAgentTool(registry, options) {
|
|
|
2503
2604
|
});
|
|
2504
2605
|
}
|
|
2505
2606
|
/**
|
|
2506
|
-
* Builds an LLM-callable tool that returns the
|
|
2507
|
-
* name — the counterpart to the lean `summary` the other tools in this package advertise
|
|
2508
|
-
* (`AGENT_TOOL_SUMMARY` / `WORKFLOW_TOOL_SUMMARY` / `WORKSPACE_TOOL_SUMMARY`).
|
|
2607
|
+
* Builds an LLM-callable tool that returns the full `description` of another registered tool by
|
|
2608
|
+
* name — the counterpart to the lean `summary` the other tools in this package advertise.
|
|
2509
2609
|
*
|
|
2510
2610
|
* @remarks
|
|
2511
2611
|
* `ToolManagerInterface.definitions()` (`@orkestrel/tool`) advertises `tool.summary ??
|
|
@@ -2516,8 +2616,8 @@ function createAgentTool(registry, options) {
|
|
|
2516
2616
|
* tool has no `description` of its own, then a placeholder when it has neither).
|
|
2517
2617
|
*
|
|
2518
2618
|
* The universal tool-handler contract: validates the call args against
|
|
2519
|
-
* {@link import('./shapers.js').describeToolShape},
|
|
2520
|
-
* success,
|
|
2619
|
+
* {@link import('./shapers.js').describeToolShape}, returns the plain description string on
|
|
2620
|
+
* success, throws a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
|
|
2521
2621
|
* or an unknown tool name.
|
|
2522
2622
|
*
|
|
2523
2623
|
* @param tools - The `ToolManagerInterface` (`@orkestrel/tool`) whose registered tools this
|
|
@@ -2554,20 +2654,23 @@ function createDescribeTool(tools) {
|
|
|
2554
2654
|
});
|
|
2555
2655
|
}
|
|
2556
2656
|
/**
|
|
2557
|
-
* Builds an LLM-callable form tool — the
|
|
2558
|
-
*
|
|
2657
|
+
* Builds an LLM-callable form tool — the ask side of the terminal seam. It asks a multi-field form
|
|
2658
|
+
* and blocks until the addressed terminal answers, returning the resolved values record.
|
|
2559
2659
|
*
|
|
2560
2660
|
* @remarks
|
|
2561
2661
|
* The universal tool-handler contract: validates the call args against
|
|
2562
2662
|
* {@link import('./shapers.js').promptToolShape}, parses the call's schema through
|
|
2563
2663
|
* `@orkestrel/form`, constructs the live form, and passes it to `TerminalManagerInterface.ask`.
|
|
2564
|
-
* `from` is
|
|
2664
|
+
* `from` is fixed at construction
|
|
2565
2665
|
* ({@link import('./types.js').PromptToolOptions.from}) — never read from the model-supplied
|
|
2566
|
-
* args — so a model cannot spoof which terminal is asking. A form
|
|
2666
|
+
* args — so a model cannot spoof which terminal is asking. A form cycle rejects with
|
|
2567
2667
|
* `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
|
|
2568
2668
|
* {@link import('./errors.js').ToolboxError}; an expired form re-surfaces as `EXPIRE`; an
|
|
2569
2669
|
* unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
|
|
2570
|
-
* and how many brokers the manager holds (`manager.count`).
|
|
2670
|
+
* and how many brokers the manager holds (`manager.count`). A schema `parseForm` refuses — an
|
|
2671
|
+
* unknown control, a duplicate field name, a `'select'` or `'checkbox'` field without usable
|
|
2672
|
+
* `choices` — throws a typed `TOOL` error up front, so a malformed schema never parks as a form
|
|
2673
|
+
* nobody can answer.
|
|
2571
2674
|
*
|
|
2572
2675
|
* @param options - The live manager, the fixed `from` identity, and advertised overrides (see
|
|
2573
2676
|
* {@link import('./types.js').PromptToolOptions})
|
|
@@ -2625,9 +2728,8 @@ function createPromptTool(options) {
|
|
|
2625
2728
|
});
|
|
2626
2729
|
}
|
|
2627
2730
|
/**
|
|
2628
|
-
* Builds an LLM-callable answer tool — the
|
|
2629
|
-
* addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of
|
|
2630
|
-
* them by id.
|
|
2731
|
+
* Builds an LLM-callable answer tool — the answer side of the terminal seam. It lists the forms
|
|
2732
|
+
* addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of them by id.
|
|
2631
2733
|
*
|
|
2632
2734
|
* @remarks
|
|
2633
2735
|
* The universal tool-handler contract: validates the call args against
|
|
@@ -2637,10 +2739,10 @@ function createPromptTool(options) {
|
|
|
2637
2739
|
* and applies it through
|
|
2638
2740
|
* `TerminalManagerInterface.answer` — a rejected / unknown / unresolvable outcome
|
|
2639
2741
|
* (`TerminalAnswerResult.error`) re-surfaces as a typed `ANSWER` `ToolboxError`; success returns
|
|
2640
|
-
* `{ answered: id }`. `to` is
|
|
2742
|
+
* `{ answered: id }`. `to` is fixed at construction
|
|
2641
2743
|
* ({@link import('./types.js').AnswerToolOptions.to}) — never read from the model-supplied args —
|
|
2642
2744
|
* so a model cannot spoof which terminal it is answering for. Concurrent answerers racing on one
|
|
2643
|
-
* endpoint are
|
|
2745
|
+
* endpoint are first-write-wins — a late answer to an already-settled form returns a typed
|
|
2644
2746
|
* `ANSWER` `ToolboxError` (surfaced as a 422 over HTTP).
|
|
2645
2747
|
*
|
|
2646
2748
|
* @param options - The live manager, the fixed `to` identity, and advertised overrides (see
|
|
@@ -2693,8 +2795,8 @@ function createAnswerTool(options) {
|
|
|
2693
2795
|
}
|
|
2694
2796
|
/**
|
|
2695
2797
|
* Creates the in-memory {@link DefinitionStoreInterface} — a process-lifetime `Map` of database
|
|
2696
|
-
* definitions, the
|
|
2697
|
-
*
|
|
2798
|
+
* definitions, the default store the database and relation tools persist their `DatabaseDefinition`
|
|
2799
|
+
* configs through.
|
|
2698
2800
|
*
|
|
2699
2801
|
* @returns A {@link DefinitionStoreInterface}
|
|
2700
2802
|
*
|
|
@@ -2741,7 +2843,7 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2741
2843
|
* @remarks
|
|
2742
2844
|
* The universal tool-handler contract: validates the call args against
|
|
2743
2845
|
* {@link import('./shapers.js').databaseToolShape}, dispatches to the matching operation, and
|
|
2744
|
-
*
|
|
2846
|
+
* returns a plain result on success. A database is resolved lazily and cached for the tool's
|
|
2745
2847
|
* lifetime — `'create'` mints one from `tables` ({@link import('./compilers.js').expandTables}) and
|
|
2746
2848
|
* a registered `driver` key ({@link import('./types.js').DatabaseToolOptions.drivers}, default
|
|
2747
2849
|
* `{ memory: () => createMemoryDriver() }`); any other operation addressing an uncached id falls
|
|
@@ -2766,7 +2868,7 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2766
2868
|
* ({@link import('./helpers.js').inferDatabaseCode}); an `ToolboxError` thrown by this tool's own
|
|
2767
2869
|
* guards passes through unwrapped.
|
|
2768
2870
|
*
|
|
2769
|
-
* A lazily re-minted database over the
|
|
2871
|
+
* A lazily re-minted database over the default in-memory driver yields an empty database — only
|
|
2770
2872
|
* the {@link import('./types.js').DatabaseDefinition} schema persists in `store`, never rows;
|
|
2771
2873
|
* durable rows need a persistent driver factory registered in
|
|
2772
2874
|
* {@link import('./types.js').DatabaseToolOptions.drivers}. `'destroy'` closes whatever handle is
|
|
@@ -2774,7 +2876,7 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2774
2876
|
* {@link import('./types.js').DatabaseToolOptions.databases} handle — the embedder relinquishes
|
|
2775
2877
|
* that handle's lifecycle to this tool for any id it wires in. This tool assumes the
|
|
2776
2878
|
* single-writer, non-reentrant model `@orkestrel/database` itself assumes — concurrent calls
|
|
2777
|
-
* against one id are
|
|
2879
|
+
* against one id are not serialized by this tool. `'get'` is uncapped by
|
|
2778
2880
|
* {@link import('./types.js').DatabaseToolOptions.limit} (bounded only by the caller's `key` array
|
|
2779
2881
|
* size), unlike `'records'` / `'find'` / `'links'`.
|
|
2780
2882
|
*
|
|
@@ -2786,11 +2888,14 @@ function createDatabaseDefinitionStore(driver = (0, _orkestrel_database.createMe
|
|
|
2786
2888
|
* import { createDatabaseTool } from '@src/core'
|
|
2787
2889
|
*
|
|
2788
2890
|
* const tool = createDatabaseTool()
|
|
2789
|
-
* await tool.execute(
|
|
2790
|
-
*
|
|
2791
|
-
*
|
|
2792
|
-
*
|
|
2793
|
-
* }
|
|
2891
|
+
* await tool.execute(
|
|
2892
|
+
* {
|
|
2893
|
+
* operation: 'create',
|
|
2894
|
+
* id: 'shop',
|
|
2895
|
+
* tables: { products: { columns: { name: 'string', price: 'number' } } },
|
|
2896
|
+
* },
|
|
2897
|
+
* { signal: new AbortController().signal },
|
|
2898
|
+
* )
|
|
2794
2899
|
* ```
|
|
2795
2900
|
*/
|
|
2796
2901
|
function createDatabaseTool(options = {}) {
|
|
@@ -2950,14 +3055,14 @@ function createDatabaseTool(options = {}) {
|
|
|
2950
3055
|
* The universal tool-handler contract: validates the call args against
|
|
2951
3056
|
* {@link import('./shapers.js').relationToolShape}, resolves the addressed
|
|
2952
3057
|
* {@link import('@orkestrel/relation').RelationManagerInterface} — an explicit `manager` field
|
|
2953
|
-
* must match a key of {@link import('./types.js').RelationToolOptions.managers}, an
|
|
2954
|
-
* resolves to the
|
|
3058
|
+
* must match a key of {@link import('./types.js').RelationToolOptions.managers}, an omitted one
|
|
3059
|
+
* resolves to the sole registered manager, either miss throwing a typed `TOOL`
|
|
2955
3060
|
* {@link import('./errors.js').ToolboxError}
|
|
2956
3061
|
* ({@link import('./helpers.js').resolveRelationManager}) — then resolves `model` against it
|
|
2957
3062
|
* ({@link import('./helpers.js').resolveRelationModel}, same typed-`TOOL`-on-miss shape), and
|
|
2958
|
-
* dispatches to the matched operation,
|
|
3063
|
+
* dispatches to the matched operation, returning a plain result on success.
|
|
2959
3064
|
*
|
|
2960
|
-
* `'load'` / `'find'` expand the call's
|
|
3065
|
+
* `'load'` / `'find'` expand the call's flat dot-path `include` list into a live
|
|
2961
3066
|
* `@orkestrel/relation` `Include` tree through {@link import('./helpers.js').expandInclude}, capped
|
|
2962
3067
|
* at {@link import('./types.js').RelationToolOptions.depth} (default
|
|
2963
3068
|
* {@link import('./constants.js').RELATION_TOOL_DEPTH}) — a path exceeding the cap, or carrying an
|
|
@@ -2967,7 +3072,7 @@ function createDatabaseTool(options = {}) {
|
|
|
2967
3072
|
* result to {@link import('./types.js').RelationToolOptions.limit} (default
|
|
2968
3073
|
* {@link import('./constants.js').RELATION_TOOL_LIMIT}), taken through
|
|
2969
3074
|
* {@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
|
|
3075
|
+
* report `truncated`; `'links'` (which has no upstream pagination) fetches the full linked-key
|
|
2971
3076
|
* list and slices/truncates it the same way. `'link'` / `'unlink'` write / remove one `through` junction
|
|
2972
3077
|
* row.
|
|
2973
3078
|
*
|
|
@@ -2986,7 +3091,10 @@ function createDatabaseTool(options = {}) {
|
|
|
2986
3091
|
* import { createRelationTool } from '@src/core'
|
|
2987
3092
|
*
|
|
2988
3093
|
* const tool = createRelationTool({ managers: { shop: manager } })
|
|
2989
|
-
* await tool.execute(
|
|
3094
|
+
* await tool.execute(
|
|
3095
|
+
* { operation: 'load', model: 'accounts', key: 'acc1', include: ['contacts'] },
|
|
3096
|
+
* { signal: new AbortController().signal },
|
|
3097
|
+
* )
|
|
2990
3098
|
* ```
|
|
2991
3099
|
*/
|
|
2992
3100
|
function createRelationTool(options) {
|
|
@@ -3067,44 +3175,44 @@ function createRelationTool(options) {
|
|
|
3067
3175
|
});
|
|
3068
3176
|
}
|
|
3069
3177
|
/**
|
|
3070
|
-
* Builds a standalone LLM-callable tool that infers a JSON Schema from example values — the
|
|
3071
|
-
*
|
|
3072
|
-
* {@link createEndpointTool}, wraps one
|
|
3178
|
+
* Builds a standalone LLM-callable tool that infers a JSON Schema from example values — the utility
|
|
3179
|
+
* half of the bridge from an existing API or database into an MCP tool (the other half,
|
|
3180
|
+
* {@link createEndpointTool}, wraps one concrete endpoint).
|
|
3073
3181
|
*
|
|
3074
3182
|
* @remarks
|
|
3075
3183
|
* The universal tool-handler contract: validates the call args against
|
|
3076
3184
|
* {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
|
|
3077
3185
|
* booleans, `candidates` an optional array), infers a schema through `@orkestrel/contract`'s
|
|
3078
3186
|
* `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` through `schemaToObject` (mirrors
|
|
3079
|
-
* the tool-parameters convention every other `create*Tool` factory advertises), and
|
|
3187
|
+
* the tool-parameters convention every other `create*Tool` factory advertises), and returns the
|
|
3080
3188
|
* resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
|
|
3081
3189
|
* `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
|
|
3082
3190
|
* {@link import('./errors.js').ToolboxError}.
|
|
3083
3191
|
*
|
|
3084
|
-
* When `candidates` is
|
|
3085
|
-
* this array existed. When `candidates` is
|
|
3086
|
-
* compiles a
|
|
3087
|
-
* `schemaToShape`,
|
|
3192
|
+
* When `candidates` is absent, the return is the bare parameters record — unchanged from before
|
|
3193
|
+
* this array existed. When `candidates` is present (any array, including empty), the handler
|
|
3194
|
+
* compiles a separate per-call contract from the raw inferred schema (through `@orkestrel/contract`'s
|
|
3195
|
+
* `schemaToShape`, not the `schemaToObject`-wrapped parameters — a bare-value sample checks a
|
|
3088
3196
|
* bare-value candidate) and returns `{ parameters, checks }`, one check per candidate at the same
|
|
3089
|
-
* index. Every entry has a
|
|
3090
|
-
* when `valid` is `false`: `valid` is the
|
|
3091
|
-
*
|
|
3092
|
-
* string slot) — here a conformance report answers "does this value conform
|
|
3093
|
-
* string slot is `valid: false`, full stop. `coercible` answers a
|
|
3094
|
-
*
|
|
3197
|
+
* index. Every entry has a uniform shape — `{ index, valid, coercible }`, with `faults` added only
|
|
3198
|
+
* when `valid` is `false`: `valid` is the strict guard verdict (`checker.is(candidate)`), the
|
|
3199
|
+
* opposite of {@link createEndpointTool}'s enforcement, which coerces (`7` becomes `'7'` for a
|
|
3200
|
+
* string slot) — here a conformance report answers "does this value conform as-is": `7` against a
|
|
3201
|
+
* string slot is `valid: false`, full stop. `coercible` answers a separate question — "would the
|
|
3202
|
+
* normalizing parse accept this value", that is would {@link createEndpointTool}'s default enforcement
|
|
3095
3203
|
* admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
|
|
3096
3204
|
* `valid`; by the house parse/guard round-trip, a `valid: true` entry is
|
|
3097
|
-
*
|
|
3205
|
+
* always also `coercible: true`. `@orkestrel/contract`'s `explain` mirrors the normalizing
|
|
3098
3206
|
* `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
|
|
3099
|
-
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`:
|
|
3207
|
+
* against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: empty faults, because
|
|
3100
3208
|
* the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
|
|
3101
|
-
* therefore only ever populates for a
|
|
3209
|
+
* therefore only ever populates for a non-coercible mismatch — a wrong type the parse can't coerce
|
|
3102
3210
|
* (a boolean in a string slot), a missing required key, or an out-of-enum value — where
|
|
3103
3211
|
* `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
|
|
3104
|
-
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches
|
|
3105
|
-
* and yields a bounded, non-throwing per-candidate verdict; a
|
|
3106
|
-
* throwing-getter `Proxy`) never reaches the checker at all — it fails the
|
|
3107
|
-
* against {@link import('./shapers.js').inferToolShape} and rejects the
|
|
3212
|
+
* JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
|
|
3213
|
+
* and yields a bounded, non-throwing per-candidate verdict; a non-JSON-safe candidate (for example a
|
|
3214
|
+
* throwing-getter `Proxy`) never reaches the checker at all — it fails the outer `args` parse
|
|
3215
|
+
* against {@link import('./shapers.js').inferToolShape} and rejects the whole call with the same
|
|
3108
3216
|
* `TOOL` {@link import('./errors.js').ToolboxError} a malformed `samples`/`format`/`enum` throws,
|
|
3109
3217
|
* with no per-candidate verdict produced.
|
|
3110
3218
|
*
|
|
@@ -3183,36 +3291,38 @@ function createInferTool(options) {
|
|
|
3183
3291
|
});
|
|
3184
3292
|
}
|
|
3185
3293
|
/**
|
|
3186
|
-
* Wraps one
|
|
3187
|
-
* `ToolInterface` — the endpoint half of the
|
|
3188
|
-
* {@link createInferTool}, is a standalone inference utility).
|
|
3294
|
+
* Wraps one concrete endpoint ({@link import('./types.js').EndpointDefinition}) as an LLM-callable
|
|
3295
|
+
* `ToolInterface` — the endpoint half of the bridge from an existing API or database into an MCP
|
|
3296
|
+
* tool (the other half, {@link createInferTool}, is a standalone inference utility).
|
|
3189
3297
|
*
|
|
3190
3298
|
* @remarks
|
|
3191
|
-
* `parameters` is inferred
|
|
3299
|
+
* `parameters` is inferred once at construction from `definition.samples` through
|
|
3192
3300
|
* `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
|
|
3193
3301
|
* `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` through `schemaToObject` —
|
|
3194
|
-
* the
|
|
3195
|
-
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the
|
|
3196
|
-
* `@orkestrel/contract`'s `schemaToShape` compiles it
|
|
3302
|
+
* the same object-rooted schema is both the advertised `parameters` and, by default
|
|
3303
|
+
* ({@link import('./types.js').EndpointToolOptions.validate} `true`), the enforced contract:
|
|
3304
|
+
* `@orkestrel/contract`'s `schemaToShape` compiles it once (through `createContract`) into a
|
|
3197
3305
|
* `ContractInterface` whose `.parse` runs on every call's `args` before `definition.execute` — a
|
|
3198
|
-
*
|
|
3306
|
+
* normalizing parse, not a strict type check: a scalar is coerced to its inferred type where the
|
|
3199
3307
|
* house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
|
|
3200
|
-
* `'false'`/`1`/`0`), so `definition.execute` receives the
|
|
3308
|
+
* `'false'`/`1`/`0`), so `definition.execute` receives the coerced value (for example `7` sent for a
|
|
3201
3309
|
* string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
|
|
3202
|
-
* a record — a required key missing, or a value not coercible to its slot's type —
|
|
3310
|
+
* a record — a required key missing, or a value not coercible to its slot's type — throws a
|
|
3203
3311
|
* typed `TOOL` {@link import('./errors.js').ToolboxError} carrying the compiled contract's
|
|
3204
3312
|
* structured `explain` faults, and `definition.execute` is never called. `format` annotations are
|
|
3205
|
-
*
|
|
3313
|
+
* never asserted, and a key outside the closed inferred schema is silently dropped rather than
|
|
3206
3314
|
* rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
|
|
3207
|
-
* `validate: false`, the tool's `execute`
|
|
3208
|
-
* `definition.execute`
|
|
3209
|
-
*
|
|
3315
|
+
* `validate: false`, the tool's `execute` passes through the model-supplied `args` to
|
|
3316
|
+
* `definition.execute` without re-validation — the raw-passthrough opt-out. The execution context
|
|
3317
|
+
* the tool's `execute` receives reaches `definition.execute` unchanged whichever way `args` are
|
|
3318
|
+
* handled, so a handler that reads `context.signal` observes the caller's cancellation. Either way,
|
|
3319
|
+
* the definition's return flows back as the tool call's plain result; a throw propagates uncaught,
|
|
3210
3320
|
* isolated by the `ToolManagerInterface` (`@orkestrel/tool`) into the canonical error envelope
|
|
3211
3321
|
* — never caught or re-wrapped here.
|
|
3212
3322
|
*
|
|
3213
3323
|
* @param definition - The endpoint's identity, non-empty samples, and local handler (see
|
|
3214
3324
|
* {@link import('./types.js').EndpointDefinition})
|
|
3215
|
-
* @param options - Construction-time inference tuning
|
|
3325
|
+
* @param options - Construction-time inference tuning and the validate opt-out (see
|
|
3216
3326
|
* {@link import('./types.js').EndpointToolOptions})
|
|
3217
3327
|
* @returns A `ToolInterface` named `definition.name`
|
|
3218
3328
|
*
|
|
@@ -3259,8 +3369,8 @@ function createEndpointTool(definition, options) {
|
|
|
3259
3369
|
name: definition.name,
|
|
3260
3370
|
description: definition.description,
|
|
3261
3371
|
...parameters === void 0 ? {} : { parameters },
|
|
3262
|
-
execute(args) {
|
|
3263
|
-
return definition.execute(args);
|
|
3372
|
+
execute(args, context) {
|
|
3373
|
+
return definition.execute(args, context);
|
|
3264
3374
|
}
|
|
3265
3375
|
});
|
|
3266
3376
|
const contract = (0, _orkestrel_contract.createContract)((0, _orkestrel_contract.schemaToShape)(objectSchema));
|
|
@@ -3268,13 +3378,13 @@ function createEndpointTool(definition, options) {
|
|
|
3268
3378
|
name: definition.name,
|
|
3269
3379
|
description: definition.description,
|
|
3270
3380
|
...parameters === void 0 ? {} : { parameters },
|
|
3271
|
-
execute(args) {
|
|
3381
|
+
execute(args, context) {
|
|
3272
3382
|
const parsed = contract.parse(args);
|
|
3273
3383
|
if (parsed === void 0 || !(0, _orkestrel_contract.isRecord)(parsed)) throw new ToolboxError("TOOL", "malformed endpoint call arguments", {
|
|
3274
3384
|
name: definition.name,
|
|
3275
3385
|
faults: contract.explain(args)
|
|
3276
3386
|
});
|
|
3277
|
-
return definition.execute(parsed);
|
|
3387
|
+
return definition.execute(parsed, context);
|
|
3278
3388
|
}
|
|
3279
3389
|
});
|
|
3280
3390
|
}
|