@orkestrel/toolbox 0.0.24 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  import type { AgentInterface } from '@orkestrel/agent';
2
2
  import type { AgentRegistryInterface } from '@orkestrel/agent';
3
3
  import { ArrayShape } from '@orkestrel/contract';
4
+ import type { AuditFault } from '@orkestrel/contract';
4
5
  import { BooleanShape } from '@orkestrel/contract';
5
6
  import type { Condition } from '@orkestrel/database';
6
7
  import type { ConditionConnector } from '@orkestrel/database';
7
- import type { ContractInterface } from '@orkestrel/contract';
8
+ import { ContractInterface } from '@orkestrel/contract';
8
9
  import type { ContractShape } from '@orkestrel/contract';
9
10
  import type { ConversationStoreInterface } from '@orkestrel/agent';
10
11
  import type { DatabaseErrorCode } from '@orkestrel/database';
@@ -14,6 +15,7 @@ import type { Guard } from '@orkestrel/contract';
14
15
  import type { Include } from '@orkestrel/relation';
15
16
  import type { IndexMap } from '@orkestrel/database';
16
17
  import type { JSONRecord } from '@orkestrel/contract';
18
+ import type { JSONSchema } from '@orkestrel/contract';
17
19
  import { JSONShape } from '@orkestrel/contract';
18
20
  import type { KeyFunction } from '@orkestrel/database';
19
21
  import type { LifecycleStatus } from '@orkestrel/workflow';
@@ -32,6 +34,7 @@ import type { TableInterface } from '@orkestrel/database';
32
34
  import type { TableMap } from '@orkestrel/database';
33
35
  import type { TerminalManagerInterface } from '@orkestrel/terminal';
34
36
  import type { ToolContext } from '@orkestrel/tool';
37
+ import { ToolError } from '@orkestrel/tool';
35
38
  import type { ToolInterface } from '@orkestrel/tool';
36
39
  import type { ToolManagerInterface } from '@orkestrel/tool';
37
40
  import { UnionShape } from '@orkestrel/contract';
@@ -42,6 +45,7 @@ import type { WorkflowRegistry } from '@orkestrel/workflow';
42
45
  import type { WorkflowResult } from '@orkestrel/workflow';
43
46
  import type { WorkflowRunnerInterface } from '@orkestrel/workflow';
44
47
  import type { WorkflowStoreInterface } from '@orkestrel/workflow';
48
+ import type { WorkspaceInterface } from '@orkestrel/workspace';
45
49
  import type { WorkspaceManagerInterface } from '@orkestrel/workspace';
46
50
  import type { WorkspaceStoreInterface } from '@orkestrel/workspace';
47
51
 
@@ -146,9 +150,12 @@ export declare interface AgentToolArguments {
146
150
  * per-call `tools` list overrides (never merges with) this default.
147
151
  * - `system` — the default system prompt seeding the sub-agent's context; a per-call `system`
148
152
  * overrides this.
149
- * - `depth` — this invocation's nesting depth (default `0`); a delegated sub-agent that itself
150
- * calls this tool again runs at `depth + 1`, bounded by
151
- * {@link import('./constants.js').AGENT_TOOL_DEPTH}.
153
+ * - `authority` — the registry key of the authority that decides the delegated sub-agent's tool
154
+ * calls. A host's choice, never the call's. Tools named with no `authority` refuse before
155
+ * `registry.build` runs, because an agent with tools needs one.
156
+ * - `depth` — this invocation's nesting depth, a nonnegative safe integer (default `0`); a delegated
157
+ * sub-agent that itself calls this tool again runs at `depth + 1`, bounded by
158
+ * {@link import('./constants.js').AGENT_TOOL_DEPTH}. Any other value throws at construction.
152
159
  * - `ancestry` — the sub-agent identifiers already in this delegation chain (default empty); a
153
160
  * cycle (the resolved agent already present) is rejected with a typed `DEPTH`
154
161
  * {@link import('./errors.js').ToolboxError}.
@@ -170,6 +177,7 @@ export declare interface AgentToolOptions {
170
177
  readonly provider?: string;
171
178
  readonly tools?: readonly string[];
172
179
  readonly system?: string;
180
+ readonly authority?: string;
173
181
  readonly depth?: number;
174
182
  readonly ancestry?: readonly string[];
175
183
  readonly store?: ConversationStoreInterface;
@@ -254,6 +262,35 @@ id: StringShape;
254
262
  values: JSONShape;
255
263
  }, false>]>;
256
264
 
265
+ /**
266
+ * Builds an argument refusal from the supplied faults, auditing the schema when no faults were supplied.
267
+ *
268
+ * @remarks
269
+ * Uses the contract's union reporting: a selected arm names its own faults, and a value selecting
270
+ * no arm receives a bounded summary of alternatives. Supplied faults remain unchanged.
271
+ *
272
+ * @param schema - The compiled schema the arguments are held to
273
+ * @param value - The arguments the call sent
274
+ * @param faults - The faults admission reported
275
+ * @returns The `ToolError` to throw, with code `ARGUMENTS` and the faults in `context.faults`
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * import { createContract, literalShape, objectShape, stringShape, unionShape } from '@orkestrel/contract'
280
+ * import { buildArgumentRefusal } from '@src/core'
281
+ *
282
+ * const { schema } = createContract(
283
+ * unionShape(
284
+ * objectShape({ operation: literalShape(['read']), path: stringShape() }),
285
+ * objectShape({ operation: literalShape(['list']) }),
286
+ * ),
287
+ * )
288
+ * buildArgumentRefusal(schema, { operation: 'read' }, []).message
289
+ * // 'path: missing; expected string'
290
+ * ```
291
+ */
292
+ export declare function buildArgumentRefusal(schema: JSONSchema, value: unknown, faults: readonly AuditFault[]): ToolError;
293
+
257
294
  /**
258
295
  * Represents the probe query and effective row limit {@link import('./helpers.js').clampQuery}
259
296
  * returns.
@@ -400,7 +437,8 @@ connector: OptionalShape<LiteralShape<readonly ["and", "or"]>>;
400
437
  * targets deeper than {@link import('./constants.js').MAX_WORKFLOW_CHAIN} are rejected before
401
438
  * agent or runner activity. When {@link import('./types.js').AgentFunctionOptions.runner} is
402
439
  * supplied, the adapter binds a recursion-safe {@link createWorkflowTool} onto the agent's
403
- * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The
440
+ * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The agent
441
+ * needs an `authority` of its own to run that tool, because an agent with none denies every call. The
404
442
  * task's cancellation propagates through the Agent's native per-run signal seam:
405
443
  * `agent.generate({ signal: controller.signal })` resolves a partial `AgentResult` on a cancel
406
444
  * (never rejects), projected onto its exact JSON fields.
@@ -431,16 +469,20 @@ export declare function createAgentFunction(agent: AgentInterface, options?: Age
431
469
  * from `registry` and runs it to completion for one delegated `task`.
432
470
  *
433
471
  * @remarks
434
- * The universal tool-handler contract: validates the call args against
435
- * {@link import('./shapers.js').agentToolShape}, assembles an `AgentJobInput` (`task` seeds the
472
+ * The universal tool-handler contract: the Tool door holds the call args to
473
+ * {@link import('./shapers.js').agentToolShape}, refusing a malformed call with a `ToolError`
474
+ * carrying code `ARGUMENTS`. The handler assembles an `AgentJobInput` (`task` seeds the
436
475
  * sub-agent's conversation as a single `user` message; `provider` / `tools` / `system` fall
437
- * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent through
476
+ * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults, and `authority` is
477
+ * the tool's own, never the call's), rehydrates the sub-agent through
438
478
  * `registry.build`, runs it with `agent.generate()`, and returns the settled
439
- * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, throws a typed `TOOL`
479
+ * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or
480
+ * tools with no configured `authority`, throws a typed `TOOL`
440
481
  * {@link import('./errors.js').ToolboxError}; a delegation that would exceed
441
482
  * {@link import('./constants.js').AGENT_TOOL_DEPTH}, or re-enter an already-delegated agent (a
442
483
  * cycle), throws a typed `DEPTH` {@link import('./errors.js').ToolboxError}, each isolated
443
- * by the `ToolManagerInterface` into the canonical tool result's top-level `error`.
484
+ * by the `ToolManagerInterface` into the canonical tool result's top-level `error`. A `depth` that
485
+ * is not a nonnegative safe integer throws `TOOL` at construction.
444
486
  *
445
487
  * `AgentInterface` (`@orkestrel/agent`) exposes no teardown method — a bound sub-agent's
446
488
  * lifetime is the single `generate()` call this handler awaits; there is nothing to release
@@ -472,8 +514,9 @@ export declare function createAgentTool(registry: AgentRegistryInterface, option
472
514
  * addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of them by id.
473
515
  *
474
516
  * @remarks
475
- * The universal tool-handler contract: validates the call args against
476
- * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`). `'pending'`
517
+ * The universal tool-handler contract: the Tool door holds the call args to
518
+ * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`), refusing malformed
519
+ * args with a `ToolError` carrying code `ARGUMENTS`. `'pending'`
477
520
  * returns a compact list (`{ id, from, schema }`) of every form addressed to `to`.
478
521
  * `'answer'` looks the form up by `id`, narrows the supplied `values` through `@orkestrel/form`,
479
522
  * and applies it through
@@ -527,9 +570,11 @@ export declare function createDatabaseDefinitionStore(driver?: DriverInterface):
527
570
  * {@link createWorkspaceTool}'s single-tool-many-operations shape).
528
571
  *
529
572
  * @remarks
530
- * The universal tool-handler contract: validates the call args against
531
- * {@link import('./shapers.js').databaseToolShape}, dispatches to the matching operation, and
532
- * returns a plain result on success. A database is resolved lazily and cached for the tool's
573
+ * The universal tool-handler contract: the Tool door holds the call args to
574
+ * {@link import('./shapers.js').databaseToolShape}, refusing malformed args with a `ToolError`
575
+ * carrying code `ARGUMENTS`. The handler dispatches to the matching operation and
576
+ * returns a plain result on success. A row a `'get'` misses is an explicit `null`. A `limit` that is
577
+ * not a positive safe integer throws `TOOL` at construction. A database is resolved lazily and cached for the tool's
533
578
  * lifetime — `'create'` mints one from `tables` ({@link import('./compilers.js').expandTables}) and
534
579
  * a registered `driver` key ({@link import('./types.js').DatabaseToolOptions.drivers}, default
535
580
  * `{ memory: () => createMemoryDriver() }`); any other operation addressing an uncached id falls
@@ -596,12 +641,13 @@ export declare function createDatabaseTool(options?: DatabaseToolOptions): ToolI
596
641
  * description when `summary` is set, keeping the advertised tool list compact for a small model.
597
642
  * This tool is the on-demand expansion seam: given a registered tool's `name`, it looks the tool
598
643
  * up through `tools.tool(name)` and returns its full `description` (falling back to `summary` when a
599
- * tool has no `description` of its own, then a placeholder when it has neither).
644
+ * tool has no `description` of its own, then an explicit `null` when it has neither).
600
645
  *
601
- * The universal tool-handler contract: validates the call args against
602
- * {@link import('./shapers.js').describeToolShape}, returns the plain description string on
603
- * success, throws a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
604
- * or an unknown tool name.
646
+ * The universal tool-handler contract: the Tool door holds the call args to
647
+ * {@link import('./shapers.js').describeToolShape}, refusing a malformed call with a `ToolError`
648
+ * carrying code `ARGUMENTS`. The handler returns the plain description on success, and throws a typed
649
+ * `TOOL` {@link import('./errors.js').ToolboxError} that lists the registered names on an unknown
650
+ * tool name.
605
651
  *
606
652
  * @param tools - The `ToolManagerInterface` (`@orkestrel/tool`) whose registered tools this
607
653
  * tool can describe
@@ -631,20 +677,19 @@ export declare function createDescribeTool(tools: ToolManagerInterface): ToolInt
631
677
  * `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
632
678
  * `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` through `schemaToObject` —
633
679
  * the same object-rooted schema is both the advertised `parameters` and, by default
634
- * ({@link import('./types.js').EndpointToolOptions.validate} `true`), the enforced contract:
635
- * `@orkestrel/contract`'s `schemaToShape` compiles it once (through `createContract`) into a
636
- * `ContractInterface` whose `.parse` runs on every call's `args` before `definition.execute` — a
680
+ * ({@link import('./types.js').EndpointToolOptions.strict} `true`), the enforced contract: the Tool
681
+ * door converts it through `@orkestrel/contract`'s lenient `schemaToShape` and compiles it on the
682
+ * first call, then runs on every call's `args` before `definition.execute` — a
637
683
  * normalizing parse, not a strict type check: a scalar is coerced to its inferred type where the
638
684
  * house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
639
685
  * `'false'`/`1`/`0`), so `definition.execute` receives the coerced value (for example `7` sent for a
640
- * string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
641
- * a record — a required key missing, or a value not coercible to its slot's type — throws a
642
- * typed `TOOL` {@link import('./errors.js').ToolboxError} carrying the compiled contract's
643
- * structured `explain` faults, and `definition.execute` is never called. `format` annotations are
644
- * never asserted, and a key outside the closed inferred schema is silently dropped rather than
645
- * rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
646
- * `validate: false`, the tool's `execute` passes through the model-supplied `args` to
647
- * `definition.execute` without re-validation — the raw-passthrough opt-out. The execution context
686
+ * string slot arrives as `'7'`), not the raw call value. A call whose `args` carries a key outside
687
+ * the closed inferred schema, misses a required key, or holds a value not coercible to its slot's
688
+ * type throws a `ToolError` with code `ARGUMENTS` (`@orkestrel/tool`) carrying `context.faults`, and
689
+ * `definition.execute` is never called. `format` annotations are never asserted (see
690
+ * {@link import('./types.js').EndpointToolOptions.strict}). With
691
+ * `strict: false`, the tool's `execute` passes through the model-supplied `args` to
692
+ * `definition.execute` without validation — the raw-passthrough opt-out. The execution context
648
693
  * the tool's `execute` receives reaches `definition.execute` unchanged whichever way `args` are
649
694
  * handled, so a handler that reads `context.signal` observes the caller's cancellation. Either way,
650
695
  * the definition's return flows back as the tool call's plain result; a throw propagates uncaught,
@@ -653,7 +698,7 @@ export declare function createDescribeTool(tools: ToolManagerInterface): ToolInt
653
698
  *
654
699
  * @param definition - The endpoint's identity, non-empty samples, and local handler (see
655
700
  * {@link import('./types.js').EndpointDefinition})
656
- * @param options - Construction-time inference tuning and the validate opt-out (see
701
+ * @param options - Construction-time inference tuning and the strict opt-out (see
657
702
  * {@link import('./types.js').EndpointToolOptions})
658
703
  * @returns A `ToolInterface` named `definition.name`
659
704
  *
@@ -686,7 +731,7 @@ export declare function createDescribeTool(tools: ToolManagerInterface): ToolInt
686
731
  * name: 'lookupUser',
687
732
  * arguments: { id: true, name: 'Ada' },
688
733
  * })
689
- * // rejected.error -> the TOOL ToolboxError message
734
+ * // rejected.error -> the ARGUMENTS ToolError message, which names `id`
690
735
  * ```
691
736
  */
692
737
  export declare function createEndpointTool(definition: EndpointDefinition, options?: EndpointToolOptions): ToolInterface;
@@ -697,14 +742,13 @@ export declare function createEndpointTool(definition: EndpointDefinition, optio
697
742
  * {@link createEndpointTool}, wraps one concrete endpoint).
698
743
  *
699
744
  * @remarks
700
- * The universal tool-handler contract: validates the call args against
745
+ * The universal tool-handler contract: the Tool door holds the call args to
701
746
  * {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
702
- * booleans, `candidates` an optional array), infers a schema through `@orkestrel/contract`'s
747
+ * booleans, `candidates` an optional array), refusing malformed args with a `ToolError` carrying code
748
+ * `ARGUMENTS`. The handler infers a schema through `@orkestrel/contract`'s
703
749
  * `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` through `schemaToObject` (mirrors
704
750
  * the tool-parameters convention every other `create*Tool` factory advertises), and returns the
705
- * resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
706
- * `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
707
- * {@link import('./errors.js').ToolboxError}.
751
+ * resulting parameters record.
708
752
  *
709
753
  * When `candidates` is absent, the return is the bare parameters record — unchanged from before
710
754
  * this array existed. When `candidates` is present (any array, including empty), the handler
@@ -719,18 +763,14 @@ export declare function createEndpointTool(definition: EndpointDefinition, optio
719
763
  * normalizing parse accept this value", that is would {@link createEndpointTool}'s default enforcement
720
764
  * admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
721
765
  * `valid`; by the house parse/guard round-trip, a `valid: true` entry is
722
- * always also `coercible: true`. `@orkestrel/contract`'s `explain` mirrors the normalizing
723
- * `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
724
- * against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: empty faults, because
725
- * the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
726
- * therefore only ever populates for a non-coercible mismatch — a wrong type the parse can't coerce
727
- * (a boolean in a string slot), a missing required key, or an out-of-enum value — where
728
- * `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
729
- * JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
766
+ * always also `coercible: true`. `faults` is `checker.audit(candidate)`, which reports against `is`'s
767
+ * strictness: a strictly-invalid but coercible candidate (`7` against a string slot) yields
768
+ * `{ valid: false, coercible: true, faults: [{ reason: 'type', ... }] }`, so every invalid candidate
769
+ * names why, an undeclared key included. `checker.is` / `.parse` / `.audit` are all total over
770
+ * JSON-safe input — a JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
730
771
  * and yields a bounded, non-throwing per-candidate verdict; a non-JSON-safe candidate (for example a
731
- * throwing-getter `Proxy`) never reaches the checker at all — it fails the outer `args` parse
732
- * against {@link import('./shapers.js').inferToolShape} and rejects the whole call with the same
733
- * `TOOL` {@link import('./errors.js').ToolboxError} a malformed `samples`/`format`/`enum` throws,
772
+ * throwing-getter `Proxy`) never reaches the checker at all — the Tool door refuses the whole call
773
+ * with the same `ToolError` a malformed `samples`/`format`/`enum` throws,
734
774
  * with no per-candidate verdict produced.
735
775
  *
736
776
  * @param options - Advertised `name` / `description` overrides (see
@@ -791,8 +831,9 @@ export declare function createMemoryDefinitionStore(): DefinitionStoreInterface;
791
831
  * and blocks until the addressed terminal answers, returning the resolved values record.
792
832
  *
793
833
  * @remarks
794
- * The universal tool-handler contract: validates the call args against
795
- * {@link import('./shapers.js').promptToolShape}, parses the call's schema through
834
+ * The universal tool-handler contract: the Tool door holds the call args to
835
+ * {@link import('./shapers.js').promptToolShape}, refusing a malformed call with a `ToolError`
836
+ * carrying code `ARGUMENTS`. The handler parses the call's schema through
796
837
  * `@orkestrel/form`, constructs the live form, and passes it to `TerminalManagerInterface.ask`.
797
838
  * `from` is fixed at construction
798
839
  * ({@link import('./types.js').PromptToolOptions.from}) — never read from the model-supplied
@@ -800,7 +841,7 @@ export declare function createMemoryDefinitionStore(): DefinitionStoreInterface;
800
841
  * `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
801
842
  * {@link import('./errors.js').ToolboxError}; an expired form re-surfaces as `EXPIRE`; an
802
843
  * unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
803
- * and how many brokers the manager holds (`manager.count`). A schema `parseForm` refuses — an
844
+ * and the mounted terminals the manager's refusal lists, encoded and bounded. A schema `parseForm` refuses — an
804
845
  * unknown control, a duplicate field name, a `'select'` or `'checkbox'` field without usable
805
846
  * `choices` — throws a typed `TOOL` error up front, so a malformed schema never parks as a form
806
847
  * nobody can answer.
@@ -831,8 +872,10 @@ export declare function createPromptTool(options: PromptToolOptions): ToolInterf
831
872
  * single-tool-many-operations shape).
832
873
  *
833
874
  * @remarks
834
- * The universal tool-handler contract: validates the call args against
835
- * {@link import('./shapers.js').relationToolShape}, resolves the addressed
875
+ * The universal tool-handler contract: the Tool door holds the call args to
876
+ * {@link import('./shapers.js').relationToolShape}, refusing malformed args with a `ToolError`
877
+ * carrying code `ARGUMENTS`. A `limit` or `depth` that is not a positive safe integer throws `TOOL` at
878
+ * construction, and a row `'load'` misses is an explicit `null`. The handler resolves the addressed
836
879
  * {@link import('@orkestrel/relation').RelationManagerInterface} — an explicit `manager` field
837
880
  * must match a key of {@link import('./types.js').RelationToolOptions.managers}, an omitted one
838
881
  * resolves to the sole registered manager, either miss throwing a typed `TOOL`
@@ -961,7 +1004,12 @@ export declare function createWorkflowFunctions(runner: WorkflowRunnerInterface,
961
1004
  * It is also the propagation carrier
962
1005
  * {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
963
1006
  * handler receives only the model-supplied `args`, the immutable lineage is closed over at bind
964
- * time. The authored target is appended and its zero-based depth derived from workflow tags.
1007
+ * time. The authored target is appended and its zero-based depth derived from workflow tags. That
1008
+ * bound tool wraps the containing workflow's id over no phases, and its empty-args call always
1009
+ * refuses as a cycle before the runner, so the wrapper never runs.
1010
+ *
1011
+ * `parameters` advertises the flat form alone, so the tool is built `strict: false` and its handler
1012
+ * runs the door ({@link import('./helpers.js').requireToolCall}) on the form the arguments take.
965
1013
  *
966
1014
  * **Widened authoring surface (additive — the canonical contract and runner stay strict and
967
1015
  * unchanged).** A 2B model reliably calls the tool but cannot reliably emit the full nested
@@ -976,9 +1024,11 @@ export declare function createWorkflowFunctions(runner: WorkflowRunnerInterface,
976
1024
  * super-set.
977
1025
  *
978
1026
  * The universal tool-handler contract: returns the plain run summary
979
- * (`{ status, count, durable?, fault? }`) on success, throws a typed `ToolboxError` on Toolbox
980
- * boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
981
- * (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
1027
+ * (`{ status, count, durable?, fault? }`) on success. Malformed authored args throw a `ToolError`
1028
+ * with code `ARGUMENTS` (`@orkestrel/tool`) whose text lists what to correct, carrying
1029
+ * `context.faults`; a wrapped definition the strict contract refuses throws `TOOL`, and an over-deep
1030
+ * or cyclic nested run throws `DEPTH`. Genuine runner `WorkflowError`s are preserved, including
1031
+ * `SCHEDULE` for a target with no task. The `ToolManagerInterface` isolates every throw into the canonical tool result's
982
1032
  * top-level `error`, so nothing escapes the run. `options.lineage` is the propagation carrier
983
1033
  * across a workflow → agent → workflow chain. Opaque `functions` remain host leaves; raw `agents`
984
1034
  * are contextually adapted through {@link createWorkflowFunctions}; native checkpoint persistence
@@ -1048,17 +1098,20 @@ export declare function createWorkflowTool(definition: WorkflowDefinition, runne
1048
1098
  * active workspace by id (lenient — an unknown id is a no-op reporting `switched: false`, never a
1049
1099
  * throw).
1050
1100
  *
1051
- * No-active rule (the ergonomic seam): a writing op (write / splice / prepend / append / move /
1052
- * remove / replace) run when `manager.active` is `undefined` auto-creates and activates a default
1053
- * workspace (`manager.add()`) so the model can start writing; a pure-read op (read / list /
1054
- * has / search) against no active workspace returns the empty result (`undefined` / `[]` /
1055
- * `false`), never creating one and never throwing.
1056
- *
1057
- * The handler conforms to the universal tool-handler contract: it `contract.parse`s
1058
- * the args, throws a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
1059
- * operation), else `switch`es on `op.operation` and returns the plain result — letting a
1060
- * `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` / `RANGE`)
1061
- * propagate uncaught. The range edit is the flat `'splice'` op: its flat caret integers are
1101
+ * No-active rule (the ergonomic seam): an op that can create the file it names (write / prepend /
1102
+ * append) run when `manager.active` is `undefined` auto-creates and activates a default workspace
1103
+ * (`manager.add()`) so the model can start writing; an op that edits a file that must already exist
1104
+ * (splice / move / remove / replace) refuses with a `TOOL` `ToolboxError` and creates none; a
1105
+ * pure-read op (read / list / has / search) against no active workspace returns the empty result
1106
+ * (`null` / `[]` / `false`), never creating one and never throwing.
1107
+ *
1108
+ * The Tool door enforces the operation union through `contract`, repairing lossless scalar leaves
1109
+ * and refusing malformed args with a `ToolError` carrying code `ARGUMENTS`. A selected operation
1110
+ * reports its own faults; an unknown operation reports a bounded summary of alternatives.
1111
+ * The handler then `switch`es on `op.operation` and returns the plain result
1112
+ * — letting a `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` /
1113
+ * `RANGE`) propagate uncaught. `read` returns `null` for a path that holds no text, because JSON
1114
+ * tells that from an empty file. The range edit is the flat `'splice'` op: its flat caret integers are
1062
1115
  * reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
1063
1116
  * ranged `write`.
1064
1117
  *
@@ -1167,12 +1220,15 @@ export declare interface DatabaseDefinitionRow {
1167
1220
  * and keeps the row type flat (`definition` reads back as `unknown`).
1168
1221
  *
1169
1222
  * - **`set(definition)` upserts under the definition's own `id`** (no separate id param) — it
1170
- * writes the row `{ id: definition.id, definition }`.
1223
+ * writes the row `{ id: definition.id, definition }`, and first refuses what `get` would refuse,
1224
+ * so no row is written that `get` cannot read.
1171
1225
  * - **`get(id)` resolves the stored definition for an id**, narrowing the opaque JSON column back
1172
1226
  * to a {@link DatabaseDefinition} ({@link import('../validators.js').isDatabaseDefinition} — the
1173
- * boundary narrow for an untrusted storage read), or `undefined` if none is stored
1174
- * or the stored blob is malformed.
1175
- * - **`delete(id)` drops a definition by id**; an absent id is a no-op (no throw).
1227
+ * boundary narrow for an untrusted storage read), or `undefined` if no row is stored. A present
1228
+ * row whose blob is malformed or names another id throws, because reading it as absent would let
1229
+ * a later `set` overwrite it.
1230
+ * - **`delete(id)` drops a definition by id** without reading it, the remedy for a refused row; an
1231
+ * absent id is a no-op (no throw).
1176
1232
  *
1177
1233
  * The public surface is exactly `get` / `set` / `delete` — no extra members (the method
1178
1234
  * bijection with {@link DefinitionStoreInterface}).
@@ -1201,8 +1257,8 @@ export declare class DatabaseDefinitionStore implements DefinitionStoreInterface
1201
1257
  * {@link DatabaseDefinition}.
1202
1258
  *
1203
1259
  * @param id - The database id to read
1204
- * @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored or the
1205
- * stored blob is malformed
1260
+ * @returns The stored {@link DatabaseDefinition}, or `undefined` when no row is stored
1261
+ * @throws {ToolboxError} Thrown with code `DATABASE` when the row's blob is malformed or names another id, with the faults under `context.faults`
1206
1262
  */
1207
1263
  get(id: string): Promise<DatabaseDefinition | undefined>;
1208
1264
  /**
@@ -1210,6 +1266,7 @@ export declare class DatabaseDefinitionStore implements DefinitionStoreInterface
1210
1266
  *
1211
1267
  * @param definition - The config to persist; its own `id` is the row key (no separate id param)
1212
1268
  * @returns A promise settling after the row is written
1269
+ * @throws {ToolboxError} Thrown with code `DATABASE` before any write when the definition fails the guard `get` reads, with the faults under `context.faults`
1213
1270
  */
1214
1271
  set(definition: DatabaseDefinition): Promise<void>;
1215
1272
  /**
@@ -1327,7 +1384,8 @@ export declare class DatabaseResolver {
1327
1384
  * constructed with, used when a written row lacks its primary key. Omitted delegates to the
1328
1385
  * database's default generator.
1329
1386
  * - `limit` — the `'records'` row cap — through {@link import('./helpers.js').clampQuery}
1330
- * — enforce when a call's `query.limit` is omitted or exceeds it. Defaults to
1387
+ * — enforce when a call's `query.limit` is omitted or exceeds it. A positive safe integer; any
1388
+ * other value throws at construction. Defaults to
1331
1389
  * {@link import('./constants.js').DATABASE_TOOL_LIMIT}.
1332
1390
  * - `timeout` — a nonnegative safe-integer number of milliseconds. A fresh
1333
1391
  * `AbortSignal.timeout(timeout)` is passed only to table operations whose current database API
@@ -1469,13 +1527,71 @@ operation: LiteralShape<readonly ["destroy"]>;
1469
1527
  id: StringShape;
1470
1528
  }, false>]>;
1471
1529
 
1530
+ /**
1531
+ * Holds the compiled {@link import('./shapers.js').definitionShape}, whose `audit` fills the faults a
1532
+ * refused stored definition carries.
1533
+ *
1534
+ * @example
1535
+ * ```ts
1536
+ * import { definitionContract } from '@orkestrel/toolbox'
1537
+ *
1538
+ * definitionContract.audit({ id: 'shop', driver: 'memory', tables: {}, keys: {} })
1539
+ * // [{ reason: 'extra', path: ['keys'] }]
1540
+ * ```
1541
+ */
1542
+ export declare const definitionContract: ContractInterface<Readonly<{
1543
+ id: string;
1544
+ driver: string;
1545
+ tables: {
1546
+ readonly [k: string]: Readonly<{
1547
+ columns: {
1548
+ readonly [k: string]: "boolean" | "integer" | "number" | "string" | Readonly<{
1549
+ primitive: "boolean" | "integer" | "number" | "string";
1550
+ } & {
1551
+ optional?: boolean;
1552
+ }>;
1553
+ };
1554
+ } & {}>;
1555
+ };
1556
+ } & {
1557
+ primary?: {
1558
+ readonly [k: string]: string;
1559
+ };
1560
+ indexes?: {
1561
+ readonly [k: string]: readonly (readonly string[])[];
1562
+ };
1563
+ version?: number;
1564
+ }>>;
1565
+
1566
+ /**
1567
+ * Describes a stored {@link import('./types.js').DatabaseDefinition} exactly: the owned record
1568
+ * {@link import('./stores/DatabaseDefinitionStore.js').DatabaseDefinitionStore} reads and writes.
1569
+ *
1570
+ * @remarks
1571
+ * {@link import('./validators.js').isDatabaseDefinition} is the guard the store runs, and a lockstep
1572
+ * test holds it to this shape. The compiled contract fills a refusal's faults.
1573
+ */
1574
+ export declare const definitionShape: ObjectShape< {
1575
+ id: StringShape;
1576
+ driver: StringShape;
1577
+ tables: ObjectShape<Record<never, never>, ObjectShape< {
1578
+ columns: ObjectShape<Record<never, never>, UnionShape<readonly [LiteralShape<readonly ["string", "integer", "number", "boolean"]>, ObjectShape< {
1579
+ primitive: LiteralShape<readonly ["string", "integer", "number", "boolean"]>;
1580
+ optional: OptionalShape<BooleanShape>;
1581
+ }, false>]>>;
1582
+ }, false>>;
1583
+ primary: OptionalShape<ObjectShape<Record<never, never>, StringShape>>;
1584
+ indexes: OptionalShape<ObjectShape<Record<never, never>, ArrayShape<ArrayShape<StringShape>>>>;
1585
+ version: OptionalShape<NumberShape>;
1586
+ }, false>;
1587
+
1472
1588
  /**
1473
1589
  * Represents the point-access persistence seam for {@link DatabaseDefinition} configs — the twin of
1474
1590
  * `@orkestrel/terminal`'s `TerminalStoreInterface`, storing a database's config-only blueprint
1475
1591
  * rather than a live handle. Every primitive is async; `delete` of an absent id is a no-op.
1476
1592
  */
1477
1593
  export declare interface DefinitionStoreInterface {
1478
- /** Resolves the persisted definition for `id`, or `undefined` when none is stored. */
1594
+ /** Resolves the persisted definition for `id`, or `undefined` when none is stored; a present row an implementation cannot read as a definition throws. */
1479
1595
  get(id: string): Promise<DatabaseDefinition | undefined>;
1480
1596
  /** Inserts or replaces a definition under its own `id`, taking no separate id argument. */
1481
1597
  set(definition: DatabaseDefinition): Promise<void>;
@@ -1521,6 +1637,40 @@ export declare const DESCRIBE_TOOL_NAME = "describe";
1521
1637
  */
1522
1638
  export declare const DESCRIBE_TOOL_SUMMARY = "Return the full description of a named registered tool.";
1523
1639
 
1640
+ /**
1641
+ * Describes a stored definition that names another id, naming the id it holds and the id it must hold.
1642
+ *
1643
+ * @param id - The database id the row is stored under
1644
+ * @param held - The id the stored definition names
1645
+ * @returns The refusal message
1646
+ *
1647
+ * @example
1648
+ * ```ts
1649
+ * import { describeDefinitionMismatch } from '@src/core'
1650
+ *
1651
+ * describeDefinitionMismatch('shop', 'other')
1652
+ * // 'database "shop" holds a definition that names id "other"; its id must be "shop"; ...'
1653
+ * ```
1654
+ */
1655
+ export declare function describeDefinitionMismatch(id: string, held: string): string;
1656
+
1657
+ /**
1658
+ * Describes a stored definition this package refuses to read, naming its first fault and the remedy.
1659
+ *
1660
+ * @param id - The database id the row is stored under
1661
+ * @param faults - The refused definition's faults
1662
+ * @returns The refusal message
1663
+ *
1664
+ * @example
1665
+ * ```ts
1666
+ * import { describeDefinitionRefusal } from '@src/core'
1667
+ *
1668
+ * describeDefinitionRefusal('shop', [{ reason: 'extra', path: ['keys'] }])
1669
+ * // 'database "shop" holds a definition this version refuses (extra key "keys" at the root; ...'
1670
+ * ```
1671
+ */
1672
+ export declare function describeDefinitionRefusal(id: string, faults: readonly AuditFault[]): string;
1673
+
1524
1674
  /**
1525
1675
  * Represents the flat args {@link import('./factories.js').createDescribeTool} accepts — the
1526
1676
  * registered tool `name` whose full `description` a model wants back.
@@ -1552,13 +1702,13 @@ name: StringShape;
1552
1702
  * @remarks
1553
1703
  * `samples` must be non-empty — {@link import('./factories.js').createEndpointTool} throws a
1554
1704
  * typed `TOOL` {@link import('./errors.js').ToolboxError} at construction when it is empty,
1555
- * because an empty sample set cannot infer a schema. By default ({@link EndpointToolOptions.validate}
1705
+ * because an empty sample set cannot infer a schema. By default ({@link EndpointToolOptions.strict}
1556
1706
  * `true`) `execute` receives the parsed, normalized args record — a copy of the model-supplied
1557
1707
  * `args` with each scalar coerced to its inferred type (for example a number sent for a string slot
1558
1708
  * arrives coerced to a string), checked against the same schema advertised as `parameters` — and
1559
- * a call with a missing required key or a non-coercible value never reaches `execute` at all (see
1560
- * {@link EndpointToolOptions.validate}). With
1561
- * `validate: false`, `execute` receives the model-supplied `args` verbatim (raw passthrough, never
1709
+ * a call with an undeclared key, a missing required key, or a non-coercible value never reaches
1710
+ * `execute` at all (see {@link EndpointToolOptions.strict}). With
1711
+ * `strict: false`, `execute` receives the model-supplied `args` verbatim (raw passthrough, never
1562
1712
  * checked against the inferred schema). Either way `execute`'s return flows back as the tool
1563
1713
  * call's result; a throw propagates uncaught, isolated by the `ToolManagerInterface`
1564
1714
  * (`@orkestrel/tool`) into the canonical error envelope. When `samples` are non-object values,
@@ -1584,7 +1734,8 @@ export declare interface EndpointDefinition {
1584
1734
  * `ToolContext` second parameter, and the same `Promise<unknown> | unknown` return. A handler that
1585
1735
  * declares `args` alone still satisfies the type, the way a function declaring fewer parameters
1586
1736
  * than its contract does, so an endpoint definition written against the argument alone keeps
1587
- * typechecking and reads no signal.
1737
+ * typechecking and reads no signal. Under the default door `args` is a null-prototype record, on
1738
+ * which `String(args)` and `args.hasOwnProperty()` throw; read it with `Object.hasOwn`.
1588
1739
  */
1589
1740
  export declare type EndpointHandler = (args: Readonly<Record<string, unknown>>, context: ToolContext) => Promise<unknown> | unknown;
1590
1741
 
@@ -1595,32 +1746,28 @@ export declare type EndpointHandler = (args: Readonly<Record<string, unknown>>,
1595
1746
  *
1596
1747
  * @remarks
1597
1748
  * `format` / `enum` default to `false`, matching `@orkestrel/contract`'s own
1598
- * `ValueToSchemaOptions` defaults. `validate` defaults to `true`: the schema
1599
- * `createEndpointTool` advertises as `parameters` (`samplesToSchema` and `schemaToObject`) is
1600
- * compiled once at construction (through `@orkestrel/contract`'s `schemaToShape`) into a
1601
- * `ContractInterface` used to `parse` every call's `args` before `execute` runs — a normalizing
1749
+ * `ValueToSchemaOptions` defaults. `strict` mirrors `@orkestrel/tool`'s `ToolOptions.strict` and
1750
+ * defaults to `true`: the schema `createEndpointTool` advertises as `parameters` (`samplesToSchema`
1751
+ * and `schemaToObject`) is converted by the Tool door through `@orkestrel/contract`'s lenient
1752
+ * `schemaToShape` on the first call, and runs on every call's `args` before `execute` — a normalizing
1602
1753
  * parse: a scalar value is coerced to its inferred type where the house parsers coerce (a number
1603
1754
  * to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/`'false'`/`1`/`0`), so `execute`
1604
1755
  * receives the coerced values (for example `7` sent for a string slot arrives at `execute` as `'7'`), not
1605
- * the raw call args. A call whose `args` fails to parse — a required key missing, or a value not
1606
- * coercible to its slot's type — throws a typed `TOOL` {@link import('./errors.js').ToolboxError}
1607
- * carrying the structured `explain` faults, and `execute` is never called. Beyond that coercion,
1608
- * enforcement is structural — required keys, `enum` membership, and numeric bounds — `format`
1609
- * annotations (`email`, `date-time`, `uuid`, `uri`, ...) are never asserted, mirroring
1610
- * `@orkestrel/contract`'s own widening-only law for `schemaToShape`: a `format: true`-tuned
1611
- * endpoint still accepts a non-conforming string in a format-tagged slot. A key not present in
1612
- * the inferred (closed, `additionalProperties: false`) schema is never a rejection either — it is
1613
- * silently dropped before `execute` runs (the same leniency `@orkestrel/contract`'s own `parse`
1614
- * grants a closed object generally), so `execute` may see fewer keys than the caller sent. Set
1615
- * `validate: false` when the endpoint's own handler validates its arguments, or when `samples`
1616
- * under-describe the real contract and the normalizing coercion would corrupt a call: the tool's
1617
- * `execute` then passes the model-supplied `args` to the handler unchanged, unchecked and
1618
- * unstripped.
1756
+ * the raw call args. A call that carries a key the inferred (closed, `additionalProperties: false`)
1757
+ * schema does not declare, misses a required key, or holds a value not coercible to its slot's type
1758
+ * throws a `ToolError` with code `ARGUMENTS` (`@orkestrel/tool`) carrying `context.faults`, and
1759
+ * `execute` is never called. Beyond that, enforcement is structural — required keys, `enum`
1760
+ * membership, and numeric bounds — `format` annotations (`email`, `date-time`, `uuid`, `uri`, ...)
1761
+ * are never asserted, mirroring `@orkestrel/contract`'s own widening-only law for `schemaToShape`: a
1762
+ * `format: true`-tuned endpoint still accepts a non-conforming string in a format-tagged slot. Set
1763
+ * `strict: false` when the endpoint's own handler validates its arguments, or when `samples`
1764
+ * under-describe the real contract and the door would refuse a real call: the tool's `execute` then
1765
+ * passes the model-supplied `args` to the handler unchanged, unchecked and unstripped.
1619
1766
  */
1620
1767
  export declare interface EndpointToolOptions {
1621
1768
  readonly format?: boolean;
1622
1769
  readonly enum?: boolean;
1623
- readonly validate?: boolean;
1770
+ readonly strict?: boolean;
1624
1771
  }
1625
1772
 
1626
1773
  /**
@@ -1692,6 +1839,9 @@ export declare function extendLineage(lineage: WorkflowLineage, tag: string): Wo
1692
1839
  /** Describes the flat dot-path relation include list, expanded through {@link import('./helpers.js').expandInclude}. */
1693
1840
  export declare const includeShape: OptionalShape<ArrayShape<StringShape>>;
1694
1841
 
1842
+ /** Describes a {@link import('./types.js').DatabaseDefinition}'s `indexes` map — table name to its index column groups. */
1843
+ export declare const indexesShape: ObjectShape<Record<never, never>, ArrayShape<ArrayShape<StringShape>>>;
1844
+
1695
1845
  /**
1696
1846
  * Holds the schema-inference protocol {@link import('./factories.js').createInferTool} advertises —
1697
1847
  * the required `samples`, the optional `format`, `enum`, and `candidates` arguments, and a worked
@@ -1774,8 +1924,8 @@ export declare interface InferToolOptions {
1774
1924
  * optional `candidates` array to check against the inferred schema.
1775
1925
  *
1776
1926
  * @remarks
1777
- * `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
1778
- * surfaced by the handler as a typed `TOOL` {@link import('./errors.js').ToolboxError}. When
1927
+ * `samples` requires at least one element (`min: 1`) — the Tool door refuses an empty array with
1928
+ * a `ToolError` carrying code `ARGUMENTS` (`@orkestrel/tool`). When
1779
1929
  * `candidates` is present (any array, including empty), the handler compiles a contract from the
1780
1930
  * freshly inferred schema and checks each candidate against it with a strict guard (`.is`, no
1781
1931
  * coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s normalizing
@@ -1805,24 +1955,54 @@ export declare function isAgentFunction(value: unknown): value is AgentFunction;
1805
1955
  export declare const isColumnPrimitive: Guard<ColumnPrimitive>;
1806
1956
 
1807
1957
  /**
1808
- * Narrows an unknown value to a {@link ColumnSpec}.
1958
+ * Narrows an unknown value to a {@link ColumnSpec} — a bare {@link ColumnPrimitive}, or exactly
1959
+ * `{ primitive, optional? }`.
1809
1960
  *
1810
1961
  * @param value - The value to inspect
1811
- * @returns True if `value` is a valid {@link ColumnPrimitive} shorthand or a `{ primitive, optional }` record with a valid `primitive`; false otherwise
1962
+ * @returns True if `value` is a valid shorthand or an exact record with a valid `primitive`; false otherwise
1812
1963
  */
1813
- export declare function isColumnSpec(value: unknown): value is ColumnSpec;
1964
+ export declare const isColumnSpec: Guard<ColumnSpec>;
1814
1965
 
1815
1966
  /**
1816
- * Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id` and `driver`, a
1817
- * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
1818
- * `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
1819
- * {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
1820
- * before trusting it as a definition (never an `as`).
1967
+ * Narrows an unknown value to exactly a {@link DatabaseDefinition} — a non-empty `id` and `driver`,
1968
+ * a {@link TableSpec}, plus optional `primary`, `indexes`, and finite `version` schema configuration,
1969
+ * refusing every other key. The boundary guard a {@link import('./types.js').DefinitionStoreInterface}
1970
+ * applies to an untrusted persisted blob before trusting it as a definition (never an `as`).
1971
+ *
1972
+ * @remarks
1973
+ * A lockstep test holds this guard to {@link import('./shapers.js').definitionShape}.
1821
1974
  *
1822
1975
  * @param value - The value to inspect
1823
1976
  * @returns True if `value` is a complete {@link DatabaseDefinition}; false otherwise
1824
1977
  */
1825
- export declare function isDatabaseDefinition(value: unknown): value is DatabaseDefinition;
1978
+ export declare const isDatabaseDefinition: Guard<DatabaseDefinition>;
1979
+
1980
+ /**
1981
+ * Narrows an unknown value to an `IndexMap` (`@orkestrel/database`) — table name to its index
1982
+ * groups, each group a nonempty list of nonempty column names.
1983
+ *
1984
+ * @param value - The value to inspect
1985
+ * @returns True if every value is a list of such groups; false otherwise
1986
+ */
1987
+ export declare function isIndexMap(value: unknown): value is IndexMap;
1988
+
1989
+ /**
1990
+ * Narrows an unknown value to a `PrimaryMap` (`@orkestrel/database`) — table name to a nonempty
1991
+ * primary-key column name.
1992
+ *
1993
+ * @param value - The value to inspect
1994
+ * @returns True if every value is a nonempty string; false otherwise
1995
+ */
1996
+ export declare function isPrimaryMap(value: unknown): value is PrimaryMap;
1997
+
1998
+ /**
1999
+ * Narrows an unknown value to a {@link TableSpec} — table name to exactly `{ columns }`, each column
2000
+ * a valid {@link ColumnSpec}.
2001
+ *
2002
+ * @param value - The value to inspect
2003
+ * @returns True if `value` is a table layout; false otherwise
2004
+ */
2005
+ export declare function isTableSpec(value: unknown): value is TableSpec;
1826
2006
 
1827
2007
  /**
1828
2008
  * Narrows an unknown caught value to a {@link ToolboxError}.
@@ -1862,6 +2042,26 @@ export declare function isWorkflowLineage(value: unknown): value is WorkflowLine
1862
2042
  */
1863
2043
  export declare const keyShape: UnionShape<readonly [ArrayShape<UnionShape<readonly [StringShape, NumberShape]>>, StringShape, NumberShape]>;
1864
2044
 
2045
+ /**
2046
+ * Caps the complete text of a refusal at `FAULT_TEXT_LIMIT` characters, saying where it stopped.
2047
+ *
2048
+ * @remarks
2049
+ * A refusal goes back to a model whose context window can be small. Use this where a message joins
2050
+ * parts this package cannot bound one by one: a head beside a report, or an upstream error's text.
2051
+ *
2052
+ * @param text - The refusal text
2053
+ * @returns The text unchanged when it fits, otherwise cut to the limit and well formed
2054
+ *
2055
+ * @example
2056
+ * ```ts
2057
+ * import { limitRefusal } from '@src/core'
2058
+ *
2059
+ * limitRefusal('send steps') // 'send steps'
2060
+ * limitRefusal('x'.repeat(5000)).length // 2048
2061
+ * ```
2062
+ */
2063
+ export declare function limitRefusal(text: string): string;
2064
+
1865
2065
  /** Describes which registered relation manager to address — omitted resolves to the sole registered manager. */
1866
2066
  export declare const managerShape: OptionalShape<StringShape>;
1867
2067
 
@@ -1883,8 +2083,8 @@ export declare const MAX_WORKFLOW_CHAIN = 8;
1883
2083
  * {@link import('../factories.js').createMemoryDefinitionStore} builds. It implements the same
1884
2084
  * {@link DefinitionStoreInterface} contract as
1885
2085
  * {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}: this store copies on
1886
- * write and on read; the table-backed store narrows an untrusted stored blob and reports
1887
- * `undefined` for a malformed one.
2086
+ * write and on read; the table-backed store narrows an untrusted stored blob and refuses a malformed
2087
+ * one.
1888
2088
  *
1889
2089
  * @remarks
1890
2090
  * A plain `Map<string, DatabaseDefinition>` (the definition is already pure,
@@ -2002,6 +2202,9 @@ concurrency: OptionalShape<NumberShape>;
2002
2202
  bail: OptionalShape<LiteralShape<readonly [true, false]>>;
2003
2203
  }, false>;
2004
2204
 
2205
+ /** Describes a {@link import('./types.js').DatabaseDefinition}'s `primary` map — table name to its primary-key column. */
2206
+ export declare const primaryShape: ObjectShape<Record<never, never>, StringShape>;
2207
+
2005
2208
  /**
2006
2209
  * Holds the form protocol {@link import('./factories.js').createPromptTool} advertises — the
2007
2210
  * required `to` and `schema`, how a field declares its control and its rules, and a worked
@@ -2135,10 +2338,11 @@ export declare const RELATION_TOOL_SUMMARY = "Traverse and edit relationships be
2135
2338
  * manager when exactly one is registered, else throws a typed `TOOL`
2136
2339
  * {@link import('./errors.js').ToolboxError} naming the registered manager keys.
2137
2340
  * - `limit` — the row cap `'find'` / `'links'` enforce when a call's `limit` is omitted or
2138
- * exceeds it. Defaults to {@link import('./constants.js').RELATION_TOOL_LIMIT}.
2341
+ * exceeds it. A positive safe integer; any other value throws at construction. Defaults to
2342
+ * {@link import('./constants.js').RELATION_TOOL_LIMIT}.
2139
2343
  * - `depth` — the max dot-path segment count `'load'` / `'find'`'s `include` paths may reach
2140
- * ({@link import('./helpers.js').expandInclude}). Defaults to
2141
- * {@link import('./constants.js').RELATION_TOOL_DEPTH}.
2344
+ * ({@link import('./helpers.js').expandInclude}). A positive safe integer; any other value throws
2345
+ * at construction. Defaults to {@link import('./constants.js').RELATION_TOOL_DEPTH}.
2142
2346
  * - `name` / `description` — advertised tool overrides; default to
2143
2347
  * {@link import('./constants.js').RELATION_TOOL_NAME} / {@link import('./constants.js').RELATION_TOOL_DESCRIPTION}.
2144
2348
  */
@@ -2197,6 +2401,89 @@ key: UnionShape<readonly [StringShape, NumberShape]>;
2197
2401
  relation: StringShape;
2198
2402
  }, false>]>;
2199
2403
 
2404
+ /**
2405
+ * Renders the candidates a refusal offers, each encoded and length-capped and at most
2406
+ * `REFUSAL_LIST_LIMIT` of them named, so a long or multiline name cannot fake or balloon the message.
2407
+ *
2408
+ * @param names - The candidates to offer
2409
+ * @returns The encoded candidates joined with `, `, empty when there are none
2410
+ *
2411
+ * @example
2412
+ * ```ts
2413
+ * import { renderCandidates } from '@src/core'
2414
+ *
2415
+ * renderCandidates(['read', 'list']) // '"read", "list"'
2416
+ * ```
2417
+ */
2418
+ export declare function renderCandidates(names: readonly string[]): string;
2419
+
2420
+ /**
2421
+ * Reads the active workspace for an operation that edits a file that must already exist, or refuses.
2422
+ *
2423
+ * @param manager - The manager the workspace tool drives
2424
+ * @param operation - The operation that needs an active workspace
2425
+ * @returns The active workspace
2426
+ * @throws {ToolboxError} Thrown with code `TOOL` when no workspace is active, and creates none
2427
+ *
2428
+ * @example
2429
+ * ```ts
2430
+ * import { createWorkspaceManager } from '@orkestrel/workspace'
2431
+ * import { requireActiveWorkspace } from '@src/core'
2432
+ *
2433
+ * const manager = createWorkspaceManager()
2434
+ * const workspace = manager.add()
2435
+ * requireActiveWorkspace(manager, 'remove') === workspace // true
2436
+ * ```
2437
+ */
2438
+ export declare function requireActiveWorkspace(manager: WorkspaceManagerInterface, operation: string): WorkspaceInterface;
2439
+
2440
+ /**
2441
+ * Reads a call's arguments through a compiled contract, or refuses them with what to send.
2442
+ *
2443
+ * @remarks
2444
+ * Uses the contract admission door and translates refusals to tool errors.
2445
+ * Tools with object and union roots enforce `createTool`'s `contract` and use this helper to type
2446
+ * the parsed arguments. The workflow tool uses this door under `strict: false` because it accepts
2447
+ * several authoring forms while advertising the flat form.
2448
+ *
2449
+ * @param contract - The compiled contract of the arguments
2450
+ * @param value - The arguments the call sent
2451
+ * @returns The admitted value, which may retain input identity under a union
2452
+ * @throws {import('@orkestrel/tool').ToolError} Thrown with code `ARGUMENTS`, carrying `context.faults` when the arguments could be read
2453
+ *
2454
+ * @example
2455
+ * ```ts
2456
+ * import { createContract, numberShape, objectShape } from '@orkestrel/contract'
2457
+ * import { requireToolCall } from '@src/core'
2458
+ *
2459
+ * const contract = createContract(objectShape({ amount: numberShape() }))
2460
+ * requireToolCall(contract, { amount: '3' }) // { amount: 3 }
2461
+ * requireToolCall(contract, { amount: 3, extra: 1 }) // throws ToolError ('ARGUMENTS')
2462
+ * ```
2463
+ */
2464
+ export declare function requireToolCall<T>(contract: ContractInterface<T>, value: unknown): T;
2465
+
2466
+ /**
2467
+ * Picks the effective value of a positive numeric tool option: the configured value when it is a
2468
+ * positive safe integer, and the fallback when it is omitted.
2469
+ *
2470
+ * @param value - The configured option, or `undefined`
2471
+ * @param fallback - The default an omitted option takes
2472
+ * @param subject - The name the refusal gives the option
2473
+ * @returns The effective value
2474
+ * @throws {ToolboxError} Thrown with code `TOOL` when `value` is present and is anything else, because `NaN` as a bound passes every comparison
2475
+ *
2476
+ * @example
2477
+ * ```ts
2478
+ * import { resolveBound } from '@src/core'
2479
+ *
2480
+ * resolveBound(undefined, 100, 'database limit') // 100
2481
+ * resolveBound(25, 100, 'database limit') // 25
2482
+ * resolveBound(0, 100, 'database limit') // throws ToolboxError ('TOOL')
2483
+ * ```
2484
+ */
2485
+ export declare function resolveBound(value: number | undefined, fallback: number, subject: string): number;
2486
+
2200
2487
  /**
2201
2488
  * Picks the effective row limit a tool reads with — the requested count when it sits inside the
2202
2489
  * cap, the cap when it exceeds it, and `0` when either falls below zero.
@@ -2249,7 +2536,21 @@ export declare function resolveRelationManager(managers: Readonly<Record<string,
2249
2536
  export declare function resolveRelationModel(manager: RelationManagerInterface, name: string): ModelInterface;
2250
2537
 
2251
2538
  /**
2252
- * Resolves an owned workflow input through the supplied flat or draft contracts.
2539
+ * Resolves an owned workflow input through the supplied flat or draft contracts, or refuses it with
2540
+ * what to send.
2541
+ *
2542
+ * @remarks
2543
+ * Empty input is the wrapped definition. A `phases` key with no `steps` key takes the draft form,
2544
+ * and everything else takes the advertised flat form, so a blob carrying both keys refuses `phases`
2545
+ * as undeclared rather than ignoring it, and a blob carrying neither is told to send `steps`.
2546
+ *
2547
+ * @param owned - The call's owned JSON arguments
2548
+ * @param definition - The wrapped definition empty input runs
2549
+ * @param steps - The compiled flat form
2550
+ * @param draft - The compiled draft form
2551
+ * @returns The definition the input resolves to
2552
+ * @throws {import('@orkestrel/tool').ToolError} Thrown with code `ARGUMENTS` when the chosen form refuses the input, carrying `context.faults`
2553
+ *
2253
2554
  * @example
2254
2555
  * ```ts
2255
2556
  * import { createContract } from '@orkestrel/contract'
@@ -2260,7 +2561,7 @@ export declare function resolveRelationModel(manager: RelationManagerInterface,
2260
2561
  * resolveWorkflowTarget({}, definition, steps, createWorkflowDraftContract()) // definition
2261
2562
  * ```
2262
2563
  */
2263
- export declare function resolveWorkflowTarget(owned: JSONRecord, definition: WorkflowDefinition, steps: ContractInterface<WorkflowSteps>, draft: ContractInterface<WorkflowDraft>): WorkflowDefinition | undefined;
2564
+ export declare function resolveWorkflowTarget(owned: JSONRecord, definition: WorkflowDefinition, steps: ContractInterface<WorkflowSteps>, draft: ContractInterface<WorkflowDraft>): WorkflowDefinition;
2264
2565
 
2265
2566
  /** Describes a loose row — a flat object of column name to JSON value. */
2266
2567
  export declare const rowShape: ObjectShape<Record<never, never>, JSONShape>;
@@ -2362,7 +2663,7 @@ export declare interface TaskDraft {
2362
2663
  readonly behavior?: string;
2363
2664
  /** Holds the extra attempts after the first on failure (a non-negative integer); persisted with the workflow. */
2364
2665
  readonly retries?: number;
2365
- /** Holds the per-attempt deadline in milliseconds (`0..MAX_TIMER_MS`); persisted with the workflow. */
2666
+ /** Holds the per-attempt deadline in milliseconds (`1..MAX_TIMER_MS`); persisted with the workflow, and omitted for no deadline. */
2366
2667
  readonly timeout?: number;
2367
2668
  }
2368
2669
 
@@ -2413,9 +2714,10 @@ export declare class ToolboxError extends Error {
2413
2714
  * thrown, typed, code-bearing error, never a `{ error }` return.
2414
2715
  *
2415
2716
  * @remarks
2416
- * `TOOL` — malformed calls and package-owned resolution or configuration failures, including
2417
- * unknown tools, terminals, databases, drivers, relation managers, or models; invalid prompt
2418
- * choices or include paths; disabled readonly mutations; and invalid infer/endpoint inputs.
2717
+ * `TOOL` — package-owned resolution or configuration failures, including unknown tools, terminals,
2718
+ * databases, drivers, relation managers, or models; invalid prompt choices or include paths;
2719
+ * disabled readonly mutations; and invalid construction options. Malformed call arguments are a
2720
+ * `ToolError` with code `ARGUMENTS` (`@orkestrel/tool`), carrying `context.faults`.
2419
2721
  * `DEPTH` — the delegation would exceed {@link import('./constants.js').AGENT_TOOL_DEPTH}, or
2420
2722
  * the resolved agent is already an ancestor (a cycle).
2421
2723
  * `DEADLOCK` — an `ask` call ({@link import('./factories.js').createPromptTool}) would form a
@@ -2426,7 +2728,9 @@ export declare class ToolboxError extends Error {
2426
2728
  * because the prompt id is unknown or already settled, or the terminal manager rejected the
2427
2729
  * answer.
2428
2730
  * `DATABASE` — a typed `@orkestrel/database` failure (`DatabaseError`), re-surfaced with the
2429
- * granular {@link import('@orkestrel/database').DatabaseErrorCode} carried in `context.code`.
2731
+ * granular {@link import('@orkestrel/database').DatabaseErrorCode} carried in `context.code`, or a
2732
+ * stored definition {@link import('./stores/DatabaseDefinitionStore.js').DatabaseDefinitionStore}
2733
+ * refuses, carrying `context.faults`.
2430
2734
  * `RELATION` — a typed `@orkestrel/relation` failure (`RelationError`), re-surfaced with the
2431
2735
  * granular {@link import('@orkestrel/relation').RelationErrorCode} carried in `context.code`.
2432
2736
  */
@@ -2691,7 +2995,7 @@ export declare const WORKSPACE_TOOL_SUMMARY = "Read and edit files in a workspac
2691
2995
  * the edit / read arms target.
2692
2996
  */
2693
2997
  export declare type WorkspaceOperation =
2694
- /** Reads a whole text file's text by `path` from the active workspace (a binary / absent path — or no active workspace — yields no content). */
2998
+ /** Reads a whole text file's text by `path` from the active workspace (a binary / absent path — or no active workspace — yields `null`). */
2695
2999
  {
2696
3000
  readonly operation: 'read';
2697
3001
  readonly path: string;