@orkestrel/toolbox 0.0.23 → 0.0.25

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,12 +1,12 @@
1
- import { INTRINSICS, arrayShape, attempt, booleanShape, cloneJSONRecord, createContract, holds, integerShape, isArray, isBoolean, isError, isFiniteNumber, isFunction, isNonEmptyString, isNumber, isObject, isRecord, isString, jsonShape, literalOf, literalShape, numberShape, objectShape, optionalShape, parseJSONValue, rawShape, recordShape, samplesToSchema, schemaToObject, schemaToParameters, schemaToShape, stringShape, unionShape } from "@orkestrel/contract";
1
+ import { FAULT_TEXT_LIMIT, INTRINSICS, admitValue, arrayOf, arrayShape, attempt, booleanShape, cloneJSONRecord, createContract, describeFaults, extractPath, holds, integerShape, isArray, isBoolean, isError, isFiniteNumber, isFunction, isNonEmptyString, isNonNegativeInteger, isNumber, isObject, isRecord, isString, jsonShape, literalOf, literalShape, numberShape, objectShape, optionalShape, parseJSONValue, preview, rawShape, recordOf, recordShape, renderList, samplesToSchema, schemaToObject, schemaToParameters, schemaToShape, stringShape, unionOf, unionShape } from "@orkestrel/contract";
2
2
  import { MAX_TIMER_MS, createWorkflowContract } from "@orkestrel/workflow";
3
3
  import { isTerminalError } from "@orkestrel/terminal";
4
4
  import { createDatabase, createMemoryDriver, isDatabaseError } from "@orkestrel/database";
5
5
  import { isRelationError } from "@orkestrel/relation";
6
- import { agentResultToJSON } from "@orkestrel/agent";
6
+ import { agentResultToJSON, isPositiveSafeInteger } from "@orkestrel/agent";
7
+ import { ToolError, createTool } from "@orkestrel/tool";
7
8
  import { createWorkspaceManager, isTextContent, rangeOf } from "@orkestrel/workspace";
8
- import { createTool } from "@orkestrel/tool";
9
- import { createForm, isFormError, isFormValues, parseForm } from "@orkestrel/form";
9
+ import { FIELD_CONTROLS, createForm, isFormError, isFormValues, parseForm } from "@orkestrel/form";
10
10
  // src/core/constants.ts
11
11
  /**
12
12
  * Holds the name {@link import('./factories.js').createAgentTool} advertises by default, `'agent'`
@@ -190,7 +190,7 @@ var WORKSPACE_TOOL_DESCRIPTION = [
190
190
  "All file operations act on the ACTIVE workspace; use \"workspaces\" then \"switch\" to move between workspaces.",
191
191
  "",
192
192
  "Operations (each takes the fields listed):",
193
- "- read { \"operation\": \"read\", \"path\": \"<file>\" } — return the file's text.",
193
+ "- read { \"operation\": \"read\", \"path\": \"<file>\" } — return the file's text, or null when the path holds no text.",
194
194
  "- list { \"operation\": \"list\" } — list every file in the active workspace (path, state, size, lines, kind).",
195
195
  "- has { \"operation\": \"has\", \"path\": \"<file>\" } — whether the file exists.",
196
196
  "- search { \"operation\": \"search\", \"query\": \"<text>\", \"regex\"?: bool, \"sensitive\"?: bool, \"limit\"?: int } — find lines matching the query across all files.",
@@ -204,7 +204,7 @@ var WORKSPACE_TOOL_DESCRIPTION = [
204
204
  "- workspaces { \"operation\": \"workspaces\" } — list the workspaces you can switch between (each id, file count, active).",
205
205
  "- switch { \"operation\": \"switch\", \"id\": \"<id>\" } — make the workspace with that id active (ids come from \"workspaces\").",
206
206
  "",
207
- "Notes: lines and columns are 1-based (column 1 is the first character). \"regex\" defaults to false (a literal substring), \"sensitive\" defaults to true (case-sensitive). \"search\"/\"replace\"/\"splice\" act only on text files. Editing with no active workspace auto-creates one.",
207
+ "Notes: lines and columns are 1-based (column 1 is the first character). \"regex\" defaults to false (a literal substring), \"sensitive\" defaults to true (case-sensitive). \"search\"/\"replace\"/\"splice\" act only on text files. With no active workspace, \"write\", \"prepend\", and \"append\" create one, and \"replace\", \"splice\", \"move\", and \"remove\" refuse.",
208
208
  "",
209
209
  "Example — write a file:",
210
210
  JSON.stringify(WORKSPACE_TOOL_EXAMPLE)
@@ -680,59 +680,77 @@ function isAgentFunction(value) {
680
680
  return inspected.success && inspected.value;
681
681
  }
682
682
  /**
683
- * Narrows an unknown value to a {@link ColumnSpec}.
683
+ * Narrows an unknown value to a {@link ColumnPrimitive}.
684
+ *
685
+ * @param value - The value to inspect
686
+ * @returns True if `value` is `'string'`, `'integer'`, `'number'`, or `'boolean'`; false otherwise
687
+ */
688
+ var isColumnPrimitive = literalOf("string", "integer", "number", "boolean");
689
+ /**
690
+ * Narrows an unknown value to a {@link ColumnSpec} — a bare {@link ColumnPrimitive}, or exactly
691
+ * `{ primitive, optional? }`.
684
692
  *
685
693
  * @param value - The value to inspect
686
- * @returns True if `value` is a valid {@link ColumnPrimitive} shorthand or a `{ primitive, optional }` record with a valid `primitive`; false otherwise
694
+ * @returns True if `value` is a valid shorthand or an exact record with a valid `primitive`; false otherwise
687
695
  */
688
- function isColumnSpec(value) {
689
- if (isColumnPrimitive(value)) return true;
690
- if (!isRecord(value)) return false;
691
- return isColumnPrimitive(value.primitive) && (value.optional === void 0 || isBoolean(value.optional));
696
+ var isColumnSpec = unionOf(isColumnPrimitive, recordOf({
697
+ primitive: isColumnPrimitive,
698
+ optional: isBoolean
699
+ }, ["optional"]));
700
+ /**
701
+ * Narrows an unknown value to a {@link TableSpec} — table name to exactly `{ columns }`, each column
702
+ * a valid {@link ColumnSpec}.
703
+ *
704
+ * @param value - The value to inspect
705
+ * @returns True if `value` is a table layout; false otherwise
706
+ */
707
+ function isTableSpec(value) {
708
+ return holds(() => isRecord(value) && Object.values(value).every((table) => isRecord(table) && Object.keys(table).length === 1 && isRecord(table.columns) && Object.values(table.columns).every((column) => isColumnSpec(column))));
692
709
  }
693
710
  /**
694
- * Narrows an unknown value to a {@link ColumnPrimitive}.
711
+ * Narrows an unknown value to a `PrimaryMap` (`@orkestrel/database`) — table name to a nonempty
712
+ * primary-key column name.
695
713
  *
696
714
  * @param value - The value to inspect
697
- * @returns True if `value` is `'string'`, `'integer'`, `'number'`, or `'boolean'`; false otherwise
715
+ * @returns True if every value is a nonempty string; false otherwise
698
716
  */
699
- var isColumnPrimitive = literalOf("string", "integer", "number", "boolean");
717
+ function isPrimaryMap(value) {
718
+ return holds(() => isRecord(value) && Object.values(value).every((key) => isNonEmptyString(key)));
719
+ }
700
720
  /**
701
- * Narrows an unknown value to a {@link DatabaseDefinition} — a non-empty `id` and `driver`, a
702
- * `tables` record whose every value is `{ columns: record of valid ColumnSpec }`, plus optional
703
- * `primary`, `indexes`, and finite `version` schema configuration. The boundary guard a
704
- * {@link import('./types.js').DefinitionStoreInterface} applies to an untrusted persisted blob
705
- * before trusting it as a definition (never an `as`).
721
+ * Narrows an unknown value to an `IndexMap` (`@orkestrel/database`) — table name to its index
722
+ * groups, each group a nonempty list of nonempty column names.
706
723
  *
707
724
  * @param value - The value to inspect
708
- * @returns True if `value` is a complete {@link DatabaseDefinition}; false otherwise
725
+ * @returns True if every value is a list of such groups; false otherwise
709
726
  */
710
- function isDatabaseDefinition(value) {
711
- if (!isRecord(value)) return false;
712
- if ("keys" in value) return false;
713
- if (!isNonEmptyString(value.id) || !isNonEmptyString(value.driver)) return false;
714
- if (!isRecord(value.tables)) return false;
715
- for (const table of Object.values(value.tables)) {
716
- if (!isRecord(table) || !isRecord(table.columns)) return false;
717
- for (const column of Object.values(table.columns)) if (!isColumnSpec(column)) return false;
718
- }
719
- if (value.primary !== void 0) {
720
- if (!isRecord(value.primary)) return false;
721
- for (const key of Object.values(value.primary)) if (!isNonEmptyString(key)) return false;
722
- }
723
- if (value.indexes !== void 0) {
724
- if (!isRecord(value.indexes)) return false;
725
- for (const groups of Object.values(value.indexes)) {
726
- if (!isArray(groups)) return false;
727
- for (const group of groups) {
728
- if (!isArray(group) || group.length === 0) return false;
729
- for (const column of group) if (!isNonEmptyString(column)) return false;
730
- }
731
- }
732
- }
733
- if (value.version !== void 0 && !isFiniteNumber(value.version)) return false;
734
- return true;
727
+ function isIndexMap(value) {
728
+ return holds(() => isRecord(value) && Object.values(value).every((groups) => isArray(groups) && groups.every((group) => isArray(group) && group.length > 0 && group.every((column) => isNonEmptyString(column)))));
735
729
  }
730
+ /**
731
+ * Narrows an unknown value to exactly a {@link DatabaseDefinition} — a non-empty `id` and `driver`,
732
+ * a {@link TableSpec}, plus optional `primary`, `indexes`, and finite `version` schema configuration,
733
+ * refusing every other key. The boundary guard a {@link import('./types.js').DefinitionStoreInterface}
734
+ * applies to an untrusted persisted blob before trusting it as a definition (never an `as`).
735
+ *
736
+ * @remarks
737
+ * A lockstep test holds this guard to {@link import('./shapers.js').definitionShape}.
738
+ *
739
+ * @param value - The value to inspect
740
+ * @returns True if `value` is a complete {@link DatabaseDefinition}; false otherwise
741
+ */
742
+ var isDatabaseDefinition = recordOf({
743
+ id: isNonEmptyString,
744
+ driver: isNonEmptyString,
745
+ tables: isTableSpec,
746
+ primary: isPrimaryMap,
747
+ indexes: isIndexMap,
748
+ version: isFiniteNumber
749
+ }, [
750
+ "primary",
751
+ "indexes",
752
+ "version"
753
+ ]);
736
754
  // src/core/shapers.ts
737
755
  /**
738
756
  * Describes the shape of {@link import('./factories.js').createPromptTool}'s call arguments — `to` names the
@@ -820,9 +838,9 @@ var taskDraftShape = objectShape({
820
838
  description: "Extra attempts after the first on failure; overrides the phase default. Omitted means none."
821
839
  })),
822
840
  timeout: optionalShape(integerShape({
823
- min: 0,
841
+ min: 1,
824
842
  max: MAX_TIMER_MS,
825
- description: "Persisted per-attempt deadline in milliseconds (0 disables it); omitted means no deadline."
843
+ description: "Per-attempt deadline in milliseconds. Omit the key for no deadline."
826
844
  }))
827
845
  });
828
846
  /**
@@ -842,6 +860,7 @@ var phaseDraftShape = objectShape({
842
860
  tasks: arrayShape(taskDraftShape, { description: "The phase tasks; they run CONCURRENTLY." }),
843
861
  concurrency: optionalShape(integerShape({
844
862
  min: 1,
863
+ max: Number.MAX_SAFE_INTEGER,
845
864
  description: "Max tasks in flight at once (a resource throttle); omitted means unbounded."
846
865
  })),
847
866
  bail: optionalShape(literalShape([true, false], { description: "Per-phase failure-policy override; omitted inherits the workflow bail." }))
@@ -924,13 +943,22 @@ var workflowStepsShape = objectShape({
924
943
  */
925
944
  var workspaceToolShape = unionShape(objectShape({
926
945
  operation: literalShape(["read"], { description: "Read a whole text file's text by path." }),
927
- path: stringShape({ description: "The path of the file to read." })
946
+ path: stringShape({
947
+ min: 1,
948
+ description: "The path of the file to read."
949
+ })
928
950
  }), objectShape({ operation: literalShape(["list"], { description: "List every file in the workspace." }) }), objectShape({
929
951
  operation: literalShape(["has"], { description: "Check whether a file exists at the path." }),
930
- path: stringShape({ description: "The path to check for." })
952
+ path: stringShape({
953
+ min: 1,
954
+ description: "The path to check for."
955
+ })
931
956
  }), objectShape({
932
957
  operation: literalShape(["search"], { description: "Search every text file for a query, returning each hit." }),
933
- query: stringShape({ description: "The text (or regular-expression source) to search for." }),
958
+ query: stringShape({
959
+ min: 1,
960
+ description: "The text (or regular-expression source) to search for."
961
+ }),
934
962
  regex: optionalShape(booleanShape({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
935
963
  sensitive: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
936
964
  limit: optionalShape(integerShape({
@@ -939,7 +967,10 @@ var workspaceToolShape = unionShape(objectShape({
939
967
  }))
940
968
  }), objectShape({
941
969
  operation: literalShape(["replace"], { description: "Replace a query with a replacement across every text file." }),
942
- query: stringShape({ description: "The text (or regular-expression source) to replace." }),
970
+ query: stringShape({
971
+ min: 1,
972
+ description: "The text (or regular-expression source) to replace."
973
+ }),
943
974
  replacement: stringShape({ description: "The text to substitute for each match." }),
944
975
  regex: optionalShape(booleanShape({ description: "Treat the query as a regular expression. Defaults to false (a literal substring)." })),
945
976
  sensitive: optionalShape(booleanShape({ description: "Match case-sensitively. Defaults to true (set false for case-insensitive)." })),
@@ -949,11 +980,17 @@ var workspaceToolShape = unionShape(objectShape({
949
980
  }))
950
981
  }), objectShape({
951
982
  operation: literalShape(["write"], { description: "Create or overwrite a whole file with content." }),
952
- path: stringShape({ description: "The path of the file to write." }),
983
+ path: stringShape({
984
+ min: 1,
985
+ description: "The path of the file to write."
986
+ }),
953
987
  content: stringShape({ description: "The full new contents of the file." })
954
988
  }), objectShape({
955
989
  operation: literalShape(["splice"], { description: "Replace a 1-based range of an existing text file (from inclusive, to exclusive) with content." }),
956
- path: stringShape({ description: "The path of the text file to edit." }),
990
+ path: stringShape({
991
+ min: 1,
992
+ description: "The path of the text file to edit."
993
+ }),
957
994
  content: stringShape({ description: "The text to splice in place of the range." }),
958
995
  fromLine: integerShape({
959
996
  min: 1,
@@ -973,19 +1010,34 @@ var workspaceToolShape = unionShape(objectShape({
973
1010
  })
974
1011
  }), objectShape({
975
1012
  operation: literalShape(["prepend"], { description: "Add content to the start of a file (creating it when absent)." }),
976
- path: stringShape({ description: "The path of the file to prepend to." }),
1013
+ path: stringShape({
1014
+ min: 1,
1015
+ description: "The path of the file to prepend to."
1016
+ }),
977
1017
  content: stringShape({ description: "The text to add at the start of the file." })
978
1018
  }), objectShape({
979
1019
  operation: literalShape(["append"], { description: "Add content to the end of a file (creating it when absent)." }),
980
- path: stringShape({ description: "The path of the file to append to." }),
1020
+ path: stringShape({
1021
+ min: 1,
1022
+ description: "The path of the file to append to."
1023
+ }),
981
1024
  content: stringShape({ description: "The text to add at the end of the file." })
982
1025
  }), objectShape({
983
1026
  operation: literalShape(["move"], { description: "Rename or move a file (overwriting an occupied target)." }),
984
- from: stringShape({ description: "The current path of the file." }),
985
- to: stringShape({ description: "The new path for the file." })
1027
+ from: stringShape({
1028
+ min: 1,
1029
+ description: "The current path of the file."
1030
+ }),
1031
+ to: stringShape({
1032
+ min: 1,
1033
+ description: "The new path for the file."
1034
+ })
986
1035
  }), objectShape({
987
1036
  operation: literalShape(["remove"], { description: "Delete a file from the workspace." }),
988
- path: stringShape({ description: "The path of the file to remove." })
1037
+ path: stringShape({
1038
+ min: 1,
1039
+ description: "The path of the file to remove."
1040
+ })
989
1041
  }), objectShape({ operation: literalShape(["workspaces"], { description: "List the workspaces you can move between (each id, file count, and whether it is active), so you can pick an id to switch to." }) }), objectShape({
990
1042
  operation: literalShape(["switch"], { description: "Switch the active workspace to the one with this id (get ids from the \"workspaces\" operation). Edit and read operations then target it." }),
991
1043
  id: stringShape({ description: "The id of the workspace to make active (from the \"workspaces\" listing)." })
@@ -1004,12 +1056,41 @@ var columnSpecShape = unionShape(columnPrimitiveShape, objectShape({
1004
1056
  }));
1005
1057
  /** Describes a {@link import('./types.js').TableSpec} — table name to `{ columns }`, each column a {@link columnSpecShape}. */
1006
1058
  var tableSpecShape = recordShape(objectShape({ columns: recordShape(columnSpecShape, { description: "Column name to its primitive or its { primitive, optional } spec." }) }), { description: "Table name to its column layout." });
1059
+ /** Describes a {@link import('./types.js').DatabaseDefinition}'s `primary` map — table name to its primary-key column. */
1060
+ var primaryShape = recordShape(stringShape({ min: 1 }), { description: "Table name to its primary-key column." });
1061
+ /** Describes a {@link import('./types.js').DatabaseDefinition}'s `indexes` map — table name to its index column groups. */
1062
+ var indexesShape = recordShape(arrayShape(arrayShape(stringShape({ min: 1 }), {
1063
+ min: 1,
1064
+ description: "One nonempty index group of column names."
1065
+ }), { description: "Zero or more index groups declared for the table." }), { description: "Table name to its index column groups." });
1066
+ /**
1067
+ * Describes a stored {@link import('./types.js').DatabaseDefinition} exactly: the owned record
1068
+ * {@link import('./stores/DatabaseDefinitionStore.js').DatabaseDefinitionStore} reads and writes.
1069
+ *
1070
+ * @remarks
1071
+ * {@link import('./validators.js').isDatabaseDefinition} is the guard the store runs, and a lockstep
1072
+ * test holds it to this shape. The compiled contract fills a refusal's faults.
1073
+ */
1074
+ var definitionShape = objectShape({
1075
+ id: stringShape({
1076
+ min: 1,
1077
+ description: "The database id."
1078
+ }),
1079
+ driver: stringShape({
1080
+ min: 1,
1081
+ description: "The registered driver key."
1082
+ }),
1083
+ tables: tableSpecShape,
1084
+ primary: optionalShape(primaryShape),
1085
+ indexes: optionalShape(indexesShape),
1086
+ version: optionalShape(numberShape({ description: "The schema version for open-time reconciliation." }))
1087
+ });
1007
1088
  /**
1008
1089
  * Describes one key value for the database tool and the relation tool — a string or number; the
1009
1090
  * array form (multiple keys, positional) resolves first, so an array argument is read as many keys
1010
1091
  * rather than one.
1011
1092
  */
1012
- var keyShape = unionShape(arrayShape(unionShape(stringShape(), numberShape()), { description: "Multiple row keys, positional — a miss at an index is undefined there." }), stringShape({ description: "One row key." }), numberShape({ description: "One row key." }));
1093
+ var keyShape = unionShape(arrayShape(unionShape(stringShape(), numberShape()), { description: "Multiple row keys, positional — a miss at an index is null there." }), stringShape({ description: "One row key." }), numberShape({ description: "One row key." }));
1013
1094
  /** Describes a loose row — a flat object of column name to JSON value. */
1014
1095
  var rowShape = recordShape(jsonShape(), { description: "A row as a flat object of column name to value." });
1015
1096
  /**
@@ -1086,11 +1167,8 @@ var databaseToolShape = unionShape(objectShape({
1086
1167
  min: 1,
1087
1168
  description: "The registered driver key. Defaults to \"memory\"."
1088
1169
  })),
1089
- primary: optionalShape(recordShape(stringShape({ min: 1 }), { description: "Table name to its primary-key column." })),
1090
- indexes: optionalShape(recordShape(arrayShape(arrayShape(stringShape({ min: 1 }), {
1091
- min: 1,
1092
- description: "One nonempty index group of column names."
1093
- }), { description: "Zero or more index groups declared for the table." }), { description: "Table name to its index column groups." })),
1170
+ primary: optionalShape(primaryShape),
1171
+ indexes: optionalShape(indexesShape),
1094
1172
  version: optionalShape(numberShape({ description: "The schema version for open-time reconciliation." }))
1095
1173
  }), objectShape({
1096
1174
  operation: literalShape(["tables"], { description: "List a database's table names." }),
@@ -1299,8 +1377,8 @@ var relationToolShape = unionShape(objectShape({
1299
1377
  * optional `candidates` array to check against the inferred schema.
1300
1378
  *
1301
1379
  * @remarks
1302
- * `samples` requires at least one element (`min: 1`) — an empty array parses to `undefined`,
1303
- * surfaced by the handler as a typed `TOOL` {@link import('./errors.js').ToolboxError}. When
1380
+ * `samples` requires at least one element (`min: 1`) — the Tool door refuses an empty array with
1381
+ * a `ToolError` carrying code `ARGUMENTS` (`@orkestrel/tool`). When
1304
1382
  * `candidates` is present (any array, including empty), the handler compiles a contract from the
1305
1383
  * freshly inferred schema and checks each candidate against it with a strict guard (`.is`, no
1306
1384
  * coercion) — the opposite of {@link import('./factories.js').createEndpointTool}'s normalizing
@@ -1315,6 +1393,20 @@ var inferToolShape = objectShape({
1315
1393
  enum: optionalShape(booleanShape({ description: "Infer enum constraints from repeated literal values. Defaults to false." })),
1316
1394
  candidates: optionalShape(arrayShape(jsonShape(), { description: "Optional values to check against the freshly inferred schema. When present, the tool returns a per-candidate verdict (strict — no coercion) alongside the inferred parameters." }))
1317
1395
  });
1396
+ // src/core/contracts.ts
1397
+ /**
1398
+ * Holds the compiled {@link import('./shapers.js').definitionShape}, whose `audit` fills the faults a
1399
+ * refused stored definition carries.
1400
+ *
1401
+ * @example
1402
+ * ```ts
1403
+ * import { definitionContract } from '@orkestrel/toolbox'
1404
+ *
1405
+ * definitionContract.audit({ id: 'shop', driver: 'memory', tables: {}, keys: {} })
1406
+ * // [{ reason: 'extra', path: ['keys'] }]
1407
+ * ```
1408
+ */
1409
+ var definitionContract = createContract(definitionShape);
1318
1410
  // src/core/helpers.ts
1319
1411
  /**
1320
1412
  * Returns the ancestry identifier of a workflow in a run chain — `workflow:<id>`.
@@ -1563,7 +1655,7 @@ function inferRelationCode(error) {
1563
1655
  function expandInclude(paths, depth) {
1564
1656
  return Iterator.from(paths ?? []).reduce((include, path) => {
1565
1657
  const segments = path.split(".");
1566
- if (segments.length > depth || segments.some((segment) => segment.length === 0)) throw new ToolboxError("TOOL", `malformed include path '${path}'`, {
1658
+ if (segments.length > depth || segments.some((segment) => segment.length === 0)) throw new ToolboxError("TOOL", `malformed include path ${preview(path)}; send dot-separated relation names with no empty segment and at most ${String(depth)} segments`, {
1567
1659
  path,
1568
1660
  depth
1569
1661
  });
@@ -1619,7 +1711,7 @@ function expandInclude(paths, depth) {
1619
1711
  function resolveRelationManager(managers, name) {
1620
1712
  if (name !== void 0) {
1621
1713
  const manager = managers[name];
1622
- if (manager === void 0) throw new ToolboxError("TOOL", `unknown relation manager '${name}'`, {
1714
+ if (manager === void 0) throw new ToolboxError("TOOL", `unknown relation manager ${preview(name)}; send manager as one of ${renderCandidates(Object.keys(managers))}`, {
1623
1715
  manager: name,
1624
1716
  managers: Object.keys(managers)
1625
1717
  });
@@ -1631,7 +1723,7 @@ function resolveRelationManager(managers, name) {
1631
1723
  const manager = managers[single];
1632
1724
  if (manager !== void 0) return manager;
1633
1725
  }
1634
- throw new ToolboxError("TOOL", "no relation manager resolved for the call", { managers: names });
1726
+ throw new ToolboxError("TOOL", `no relation manager resolved for the call; send manager as one of ${renderCandidates(names)}`, { managers: names });
1635
1727
  }
1636
1728
  /**
1637
1729
  * Resolves a `model` name against a live {@link RelationManagerInterface} — the pure model-lookup
@@ -1643,7 +1735,7 @@ function resolveRelationManager(managers, name) {
1643
1735
  * @returns The model's {@link ModelInterface}
1644
1736
  */
1645
1737
  function resolveRelationModel(manager, name) {
1646
- if (!manager.has(name)) throw new ToolboxError("TOOL", `unknown model '${name}'`, {
1738
+ if (!manager.has(name)) throw new ToolboxError("TOOL", `unknown model ${preview(name)}; send model as one of ${renderCandidates(manager.names())}`, {
1647
1739
  model: name,
1648
1740
  models: manager.names()
1649
1741
  });
@@ -1737,7 +1829,21 @@ function clampQuery(query, cap) {
1737
1829
  };
1738
1830
  }
1739
1831
  /**
1740
- * Resolves an owned workflow input through the supplied flat or draft contracts.
1832
+ * Resolves an owned workflow input through the supplied flat or draft contracts, or refuses it with
1833
+ * what to send.
1834
+ *
1835
+ * @remarks
1836
+ * Empty input is the wrapped definition. A `phases` key with no `steps` key takes the draft form,
1837
+ * and everything else takes the advertised flat form, so a blob carrying both keys refuses `phases`
1838
+ * as undeclared rather than ignoring it, and a blob carrying neither is told to send `steps`.
1839
+ *
1840
+ * @param owned - The call's owned JSON arguments
1841
+ * @param definition - The wrapped definition empty input runs
1842
+ * @param steps - The compiled flat form
1843
+ * @param draft - The compiled draft form
1844
+ * @returns The definition the input resolves to
1845
+ * @throws {import('@orkestrel/tool').ToolError} Thrown with code `ARGUMENTS` when the chosen form refuses the input, carrying `context.faults`
1846
+ *
1741
1847
  * @example
1742
1848
  * ```ts
1743
1849
  * import { createContract } from '@orkestrel/contract'
@@ -1750,12 +1856,196 @@ function clampQuery(query, cap) {
1750
1856
  */
1751
1857
  function resolveWorkflowTarget(owned, definition, steps, draft) {
1752
1858
  if (Object.keys(owned).length === 0) return definition;
1753
- if (isArray(owned.steps)) {
1754
- const flat = steps.parse(owned);
1755
- return flat === void 0 ? void 0 : expandSteps(flat);
1859
+ return Object.hasOwn(owned, "phases") && !Object.hasOwn(owned, "steps") ? completeDraft(requireToolCall(draft, owned)) : expandSteps(requireToolCall(steps, owned));
1860
+ }
1861
+ /**
1862
+ * Renders the candidates a refusal offers, each encoded and length-capped and at most
1863
+ * `REFUSAL_LIST_LIMIT` of them named, so a long or multiline name cannot fake or balloon the message.
1864
+ *
1865
+ * @param names - The candidates to offer
1866
+ * @returns The encoded candidates joined with `, `, empty when there are none
1867
+ *
1868
+ * @example
1869
+ * ```ts
1870
+ * import { renderCandidates } from '@src/core'
1871
+ *
1872
+ * renderCandidates(['read', 'list']) // '"read", "list"'
1873
+ * ```
1874
+ */
1875
+ function renderCandidates(names) {
1876
+ return renderList(names.map((name) => preview(name)));
1877
+ }
1878
+ /**
1879
+ * Caps the complete text of a refusal at `FAULT_TEXT_LIMIT` characters, saying where it stopped.
1880
+ *
1881
+ * @remarks
1882
+ * A refusal goes back to a model whose context window can be small. Use this where a message joins
1883
+ * parts this package cannot bound one by one: a head beside a report, or an upstream error's text.
1884
+ *
1885
+ * @param text - The refusal text
1886
+ * @returns The text unchanged when it fits, otherwise cut to the limit and well formed
1887
+ *
1888
+ * @example
1889
+ * ```ts
1890
+ * import { limitRefusal } from '@src/core'
1891
+ *
1892
+ * limitRefusal('send steps') // 'send steps'
1893
+ * limitRefusal('x'.repeat(5000)).length // 2048
1894
+ * ```
1895
+ */
1896
+ function limitRefusal(text) {
1897
+ if (text.length <= FAULT_TEXT_LIMIT) return text;
1898
+ return `${text.slice(0, FAULT_TEXT_LIMIT - 62).toWellFormed()}… the message stops here; correct what it names and call again`;
1899
+ }
1900
+ /**
1901
+ * Reads a call's arguments through a compiled contract, or refuses them with what to send.
1902
+ *
1903
+ * @remarks
1904
+ * Uses the contract admission door and translates refusals to tool errors.
1905
+ * Tools with object and union roots enforce `createTool`'s `contract` and use this helper to type
1906
+ * the parsed arguments. The workflow tool uses this door under `strict: false` because it accepts
1907
+ * several authoring forms while advertising the flat form.
1908
+ *
1909
+ * @param contract - The compiled contract of the arguments
1910
+ * @param value - The arguments the call sent
1911
+ * @returns The admitted value, which may retain input identity under a union
1912
+ * @throws {import('@orkestrel/tool').ToolError} Thrown with code `ARGUMENTS`, carrying `context.faults` when the arguments could be read
1913
+ *
1914
+ * @example
1915
+ * ```ts
1916
+ * import { createContract, numberShape, objectShape } from '@orkestrel/contract'
1917
+ * import { requireToolCall } from '@src/core'
1918
+ *
1919
+ * const contract = createContract(objectShape({ amount: numberShape() }))
1920
+ * requireToolCall(contract, { amount: '3' }) // { amount: 3 }
1921
+ * requireToolCall(contract, { amount: 3, extra: 1 }) // throws ToolError ('ARGUMENTS')
1922
+ * ```
1923
+ */
1924
+ function requireToolCall(contract, value) {
1925
+ const read = admitValue(contract, value);
1926
+ if (!read.success) {
1927
+ if (read.error.cause === void 0) throw buildArgumentRefusal(contract.schema, value, read.error.faults);
1928
+ throw new ToolError("ARGUMENTS", "Arguments could not be read; send one JSON object of plain values", void 0, { cause: read.error.cause });
1756
1929
  }
1757
- const parsed = draft.parse(owned);
1758
- return parsed === void 0 ? void 0 : completeDraft(parsed);
1930
+ return read.value.value;
1931
+ }
1932
+ /**
1933
+ * Builds an argument refusal from the supplied faults, auditing the schema when no faults were supplied.
1934
+ *
1935
+ * @remarks
1936
+ * Uses the contract's union reporting: a selected arm names its own faults, and a value selecting
1937
+ * no arm receives a bounded summary of alternatives. Supplied faults remain unchanged.
1938
+ *
1939
+ * @param schema - The compiled schema the arguments are held to
1940
+ * @param value - The arguments the call sent
1941
+ * @param faults - The faults admission reported
1942
+ * @returns The `ToolError` to throw, with code `ARGUMENTS` and the faults in `context.faults`
1943
+ *
1944
+ * @example
1945
+ * ```ts
1946
+ * import { createContract, literalShape, objectShape, stringShape, unionShape } from '@orkestrel/contract'
1947
+ * import { buildArgumentRefusal } from '@src/core'
1948
+ *
1949
+ * const { schema } = createContract(
1950
+ * unionShape(
1951
+ * objectShape({ operation: literalShape(['read']), path: stringShape() }),
1952
+ * objectShape({ operation: literalShape(['list']) }),
1953
+ * ),
1954
+ * )
1955
+ * buildArgumentRefusal(schema, { operation: 'read' }, []).message
1956
+ * // 'path: missing; expected string'
1957
+ * ```
1958
+ */
1959
+ function buildArgumentRefusal(schema, value, faults) {
1960
+ const listed = faults.length === 0 ? createContract(schemaToShape(schema)).audit(value) : faults;
1961
+ return new ToolError("ARGUMENTS", describeFaults(listed, schema), { faults: listed });
1962
+ }
1963
+ /**
1964
+ * Picks the effective value of a positive numeric tool option: the configured value when it is a
1965
+ * positive safe integer, and the fallback when it is omitted.
1966
+ *
1967
+ * @param value - The configured option, or `undefined`
1968
+ * @param fallback - The default an omitted option takes
1969
+ * @param subject - The name the refusal gives the option
1970
+ * @returns The effective value
1971
+ * @throws {ToolboxError} Thrown with code `TOOL` when `value` is present and is anything else, because `NaN` as a bound passes every comparison
1972
+ *
1973
+ * @example
1974
+ * ```ts
1975
+ * import { resolveBound } from '@src/core'
1976
+ *
1977
+ * resolveBound(undefined, 100, 'database limit') // 100
1978
+ * resolveBound(25, 100, 'database limit') // 25
1979
+ * resolveBound(0, 100, 'database limit') // throws ToolboxError ('TOOL')
1980
+ * ```
1981
+ */
1982
+ function resolveBound(value, fallback, subject) {
1983
+ if (value === void 0) return fallback;
1984
+ if (isPositiveSafeInteger(value)) return value;
1985
+ throw new ToolboxError("TOOL", `${subject} must be a positive safe integer; received ${preview(value)}; omit it for the default ${String(fallback)}`, { received: preview(value) });
1986
+ }
1987
+ /**
1988
+ * Reads the active workspace for an operation that edits a file that must already exist, or refuses.
1989
+ *
1990
+ * @param manager - The manager the workspace tool drives
1991
+ * @param operation - The operation that needs an active workspace
1992
+ * @returns The active workspace
1993
+ * @throws {ToolboxError} Thrown with code `TOOL` when no workspace is active, and creates none
1994
+ *
1995
+ * @example
1996
+ * ```ts
1997
+ * import { createWorkspaceManager } from '@orkestrel/workspace'
1998
+ * import { requireActiveWorkspace } from '@src/core'
1999
+ *
2000
+ * const manager = createWorkspaceManager()
2001
+ * const workspace = manager.add()
2002
+ * requireActiveWorkspace(manager, 'remove') === workspace // true
2003
+ * ```
2004
+ */
2005
+ function requireActiveWorkspace(manager, operation) {
2006
+ const active = manager.active;
2007
+ if (active !== void 0) return active;
2008
+ throw new ToolboxError("TOOL", `operation ${preview(operation)} needs an active workspace and none is; call "write" to create one, or "workspaces" then "switch"`, { operation });
2009
+ }
2010
+ /**
2011
+ * Describes a stored definition that names another id, naming the id it holds and the id it must hold.
2012
+ *
2013
+ * @param id - The database id the row is stored under
2014
+ * @param held - The id the stored definition names
2015
+ * @returns The refusal message
2016
+ *
2017
+ * @example
2018
+ * ```ts
2019
+ * import { describeDefinitionMismatch } from '@src/core'
2020
+ *
2021
+ * describeDefinitionMismatch('shop', 'other')
2022
+ * // 'database "shop" holds a definition that names id "other"; its id must be "shop"; ...'
2023
+ * ```
2024
+ */
2025
+ function describeDefinitionMismatch(id, held) {
2026
+ const required = preview(id);
2027
+ return `database ${required} holds a definition that names id ${preview(held)}; its id must be ${required}; read the row through your own table and set its id to ${required}, or delete it with store.delete(${required})`;
2028
+ }
2029
+ /**
2030
+ * Describes a stored definition this package refuses to read, naming its first fault and the remedy.
2031
+ *
2032
+ * @param id - The database id the row is stored under
2033
+ * @param faults - The refused definition's faults
2034
+ * @returns The refusal message
2035
+ *
2036
+ * @example
2037
+ * ```ts
2038
+ * import { describeDefinitionRefusal } from '@src/core'
2039
+ *
2040
+ * describeDefinitionRefusal('shop', [{ reason: 'extra', path: ['keys'] }])
2041
+ * // 'database "shop" holds a definition this version refuses (extra key "keys" at the root; ...'
2042
+ * ```
2043
+ */
2044
+ function describeDefinitionRefusal(id, faults) {
2045
+ const [first] = faults;
2046
+ const path = first === void 0 ? [] : extractPath(first);
2047
+ const held = preview(id);
2048
+ return `database ${held} holds a definition this version refuses (${first?.reason === "extra" ? `extra key ${preview(path.at(-1))} at ${path.length === 1 ? "the root" : preview(path.slice(0, -1).join("."))}` : first === void 0 ? "a value its shape cannot name" : `${first.reason} at ${path.length === 0 ? "the root" : preview(path.join("."))}`}; a definition written before this change can carry it); read the row through your own table and ${first?.reason === "extra" ? "rewrite it without the key" : "rewrite it to the declared shape"}, or delete it with store.delete(${held})`;
1759
2049
  }
1760
2050
  // src/core/compilers.ts
1761
2051
  /**
@@ -1809,8 +2099,8 @@ function compileColumnPrimitive(primitive) {
1809
2099
  * {@link import('../factories.js').createMemoryDefinitionStore} builds. It implements the same
1810
2100
  * {@link DefinitionStoreInterface} contract as
1811
2101
  * {@link import('./DatabaseDefinitionStore.js').DatabaseDefinitionStore}: this store copies on
1812
- * write and on read; the table-backed store narrows an untrusted stored blob and reports
1813
- * `undefined` for a malformed one.
2102
+ * write and on read; the table-backed store narrows an untrusted stored blob and refuses a malformed
2103
+ * one.
1814
2104
  *
1815
2105
  * @remarks
1816
2106
  * A plain `Map<string, DatabaseDefinition>` (the definition is already pure,
@@ -1894,12 +2184,15 @@ var MemoryDefinitionStore = class {
1894
2184
  * and keeps the row type flat (`definition` reads back as `unknown`).
1895
2185
  *
1896
2186
  * - **`set(definition)` upserts under the definition's own `id`** (no separate id param) — it
1897
- * writes the row `{ id: definition.id, definition }`.
2187
+ * writes the row `{ id: definition.id, definition }`, and first refuses what `get` would refuse,
2188
+ * so no row is written that `get` cannot read.
1898
2189
  * - **`get(id)` resolves the stored definition for an id**, narrowing the opaque JSON column back
1899
2190
  * to a {@link DatabaseDefinition} ({@link import('../validators.js').isDatabaseDefinition} — the
1900
- * boundary narrow for an untrusted storage read), or `undefined` if none is stored
1901
- * or the stored blob is malformed.
1902
- * - **`delete(id)` drops a definition by id**; an absent id is a no-op (no throw).
2191
+ * boundary narrow for an untrusted storage read), or `undefined` if no row is stored. A present
2192
+ * row whose blob is malformed or names another id throws, because reading it as absent would let
2193
+ * a later `set` overwrite it.
2194
+ * - **`delete(id)` drops a definition by id** without reading it, the remedy for a refused row; an
2195
+ * absent id is a no-op (no throw).
1903
2196
  *
1904
2197
  * The public surface is exactly `get` / `set` / `delete` — no extra members (the method
1905
2198
  * bijection with {@link DefinitionStoreInterface}).
@@ -1930,21 +2223,47 @@ var DatabaseDefinitionStore = class {
1930
2223
  * {@link DatabaseDefinition}.
1931
2224
  *
1932
2225
  * @param id - The database id to read
1933
- * @returns The stored {@link DatabaseDefinition}, or `undefined` when none is stored or the
1934
- * stored blob is malformed
2226
+ * @returns The stored {@link DatabaseDefinition}, or `undefined` when no row is stored
2227
+ * @throws {ToolboxError} Thrown with code `DATABASE` when the row's blob is malformed or names another id, with the faults under `context.faults`
1935
2228
  */
1936
2229
  async get(id) {
1937
2230
  const row = await this.#table.get(id);
1938
2231
  if (row === void 0) return void 0;
1939
- return isDatabaseDefinition(row.definition) ? row.definition : void 0;
2232
+ const definition = row.definition;
2233
+ if (!isDatabaseDefinition(definition)) {
2234
+ const faults = definitionContract.audit(definition);
2235
+ throw new ToolboxError("DATABASE", describeDefinitionRefusal(id, faults), {
2236
+ id,
2237
+ faults
2238
+ });
2239
+ }
2240
+ if (definition.id !== id) {
2241
+ const faults = [{
2242
+ reason: "type",
2243
+ path: ["id"],
2244
+ expected: "literal",
2245
+ received: preview(definition.id)
2246
+ }];
2247
+ throw new ToolboxError("DATABASE", describeDefinitionMismatch(id, definition.id), {
2248
+ id,
2249
+ faults
2250
+ });
2251
+ }
2252
+ return definition;
1940
2253
  }
1941
2254
  /**
1942
2255
  * Inserts or replaces a definition under its own `id` — the written row is `{ id, definition }`.
1943
2256
  *
1944
2257
  * @param definition - The config to persist; its own `id` is the row key (no separate id param)
1945
2258
  * @returns A promise settling after the row is written
2259
+ * @throws {ToolboxError} Thrown with code `DATABASE` before any write when the definition fails the guard `get` reads, with the faults under `context.faults`
1946
2260
  */
1947
2261
  async set(definition) {
2262
+ if (!isDatabaseDefinition(definition)) {
2263
+ const unchecked = definition;
2264
+ const faults = definitionContract.audit(unchecked);
2265
+ throw new ToolboxError("DATABASE", limitRefusal(`a definition store writes only a complete definition; correct each line and call set again:\n${describeFaults(faults, definitionContract.schema)}`), { faults });
2266
+ }
1948
2267
  await this.#table.set({
1949
2268
  id: definition.id,
1950
2269
  definition
@@ -2043,7 +2362,7 @@ var DatabaseResolver = class {
2043
2362
  const definition = await this.#store.get(id);
2044
2363
  if (definition !== void 0) {
2045
2364
  const factory = this.#drivers[definition.driver];
2046
- if (factory === void 0) throw new ToolboxError("TOOL", `unknown driver '${definition.driver}'`, {
2365
+ if (factory === void 0) throw new ToolboxError("TOOL", `database ${preview(id)} names unknown driver ${preview(definition.driver)}; register it in the tool's drivers, one of ${renderCandidates(Object.keys(this.#drivers))}`, {
2047
2366
  id,
2048
2367
  driver: definition.driver
2049
2368
  });
@@ -2060,7 +2379,7 @@ var DatabaseResolver = class {
2060
2379
  return handle;
2061
2380
  }
2062
2381
  }
2063
- throw new ToolboxError("TOOL", `unknown database '${id}'`, { id });
2382
+ throw new ToolboxError("TOOL", `unknown database ${preview(id)}; define it first with { "operation": "create", "id", "tables" }`, { id });
2064
2383
  }
2065
2384
  };
2066
2385
  // src/core/factories.ts
@@ -2117,7 +2436,8 @@ function createToolFunction(tools, name) {
2117
2436
  * targets deeper than {@link import('./constants.js').MAX_WORKFLOW_CHAIN} are rejected before
2118
2437
  * agent or runner activity. When {@link import('./types.js').AgentFunctionOptions.runner} is
2119
2438
  * supplied, the adapter binds a recursion-safe {@link createWorkflowTool} onto the agent's
2120
- * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The
2439
+ * `context.tools`, propagating opaque leaves, raw agents, lineage, and the native store. The agent
2440
+ * needs an `authority` of its own to run that tool, because an agent with none denies every call. The
2121
2441
  * task's cancellation propagates through the Agent's native per-run signal seam:
2122
2442
  * `agent.generate({ signal: controller.signal })` resolves a partial `AgentResult` on a cancel
2123
2443
  * (never rejects), projected onto its exact JSON fields.
@@ -2263,7 +2583,12 @@ function createWorkflowDraftContract() {
2263
2583
  * It is also the propagation carrier
2264
2584
  * {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
2265
2585
  * handler receives only the model-supplied `args`, the immutable lineage is closed over at bind
2266
- * time. The authored target is appended and its zero-based depth derived from workflow tags.
2586
+ * time. The authored target is appended and its zero-based depth derived from workflow tags. That
2587
+ * bound tool wraps the containing workflow's id over no phases, and its empty-args call always
2588
+ * refuses as a cycle before the runner, so the wrapper never runs.
2589
+ *
2590
+ * `parameters` advertises the flat form alone, so the tool is built `strict: false` and its handler
2591
+ * runs the door ({@link import('./helpers.js').requireToolCall}) on the form the arguments take.
2267
2592
  *
2268
2593
  * **Widened authoring surface (additive — the canonical contract and runner stay strict and
2269
2594
  * unchanged).** A 2B model reliably calls the tool but cannot reliably emit the full nested
@@ -2278,9 +2603,11 @@ function createWorkflowDraftContract() {
2278
2603
  * super-set.
2279
2604
  *
2280
2605
  * The universal tool-handler contract: returns the plain run summary
2281
- * (`{ status, count, durable?, fault? }`) on success, throws a typed `ToolboxError` on Toolbox
2282
- * boundary failures — malformed authored args (`TOOL`), or an over-deep / cyclic nested run
2283
- * (`DEPTH`) — and preserves genuine runner `WorkflowError`s. The `ToolManagerInterface` isolates every throw into the canonical tool result's
2606
+ * (`{ status, count, durable?, fault? }`) on success. Malformed authored args throw a `ToolError`
2607
+ * with code `ARGUMENTS` (`@orkestrel/tool`) whose text lists what to correct, carrying
2608
+ * `context.faults`; a wrapped definition the strict contract refuses throws `TOOL`, and an over-deep
2609
+ * or cyclic nested run throws `DEPTH`. Genuine runner `WorkflowError`s are preserved, including
2610
+ * `SCHEDULE` for a target with no task. The `ToolManagerInterface` isolates every throw into the canonical tool result's
2284
2611
  * top-level `error`, so nothing escapes the run. `options.lineage` is the propagation carrier
2285
2612
  * across a workflow → agent → workflow chain. Opaque `functions` remain host leaves; raw `agents`
2286
2613
  * are contextually adapted through {@link createWorkflowFunctions}; native checkpoint persistence
@@ -2348,20 +2675,26 @@ function createWorkflowTool(definition, runner, options) {
2348
2675
  description: WORKFLOW_TOOL_DESCRIPTION,
2349
2676
  summary: WORKFLOW_TOOL_SUMMARY,
2350
2677
  ...parameters === void 0 ? {} : { parameters },
2678
+ strict: false,
2351
2679
  async execute(args) {
2352
2680
  const cloned = attempt(() => cloneJSONRecord(args));
2353
- if (!cloned.success) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
2354
- const owned = cloned.value;
2355
- const target = resolveWorkflowTarget(owned, definition, steps, draft);
2356
- if (target === void 0 || !strict.is(target)) throw new ToolboxError("TOOL", "malformed workflow definition", { workflow: definition.id });
2681
+ if (!cloned.success) throw new ToolError("ARGUMENTS", `Arguments could not be read; send one JSON object such as ${JSON.stringify(WORKFLOW_TOOL_FLAT_EXAMPLE)}`, void 0, { cause: cloned.error });
2682
+ const target = resolveWorkflowTarget(cloned.value, definition, steps, draft);
2683
+ if (!strict.is(target)) {
2684
+ const faults = strict.audit(target);
2685
+ throw new ToolboxError("TOOL", limitRefusal(`workflow ${preview(definition.id)} cannot run; send steps to author another one, or correct it:\n${describeFaults(faults, strict.schema)}`), {
2686
+ workflow: definition.id,
2687
+ faults
2688
+ });
2689
+ }
2357
2690
  const tag = tagWorkflow(target.id);
2358
- if (lineage.includes(tag)) throw new ToolboxError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
2691
+ if (lineage.includes(tag)) throw new ToolboxError("DEPTH", `workflow ${preview(target.id)} is already an ancestor (cycle); send steps with a new name, or phases with a new id`, {
2359
2692
  workflow: target.id,
2360
2693
  lineage: [...lineage]
2361
2694
  });
2362
2695
  const targetLineage = extendLineage(lineage, tag);
2363
2696
  const depth = deriveWorkflowDepth(targetLineage);
2364
- if (depth > 8) throw new ToolboxError("DEPTH", `nested workflow exceeds max depth 8`, {
2697
+ if (depth > 8) throw new ToolboxError("DEPTH", `nested workflow exceeds max depth 8; finish this task without nesting another workflow`, {
2365
2698
  workflow: target.id,
2366
2699
  depth,
2367
2700
  max: 8
@@ -2394,17 +2727,20 @@ function createWorkflowTool(definition, runner, options) {
2394
2727
  * active workspace by id (lenient — an unknown id is a no-op reporting `switched: false`, never a
2395
2728
  * throw).
2396
2729
  *
2397
- * No-active rule (the ergonomic seam): a writing op (write / splice / prepend / append / move /
2398
- * remove / replace) run when `manager.active` is `undefined` auto-creates and activates a default
2399
- * workspace (`manager.add()`) so the model can start writing; a pure-read op (read / list /
2400
- * has / search) against no active workspace returns the empty result (`undefined` / `[]` /
2401
- * `false`), never creating one and never throwing.
2402
- *
2403
- * The handler conforms to the universal tool-handler contract: it `contract.parse`s
2404
- * the args, throws a `TOOL` `ToolboxError` when no operation arm matched (a malformed / unknown
2405
- * operation), else `switch`es on `op.operation` and returns the plain result — letting a
2406
- * `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` / `RANGE`)
2407
- * propagate uncaught. The range edit is the flat `'splice'` op: its flat caret integers are
2730
+ * No-active rule (the ergonomic seam): an op that can create the file it names (write / prepend /
2731
+ * append) run when `manager.active` is `undefined` auto-creates and activates a default workspace
2732
+ * (`manager.add()`) so the model can start writing; an op that edits a file that must already exist
2733
+ * (splice / move / remove / replace) refuses with a `TOOL` `ToolboxError` and creates none; a
2734
+ * pure-read op (read / list / has / search) against no active workspace returns the empty result
2735
+ * (`null` / `[]` / `false`), never creating one and never throwing.
2736
+ *
2737
+ * The Tool door enforces the operation union through `contract`, repairing lossless scalar leaves
2738
+ * and refusing malformed args with a `ToolError` carrying code `ARGUMENTS`. A selected operation
2739
+ * reports its own faults; an unknown operation reports a bounded summary of alternatives.
2740
+ * The handler then `switch`es on `op.operation` and returns the plain result
2741
+ * — letting a `WorkspaceError` raised by the live workspace (`MISSING` / `MODALITY` / `PATTERN` /
2742
+ * `RANGE`) propagate uncaught. `read` returns `null` for a path that holds no text, because JSON
2743
+ * tells that from an empty file. The range edit is the flat `'splice'` op: its flat caret integers are
2408
2744
  * reassembled into a `Range` (`@orkestrel/workspace`) by `rangeOf` and fed to the workspace's
2409
2745
  * ranged `write`.
2410
2746
  *
@@ -2425,15 +2761,13 @@ function createWorkflowTool(definition, runner, options) {
2425
2761
  function createWorkspaceTool(options) {
2426
2762
  const manager = options?.manager ?? createWorkspaceManager(options?.store === void 0 ? void 0 : { store: options.store });
2427
2763
  const contract = createContract(workspaceToolShape);
2428
- const parameters = schemaToParameters(contract.schema);
2429
2764
  return createTool({
2430
2765
  name: options?.name ?? "workspace",
2431
2766
  description: options?.description ?? WORKSPACE_TOOL_DESCRIPTION,
2432
2767
  summary: WORKSPACE_TOOL_SUMMARY,
2433
- ...parameters === void 0 ? {} : { parameters },
2768
+ contract: workspaceToolShape,
2434
2769
  execute(args) {
2435
- const op = contract.parse(args);
2436
- if (op === void 0) throw new ToolboxError("TOOL", "unknown or malformed operation", { args });
2770
+ const op = requireToolCall(contract, args);
2437
2771
  if (op.operation === "workspaces") {
2438
2772
  const activeId = manager.active?.id;
2439
2773
  return manager.workspaces().map((workspace) => ({
@@ -2455,7 +2789,7 @@ function createWorkspaceTool(options) {
2455
2789
  }
2456
2790
  const active = manager.active;
2457
2791
  switch (op.operation) {
2458
- case "read": return active?.read(op.path);
2792
+ case "read": return active?.read(op.path) ?? null;
2459
2793
  case "list": return (active?.files() ?? []).map((file) => ({
2460
2794
  path: file.path,
2461
2795
  state: file.state,
@@ -2469,7 +2803,7 @@ function createWorkspaceTool(options) {
2469
2803
  ...op.sensitive === void 0 ? {} : { sensitive: op.sensitive },
2470
2804
  ...op.limit === void 0 ? {} : { limit: op.limit }
2471
2805
  }) ?? [];
2472
- case "replace": return (active ?? manager.add()).replace(op.query, op.replacement, {
2806
+ case "replace": return requireActiveWorkspace(manager, op.operation).replace(op.query, op.replacement, {
2473
2807
  ...op.regex === void 0 ? {} : { regex: op.regex },
2474
2808
  ...op.sensitive === void 0 ? {} : { sensitive: op.sensitive },
2475
2809
  ...op.limit === void 0 ? {} : { limit: op.limit }
@@ -2483,7 +2817,7 @@ function createWorkspaceTool(options) {
2483
2817
  };
2484
2818
  }
2485
2819
  case "splice": {
2486
- const workspace = active ?? manager.add();
2820
+ const workspace = requireActiveWorkspace(manager, op.operation);
2487
2821
  workspace.write(op.path, op.content, rangeOf(op.fromLine, op.fromColumn, op.toLine, op.toColumn));
2488
2822
  return {
2489
2823
  path: op.path,
@@ -2507,7 +2841,7 @@ function createWorkspaceTool(options) {
2507
2841
  };
2508
2842
  }
2509
2843
  case "move": {
2510
- const workspace = active ?? manager.add();
2844
+ const workspace = requireActiveWorkspace(manager, op.operation);
2511
2845
  return {
2512
2846
  from: op.from,
2513
2847
  to: op.to,
@@ -2515,7 +2849,7 @@ function createWorkspaceTool(options) {
2515
2849
  };
2516
2850
  }
2517
2851
  case "remove": {
2518
- const workspace = active ?? manager.add();
2852
+ const workspace = requireActiveWorkspace(manager, op.operation);
2519
2853
  return {
2520
2854
  path: op.path,
2521
2855
  removed: workspace.remove(op.path)
@@ -2530,16 +2864,20 @@ function createWorkspaceTool(options) {
2530
2864
  * from `registry` and runs it to completion for one delegated `task`.
2531
2865
  *
2532
2866
  * @remarks
2533
- * The universal tool-handler contract: validates the call args against
2534
- * {@link import('./shapers.js').agentToolShape}, assembles an `AgentJobInput` (`task` seeds the
2867
+ * The universal tool-handler contract: the Tool door holds the call args to
2868
+ * {@link import('./shapers.js').agentToolShape}, refusing a malformed call with a `ToolError`
2869
+ * carrying code `ARGUMENTS`. The handler assembles an `AgentJobInput` (`task` seeds the
2535
2870
  * sub-agent's conversation as a single `user` message; `provider` / `tools` / `system` fall
2536
- * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults), rehydrates the sub-agent through
2871
+ * back to the tool's own {@link import('./types.js').AgentToolOptions} defaults, and `authority` is
2872
+ * the tool's own, never the call's), rehydrates the sub-agent through
2537
2873
  * `registry.build`, runs it with `agent.generate()`, and returns the settled
2538
- * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or a malformed call, throws a typed `TOOL`
2874
+ * `AgentResult.content` string (the sub-agent's final text). A missing / unresolvable `provider`, or
2875
+ * tools with no configured `authority`, throws a typed `TOOL`
2539
2876
  * {@link import('./errors.js').ToolboxError}; a delegation that would exceed
2540
2877
  * {@link import('./constants.js').AGENT_TOOL_DEPTH}, or re-enter an already-delegated agent (a
2541
2878
  * cycle), throws a typed `DEPTH` {@link import('./errors.js').ToolboxError}, each isolated
2542
- * by the `ToolManagerInterface` into the canonical tool result's top-level `error`.
2879
+ * by the `ToolManagerInterface` into the canonical tool result's top-level `error`. A `depth` that
2880
+ * is not a nonnegative safe integer throws `TOOL` at construction.
2543
2881
  *
2544
2882
  * `AgentInterface` (`@orkestrel/agent`) exposes no teardown method — a bound sub-agent's
2545
2883
  * lifetime is the single `generate()` call this handler awaits; there is nothing to release
@@ -2566,30 +2904,33 @@ function createWorkspaceTool(options) {
2566
2904
  */
2567
2905
  function createAgentTool(registry, options) {
2568
2906
  const contract = createContract(agentToolShape);
2569
- const parameters = schemaToParameters(contract.schema);
2570
2907
  const depth = options?.depth ?? 0;
2908
+ if (!isNonNegativeInteger(depth) || !Number.isSafeInteger(depth)) throw new ToolboxError("TOOL", `agent tool depth must be a nonnegative safe integer; received ${preview(depth)}; omit it for a direct root`, { received: preview(depth) });
2571
2909
  const ancestry = options?.ancestry ?? [];
2572
2910
  return createTool({
2573
2911
  name: options?.name ?? "agent",
2574
2912
  description: options?.description ?? AGENT_TOOL_DESCRIPTION,
2575
2913
  summary: AGENT_TOOL_SUMMARY,
2576
- ...parameters === void 0 ? {} : { parameters },
2914
+ contract: agentToolShape,
2577
2915
  async execute(args) {
2578
- const call = contract.parse(args);
2579
- if (call === void 0) throw new ToolboxError("TOOL", "malformed agent-delegation call", { args });
2916
+ const call = requireToolCall(contract, args);
2580
2917
  const provider = call.provider ?? options?.provider;
2581
- if (provider === void 0) throw new ToolboxError("TOOL", "no provider resolved for the delegated agent", { task: call.task });
2582
- if (depth + 1 > 8) throw new ToolboxError("DEPTH", `delegation exceeds max agent depth 8`, {
2918
+ if (provider === void 0) throw new ToolboxError("TOOL", "no provider resolved for the delegated agent; send provider as a registered provider key", { task: call.task });
2919
+ if (depth + 1 > 8) throw new ToolboxError("DEPTH", `delegation exceeds max agent depth 8; carry out this task without delegating`, {
2583
2920
  provider,
2584
2921
  depth,
2585
2922
  max: 8
2586
2923
  });
2587
2924
  const tag = tagAgent(provider);
2588
- if (ancestry.includes(tag)) throw new ToolboxError("DEPTH", `agent '${provider}' is already an ancestor (cycle)`, {
2925
+ if (ancestry.includes(tag)) throw new ToolboxError("DEPTH", `agent ${preview(provider)} is already an ancestor (cycle); send another provider, or carry out this task without delegating`, {
2589
2926
  provider,
2590
2927
  ancestry: [...ancestry]
2591
2928
  });
2592
2929
  const tools = call.tools ?? options?.tools;
2930
+ if (tools !== void 0 && tools.length > 0 && options?.authority === void 0) throw new ToolboxError("TOOL", "this agent tool has no authority to decide tool calls, so its sub-agent can load no tools; send tools: [] to delegate with none, or have the host set authority on this tool", {
2931
+ provider,
2932
+ tools: [...tools]
2933
+ });
2593
2934
  const system = call.system ?? options?.system;
2594
2935
  const agent = registry.build({
2595
2936
  provider,
@@ -2598,7 +2939,8 @@ function createAgentTool(registry, options) {
2598
2939
  content: call.task
2599
2940
  }],
2600
2941
  ...system === void 0 ? {} : { system },
2601
- ...tools === void 0 ? {} : { tools }
2942
+ ...tools === void 0 ? {} : { tools },
2943
+ ...options?.authority === void 0 ? {} : { authority: options.authority }
2602
2944
  });
2603
2945
  const result = await agent.generate();
2604
2946
  if (options?.store !== void 0) {
@@ -2619,12 +2961,13 @@ function createAgentTool(registry, options) {
2619
2961
  * description when `summary` is set, keeping the advertised tool list compact for a small model.
2620
2962
  * This tool is the on-demand expansion seam: given a registered tool's `name`, it looks the tool
2621
2963
  * up through `tools.tool(name)` and returns its full `description` (falling back to `summary` when a
2622
- * tool has no `description` of its own, then a placeholder when it has neither).
2964
+ * tool has no `description` of its own, then an explicit `null` when it has neither).
2623
2965
  *
2624
- * The universal tool-handler contract: validates the call args against
2625
- * {@link import('./shapers.js').describeToolShape}, returns the plain description string on
2626
- * success, throws a typed `TOOL` {@link import('./errors.js').ToolboxError} on a malformed call
2627
- * or an unknown tool name.
2966
+ * The universal tool-handler contract: the Tool door holds the call args to
2967
+ * {@link import('./shapers.js').describeToolShape}, refusing a malformed call with a `ToolError`
2968
+ * carrying code `ARGUMENTS`. The handler returns the plain description on success, and throws a typed
2969
+ * `TOOL` {@link import('./errors.js').ToolboxError} that lists the registered names on an unknown
2970
+ * tool name.
2628
2971
  *
2629
2972
  * @param tools - The `ToolManagerInterface` (`@orkestrel/tool`) whose registered tools this
2630
2973
  * tool can describe
@@ -2644,18 +2987,22 @@ function createAgentTool(registry, options) {
2644
2987
  */
2645
2988
  function createDescribeTool(tools) {
2646
2989
  const contract = createContract(describeToolShape);
2647
- const parameters = schemaToParameters(contract.schema);
2648
2990
  return createTool({
2649
2991
  name: DESCRIBE_TOOL_NAME,
2650
2992
  description: DESCRIBE_TOOL_DESCRIPTION,
2651
2993
  summary: DESCRIBE_TOOL_SUMMARY,
2652
- ...parameters === void 0 ? {} : { parameters },
2994
+ contract: describeToolShape,
2653
2995
  async execute(args) {
2654
- const call = contract.parse(args);
2655
- if (call === void 0) throw new ToolboxError("TOOL", "malformed describe call", { args });
2996
+ const call = requireToolCall(contract, args);
2656
2997
  const tool = tools.tool(call.name);
2657
- if (tool === void 0) throw new ToolboxError("TOOL", `unknown tool '${call.name}'`, { name: call.name });
2658
- return tool.description ?? tool.summary ?? "<no description>";
2998
+ if (tool === void 0) {
2999
+ const names = tools.tools().map((registered) => registered.name);
3000
+ throw new ToolboxError("TOOL", `unknown tool ${preview(call.name)}; send name as one of ${renderCandidates(names)}`, {
3001
+ name: call.name,
3002
+ names
3003
+ });
3004
+ }
3005
+ return tool.description ?? tool.summary ?? null;
2659
3006
  }
2660
3007
  });
2661
3008
  }
@@ -2664,8 +3011,9 @@ function createDescribeTool(tools) {
2664
3011
  * and blocks until the addressed terminal answers, returning the resolved values record.
2665
3012
  *
2666
3013
  * @remarks
2667
- * The universal tool-handler contract: validates the call args against
2668
- * {@link import('./shapers.js').promptToolShape}, parses the call's schema through
3014
+ * The universal tool-handler contract: the Tool door holds the call args to
3015
+ * {@link import('./shapers.js').promptToolShape}, refusing a malformed call with a `ToolError`
3016
+ * carrying code `ARGUMENTS`. The handler parses the call's schema through
2669
3017
  * `@orkestrel/form`, constructs the live form, and passes it to `TerminalManagerInterface.ask`.
2670
3018
  * `from` is fixed at construction
2671
3019
  * ({@link import('./types.js').PromptToolOptions.from}) — never read from the model-supplied
@@ -2673,7 +3021,7 @@ function createDescribeTool(tools) {
2673
3021
  * `TerminalError('DEADLOCK')`, re-surfaced as a typed `DEADLOCK`
2674
3022
  * {@link import('./errors.js').ToolboxError}; an expired form re-surfaces as `EXPIRE`; an
2675
3023
  * unknown `to` (or any other `TerminalError`) re-surfaces as `TOOL`, naming the unknown terminal
2676
- * and how many brokers the manager holds (`manager.count`). A schema `parseForm` refuses — an
3024
+ * and the mounted terminals the manager's refusal lists, encoded and bounded. A schema `parseForm` refuses — an
2677
3025
  * unknown control, a duplicate field name, a `'select'` or `'checkbox'` field without usable
2678
3026
  * `choices` — throws a typed `TOOL` error up front, so a malformed schema never parks as a form
2679
3027
  * nobody can answer.
@@ -2698,37 +3046,40 @@ function createDescribeTool(tools) {
2698
3046
  */
2699
3047
  function createPromptTool(options) {
2700
3048
  const contract = createContract(promptToolShape);
2701
- const parameters = schemaToParameters(contract.schema);
2702
3049
  return createTool({
2703
3050
  name: options.name ?? "ask",
2704
3051
  description: options.description ?? PROMPT_TOOL_DESCRIPTION,
2705
3052
  summary: PROMPT_TOOL_SUMMARY,
2706
- ...parameters === void 0 ? {} : { parameters },
3053
+ contract: promptToolShape,
2707
3054
  async execute(args) {
2708
- const call = contract.parse(args);
2709
- if (call === void 0) throw new ToolboxError("TOOL", "malformed ask call", { args });
3055
+ const call = requireToolCall(contract, args);
2710
3056
  const schema = parseForm(call.schema);
2711
- if (schema === void 0) throw new ToolboxError("TOOL", "malformed form schema", { schema: call.schema });
2712
- for (const field of schema.fields) if ((field.control === "select" || field.control === "checkbox") && !field.choices.some((choice) => choice.disabled !== true)) throw new ToolboxError("TOOL", `${field.control} field '${field.name}' has no enabled choices`, {
3057
+ if (schema === void 0) throw new ToolboxError("TOOL", `malformed form schema; send { "fields": [{ "control", "name" }] } with unique names, where control is one of ${FIELD_CONTROLS.join(", ")}, and select or checkbox carries choices of { "label", "value" }`, { schema: call.schema });
3058
+ for (const field of schema.fields) if ((field.control === "select" || field.control === "checkbox") && !field.choices.some((choice) => choice.disabled !== true)) throw new ToolboxError("TOOL", `${field.control} field ${preview(field.name)} has no enabled choices; add a choice that is not disabled`, {
2713
3059
  control: field.control,
2714
3060
  field: field.name
2715
3061
  });
2716
3062
  try {
2717
3063
  return await options.manager.ask(options.from, call.to, createForm(schema));
2718
3064
  } catch (error) {
2719
- if (isFormError(error) && error.code === "ABANDONED") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
3065
+ if (isFormError(error) && error.code === "ABANDONED") throw new ToolboxError("EXPIRE", `form to ${preview(call.to)} expired before it was answered; ask again to give it more time`, { to: call.to });
2720
3066
  const code = inferTerminalCode(error);
2721
3067
  if (code === void 0) throw error;
2722
- if (code === "DEADLOCK") throw new ToolboxError("DEADLOCK", `asking '${call.to}' would create a form cycle`, isTerminalError(error) ? error.context : {
3068
+ if (code === "DEADLOCK") throw new ToolboxError("DEADLOCK", `asking ${preview(call.to)} would create a form cycle; answer the form addressed to you first`, isTerminalError(error) ? error.context : {
2723
3069
  from: options.from,
2724
3070
  to: call.to
2725
3071
  });
2726
- if (code === "EXPIRE") throw new ToolboxError("EXPIRE", `form to '${call.to}' expired before it was answered`, { to: call.to });
2727
- if (isTerminalError(error) && error.code === "TARGET") throw new ToolboxError("TOOL", `unknown terminal '${call.to}'`, {
2728
- to: call.to,
2729
- count: options.manager.count
2730
- });
2731
- throw new ToolboxError("TOOL", `asking '${call.to}' failed`, { to: call.to });
3072
+ if (code === "EXPIRE") throw new ToolboxError("EXPIRE", `form to ${preview(call.to)} expired before it was answered; ask again to give it more time`, { to: call.to });
3073
+ if (isTerminalError(error) && error.code === "TARGET") {
3074
+ const listed = error.context?.known;
3075
+ const known = arrayOf(isString)(listed) ? listed : [];
3076
+ throw new ToolboxError("TOOL", `unknown terminal ${preview(call.to)}; ${known.length === 0 ? "no mounted terminal name is available" : `send to as one of ${renderCandidates(known)}`}`, {
3077
+ to: call.to,
3078
+ known,
3079
+ count: options.manager.count
3080
+ });
3081
+ }
3082
+ throw new ToolboxError("TOOL", `asking ${preview(call.to)} failed; ask again`, { to: call.to });
2732
3083
  }
2733
3084
  }
2734
3085
  });
@@ -2738,8 +3089,9 @@ function createPromptTool(options) {
2738
3089
  * addressed to {@link import('./types.js').AnswerToolOptions.to}, or answers one of them by id.
2739
3090
  *
2740
3091
  * @remarks
2741
- * The universal tool-handler contract: validates the call args against
2742
- * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`). `'pending'`
3092
+ * The universal tool-handler contract: the Tool door holds the call args to
3093
+ * {@link import('./shapers.js').answerToolShape} (discriminated by `operation`), refusing malformed
3094
+ * args with a `ToolError` carrying code `ARGUMENTS`. `'pending'`
2743
3095
  * returns a compact list (`{ id, from, schema }`) of every form addressed to `to`.
2744
3096
  * `'answer'` looks the form up by `id`, narrows the supplied `values` through `@orkestrel/form`,
2745
3097
  * and applies it through
@@ -2770,31 +3122,32 @@ function createPromptTool(options) {
2770
3122
  */
2771
3123
  function createAnswerTool(options) {
2772
3124
  const contract = createContract(answerToolShape);
2773
- const parameters = schemaToParameters(contract.schema);
2774
3125
  return createTool({
2775
3126
  name: options.name ?? "answer",
2776
3127
  description: options.description ?? ANSWER_TOOL_DESCRIPTION,
2777
3128
  summary: ANSWER_TOOL_SUMMARY,
2778
- ...parameters === void 0 ? {} : { parameters },
3129
+ contract: answerToolShape,
2779
3130
  async execute(args) {
2780
- const call = contract.parse(args);
2781
- if (call === void 0) throw new ToolboxError("TOOL", "malformed answer call", { args });
3131
+ const call = requireToolCall(contract, args);
2782
3132
  if (call.operation === "pending") return options.manager.pending(options.to).map((form) => ({
2783
3133
  id: form.id,
2784
3134
  from: form.from,
2785
3135
  schema: form.schema
2786
3136
  }));
2787
- if (options.manager.pending(options.to).find((entry) => entry.id === call.id) === void 0) throw new ToolboxError("ANSWER", `unknown form '${call.id}'`, {
3137
+ if (options.manager.pending(options.to).find((entry) => entry.id === call.id) === void 0) throw new ToolboxError("ANSWER", `unknown form ${preview(call.id)}; call { "operation": "pending" } for the ids you can answer`, {
2788
3138
  id: call.id,
2789
3139
  reason: "unknown"
2790
3140
  });
2791
- if (!isFormValues(call.values)) throw new ToolboxError("TOOL", "malformed form values", { values: call.values });
3141
+ if (!isFormValues(call.values)) throw new ToolboxError("TOOL", "malformed form values; send values as a record of each field name to a string, a finite number, a boolean, or a list of strings", { values: call.values });
2792
3142
  const result = options.manager.answer(options.to, call.id, call.values);
2793
- if (!result.success) throw new ToolboxError("ANSWER", `failed to answer form '${call.id}': ${result.error.reason}`, {
2794
- id: call.id,
2795
- reason: result.error.reason,
2796
- ...result.error.reason === "rejected" ? { errors: result.error.errors } : {}
2797
- });
3143
+ if (!result.success) {
3144
+ const detail = result.error.reason === "rejected" ? `; correct these fields and answer again: ${renderList(result.error.errors.map((fault) => `${preview(fault.field)}: ${preview(fault.message)}`))}` : "; call { \"operation\": \"pending\" } for the ids you can answer";
3145
+ throw new ToolboxError("ANSWER", `failed to answer form ${preview(call.id)}: ${result.error.reason}${detail}`, {
3146
+ id: call.id,
3147
+ reason: result.error.reason,
3148
+ ...result.error.reason === "rejected" ? { errors: result.error.errors } : {}
3149
+ });
3150
+ }
2798
3151
  return { answered: call.id };
2799
3152
  }
2800
3153
  });
@@ -2847,9 +3200,11 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2847
3200
  * {@link createWorkspaceTool}'s single-tool-many-operations shape).
2848
3201
  *
2849
3202
  * @remarks
2850
- * The universal tool-handler contract: validates the call args against
2851
- * {@link import('./shapers.js').databaseToolShape}, dispatches to the matching operation, and
2852
- * returns a plain result on success. A database is resolved lazily and cached for the tool's
3203
+ * The universal tool-handler contract: the Tool door holds the call args to
3204
+ * {@link import('./shapers.js').databaseToolShape}, refusing malformed args with a `ToolError`
3205
+ * carrying code `ARGUMENTS`. The handler dispatches to the matching operation and
3206
+ * returns a plain result on success. A row a `'get'` misses is an explicit `null`. A `limit` that is
3207
+ * not a positive safe integer throws `TOOL` at construction. A database is resolved lazily and cached for the tool's
2853
3208
  * lifetime — `'create'` mints one from `tables` ({@link import('./compilers.js').expandTables}) and
2854
3209
  * a registered `driver` key ({@link import('./types.js').DatabaseToolOptions.drivers}, default
2855
3210
  * `{ memory: () => createMemoryDriver() }`); any other operation addressing an uncached id falls
@@ -2907,30 +3262,28 @@ function createDatabaseDefinitionStore(driver = createMemoryDriver()) {
2907
3262
  function createDatabaseTool(options = {}) {
2908
3263
  if (options.timeout !== void 0 && (options.timeout < 0 || !isNumber(options.timeout) || !Number.isSafeInteger(options.timeout))) throw new ToolboxError("TOOL", "database timeout must be a nonnegative safe integer", { timeout: options.timeout });
2909
3264
  const contract = createContract(databaseToolShape);
2910
- const parameters = schemaToParameters(contract.schema);
2911
3265
  const handles = new Map(Object.entries(options.databases ?? {}));
2912
3266
  const drivers = options.drivers ?? { memory: createMemoryDriver };
2913
3267
  const generator = options.generator;
2914
- const cap = options.limit ?? 1e3;
3268
+ const cap = resolveBound(options.limit, DATABASE_TOOL_LIMIT, "database limit");
2915
3269
  const store = options.store;
2916
3270
  const resolver = store === void 0 ? new DatabaseResolver(handles, drivers, generator) : new DatabaseResolver(handles, drivers, generator, store);
2917
3271
  return createTool({
2918
3272
  name: options.name ?? "database",
2919
3273
  description: options.description ?? DATABASE_TOOL_DESCRIPTION,
2920
3274
  summary: DATABASE_TOOL_SUMMARY,
2921
- ...parameters === void 0 ? {} : { parameters },
3275
+ contract: databaseToolShape,
2922
3276
  async execute(args) {
2923
- const call = contract.parse(args);
2924
- if (call === void 0) throw new ToolboxError("TOOL", "malformed database call", { args });
2925
- if (options.readonly === true && DATABASE_TOOL_MUTATIONS.includes(call.operation)) throw new ToolboxError("TOOL", `operation '${call.operation}' is disabled in readonly mode`, { operation: call.operation });
3277
+ const call = requireToolCall(contract, args);
3278
+ if (options.readonly === true && DATABASE_TOOL_MUTATIONS.includes(call.operation)) throw new ToolboxError("TOOL", `operation '${call.operation}' is disabled in readonly mode; call tables, get, records, count, or aggregate`, { operation: call.operation });
2926
3279
  const read = options.timeout === void 0 ? void 0 : { signal: AbortSignal.timeout(options.timeout) };
2927
3280
  try {
2928
3281
  switch (call.operation) {
2929
3282
  case "create": {
2930
- if (resolver.has(call.id) || store !== void 0 && await store.get(call.id) !== void 0) throw new ToolboxError("TOOL", `database '${call.id}' already exists`, { id: call.id });
3283
+ if (resolver.has(call.id) || store !== void 0 && await store.get(call.id) !== void 0) throw new ToolboxError("TOOL", `database ${preview(call.id)} already exists; use it, destroy it first, or send another id`, { id: call.id });
2931
3284
  const name = call.driver ?? "memory";
2932
3285
  const factory = drivers[name];
2933
- if (factory === void 0) throw new ToolboxError("TOOL", `unknown driver '${name}'`, {
3286
+ if (factory === void 0) throw new ToolboxError("TOOL", `unknown driver ${preview(name)}; send driver as one of ${renderCandidates(Object.keys(drivers))}`, {
2934
3287
  id: call.id,
2935
3288
  driver: name
2936
3289
  });
@@ -2977,7 +3330,7 @@ function createDatabaseTool(options = {}) {
2977
3330
  const table = (await resolver.resolve(call.id)).table(call.table);
2978
3331
  const many = isArray(call.key);
2979
3332
  const keys = isArray(call.key) ? call.key : [call.key];
2980
- const rows = await table.get(keys);
3333
+ const rows = (await table.get(keys)).map((row) => row ?? null);
2981
3334
  return many ? { rows } : { row: rows[0] };
2982
3335
  }
2983
3336
  case "records": {
@@ -3042,7 +3395,7 @@ function createDatabaseTool(options = {}) {
3042
3395
  if (isToolboxError(error)) throw error;
3043
3396
  const code = inferDatabaseCode(error);
3044
3397
  if (code === void 0) throw error;
3045
- throw new ToolboxError("DATABASE", isError(error) ? error.message : String(error), {
3398
+ throw new ToolboxError("DATABASE", limitRefusal(isError(error) ? error.message : String(error)), {
3046
3399
  code,
3047
3400
  operation: call.operation,
3048
3401
  id: call.id,
@@ -3058,8 +3411,10 @@ function createDatabaseTool(options = {}) {
3058
3411
  * single-tool-many-operations shape).
3059
3412
  *
3060
3413
  * @remarks
3061
- * The universal tool-handler contract: validates the call args against
3062
- * {@link import('./shapers.js').relationToolShape}, resolves the addressed
3414
+ * The universal tool-handler contract: the Tool door holds the call args to
3415
+ * {@link import('./shapers.js').relationToolShape}, refusing malformed args with a `ToolError`
3416
+ * carrying code `ARGUMENTS`. A `limit` or `depth` that is not a positive safe integer throws `TOOL` at
3417
+ * construction, and a row `'load'` misses is an explicit `null`. The handler resolves the addressed
3063
3418
  * {@link import('@orkestrel/relation').RelationManagerInterface} — an explicit `manager` field
3064
3419
  * must match a key of {@link import('./types.js').RelationToolOptions.managers}, an omitted one
3065
3420
  * resolves to the sole registered manager, either miss throwing a typed `TOOL`
@@ -3105,24 +3460,22 @@ function createDatabaseTool(options = {}) {
3105
3460
  */
3106
3461
  function createRelationTool(options) {
3107
3462
  const contract = createContract(relationToolShape);
3108
- const parameters = schemaToParameters(contract.schema);
3109
- const depth = options.depth ?? 3;
3110
- const cap = options.limit ?? 1e3;
3463
+ const depth = resolveBound(options.depth, 3, "relation depth");
3464
+ const cap = resolveBound(options.limit, RELATION_TOOL_LIMIT, "relation limit");
3111
3465
  return createTool({
3112
3466
  name: options.name ?? "relation",
3113
3467
  description: options.description ?? RELATION_TOOL_DESCRIPTION,
3114
3468
  summary: RELATION_TOOL_SUMMARY,
3115
- ...parameters === void 0 ? {} : { parameters },
3469
+ contract: relationToolShape,
3116
3470
  async execute(args) {
3117
- const call = contract.parse(args);
3118
- if (call === void 0) throw new ToolboxError("TOOL", "malformed relation call", { args });
3471
+ const call = requireToolCall(contract, args);
3119
3472
  try {
3120
3473
  const model = resolveRelationModel(resolveRelationManager(options.managers, call.manager), call.model);
3121
3474
  switch (call.operation) {
3122
3475
  case "load": {
3123
3476
  const include = expandInclude(call.include, depth);
3124
- if (isString(call.key) || isNumber(call.key)) return { row: await model.load(call.key, include) };
3125
- return { rows: await model.load(call.key, include) };
3477
+ if (isString(call.key) || isNumber(call.key)) return { row: await model.load(call.key, include) ?? null };
3478
+ return { rows: (await model.load(call.key, include)).map((row) => row ?? null) };
3126
3479
  }
3127
3480
  case "find": {
3128
3481
  const include = expandInclude(call.include, depth);
@@ -3164,7 +3517,7 @@ function createRelationTool(options) {
3164
3517
  } catch (error) {
3165
3518
  if (isToolboxError(error)) throw error;
3166
3519
  const relation = inferRelationCode(error);
3167
- if (relation !== void 0) throw new ToolboxError("RELATION", isError(error) ? error.message : String(error), {
3520
+ if (relation !== void 0) throw new ToolboxError("RELATION", limitRefusal(isError(error) ? error.message : String(error)), {
3168
3521
  code: relation,
3169
3522
  operation: call.operation,
3170
3523
  model: call.model,
@@ -3172,7 +3525,7 @@ function createRelationTool(options) {
3172
3525
  });
3173
3526
  const database = inferDatabaseCode(error);
3174
3527
  if (database === void 0) throw error;
3175
- throw new ToolboxError("DATABASE", isError(error) ? error.message : String(error), {
3528
+ throw new ToolboxError("DATABASE", limitRefusal(isError(error) ? error.message : String(error)), {
3176
3529
  code: database,
3177
3530
  operation: call.operation
3178
3531
  });
@@ -3186,14 +3539,13 @@ function createRelationTool(options) {
3186
3539
  * {@link createEndpointTool}, wraps one concrete endpoint).
3187
3540
  *
3188
3541
  * @remarks
3189
- * The universal tool-handler contract: validates the call args against
3542
+ * The universal tool-handler contract: the Tool door holds the call args to
3190
3543
  * {@link import('./shapers.js').inferToolShape} (`samples` non-empty, `format` / `enum` optional
3191
- * booleans, `candidates` an optional array), infers a schema through `@orkestrel/contract`'s
3544
+ * booleans, `candidates` an optional array), refusing malformed args with a `ToolError` carrying code
3545
+ * `ARGUMENTS`. The handler infers a schema through `@orkestrel/contract`'s
3192
3546
  * `samplesToSchema`, wraps a non-object root as `{ value: <schema> }` through `schemaToObject` (mirrors
3193
3547
  * the tool-parameters convention every other `create*Tool` factory advertises), and returns the
3194
- * resulting parameters record. An empty `samples` array fails `inferToolShape`'s `min: 1` bound —
3195
- * `contract.parse` returns `undefined` and the handler throws a typed `TOOL`
3196
- * {@link import('./errors.js').ToolboxError}.
3548
+ * resulting parameters record.
3197
3549
  *
3198
3550
  * When `candidates` is absent, the return is the bare parameters record — unchanged from before
3199
3551
  * this array existed. When `candidates` is present (any array, including empty), the handler
@@ -3208,18 +3560,14 @@ function createRelationTool(options) {
3208
3560
  * normalizing parse accept this value", that is would {@link createEndpointTool}'s default enforcement
3209
3561
  * admit it (`checker.parse(candidate) !== undefined`) — computed for every candidate regardless of
3210
3562
  * `valid`; by the house parse/guard round-trip, a `valid: true` entry is
3211
- * always also `coercible: true`. `@orkestrel/contract`'s `explain` mirrors the normalizing
3212
- * `parse`'s leniency, not `is`'s strictness — so a strictly-invalid but coercible candidate (`7`
3213
- * against a string slot) yields `{ valid: false, coercible: true, faults: [] }`: empty faults, because
3214
- * the mismatch the normalizing parse would silently fix is not one `explain` reports. `faults`
3215
- * therefore only ever populates for a non-coercible mismatch — a wrong type the parse can't coerce
3216
- * (a boolean in a string slot), a missing required key, or an out-of-enum value — where
3217
- * `coercible: false`. `checker.is` / `.parse` / `.explain` are all total over JSON-safe input — a
3218
- * JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
3563
+ * always also `coercible: true`. `faults` is `checker.audit(candidate)`, which reports against `is`'s
3564
+ * strictness: a strictly-invalid but coercible candidate (`7` against a string slot) yields
3565
+ * `{ valid: false, coercible: true, faults: [{ reason: 'type', ... }] }`, so every invalid candidate
3566
+ * names why, an undeclared key included. `checker.is` / `.parse` / `.audit` are all total over
3567
+ * JSON-safe input — a JSON-safe hostile candidate (a `__proto__`-carrying object, deeply nested data) reaches each of them
3219
3568
  * and yields a bounded, non-throwing per-candidate verdict; a non-JSON-safe candidate (for example a
3220
- * throwing-getter `Proxy`) never reaches the checker at all — it fails the outer `args` parse
3221
- * against {@link import('./shapers.js').inferToolShape} and rejects the whole call with the same
3222
- * `TOOL` {@link import('./errors.js').ToolboxError} a malformed `samples`/`format`/`enum` throws,
3569
+ * throwing-getter `Proxy`) never reaches the checker at all — the Tool door refuses the whole call
3570
+ * with the same `ToolError` a malformed `samples`/`format`/`enum` throws,
3223
3571
  * with no per-candidate verdict produced.
3224
3572
  *
3225
3573
  * @param options - Advertised `name` / `description` overrides (see
@@ -3259,21 +3607,19 @@ function createRelationTool(options) {
3259
3607
  */
3260
3608
  function createInferTool(options) {
3261
3609
  const contract = createContract(inferToolShape);
3262
- const parameters = schemaToParameters(contract.schema);
3263
3610
  return createTool({
3264
3611
  name: options?.name ?? "infer",
3265
3612
  description: options?.description ?? INFER_TOOL_DESCRIPTION,
3266
3613
  summary: INFER_TOOL_SUMMARY,
3267
- ...parameters === void 0 ? {} : { parameters },
3614
+ contract: inferToolShape,
3268
3615
  async execute(args) {
3269
- const parsed = contract.parse(args);
3270
- if (parsed === void 0) throw new ToolboxError("TOOL", "malformed infer arguments", { args });
3616
+ const parsed = requireToolCall(contract, args);
3271
3617
  const schema = samplesToSchema(parsed.samples, {
3272
3618
  format: parsed.format ?? false,
3273
3619
  enum: parsed.enum ?? false
3274
3620
  });
3275
3621
  const result = schemaToParameters(schemaToObject(schema));
3276
- if (result === void 0) throw new ToolboxError("TOOL", "could not infer a schema", { args });
3622
+ if (result === void 0) throw new ToolboxError("TOOL", "could not infer a schema; send samples that are JSON values of one shape", { args });
3277
3623
  if (parsed.candidates === void 0) return result;
3278
3624
  const checker = createContract(schemaToShape(schema));
3279
3625
  return {
@@ -3289,7 +3635,7 @@ function createInferTool(options) {
3289
3635
  index,
3290
3636
  valid,
3291
3637
  coercible,
3292
- faults: checker.explain(candidate)
3638
+ faults: checker.audit(candidate)
3293
3639
  };
3294
3640
  })
3295
3641
  };
@@ -3306,20 +3652,19 @@ function createInferTool(options) {
3306
3652
  * `@orkestrel/contract`'s `samplesToSchema` (tuned by {@link import('./types.js').EndpointToolOptions}'s
3307
3653
  * `format` / `enum`), wrapping a non-object root as `{ value: <schema> }` through `schemaToObject` —
3308
3654
  * the same object-rooted schema is both the advertised `parameters` and, by default
3309
- * ({@link import('./types.js').EndpointToolOptions.validate} `true`), the enforced contract:
3310
- * `@orkestrel/contract`'s `schemaToShape` compiles it once (through `createContract`) into a
3311
- * `ContractInterface` whose `.parse` runs on every call's `args` before `definition.execute` — a
3655
+ * ({@link import('./types.js').EndpointToolOptions.strict} `true`), the enforced contract: the Tool
3656
+ * door converts it through `@orkestrel/contract`'s lenient `schemaToShape` and compiles it on the
3657
+ * first call, then runs on every call's `args` before `definition.execute` — a
3312
3658
  * normalizing parse, not a strict type check: a scalar is coerced to its inferred type where the
3313
3659
  * house parsers coerce (a number to/from a numeric string, a boolean from `'1'`/`'0'`/`'true'`/
3314
3660
  * `'false'`/`1`/`0`), so `definition.execute` receives the coerced value (for example `7` sent for a
3315
- * string slot arrives as `'7'`), not the raw call value. A call whose `args` fails to parse into
3316
- * a record — a required key missing, or a value not coercible to its slot's type — throws a
3317
- * typed `TOOL` {@link import('./errors.js').ToolboxError} carrying the compiled contract's
3318
- * structured `explain` faults, and `definition.execute` is never called. `format` annotations are
3319
- * never asserted, and a key outside the closed inferred schema is silently dropped rather than
3320
- * rejected (see {@link import('./types.js').EndpointToolOptions.validate}). With
3321
- * `validate: false`, the tool's `execute` passes through the model-supplied `args` to
3322
- * `definition.execute` without re-validation — the raw-passthrough opt-out. The execution context
3661
+ * string slot arrives as `'7'`), not the raw call value. A call whose `args` carries a key outside
3662
+ * the closed inferred schema, misses a required key, or holds a value not coercible to its slot's
3663
+ * type throws a `ToolError` with code `ARGUMENTS` (`@orkestrel/tool`) carrying `context.faults`, and
3664
+ * `definition.execute` is never called. `format` annotations are never asserted (see
3665
+ * {@link import('./types.js').EndpointToolOptions.strict}). With
3666
+ * `strict: false`, the tool's `execute` passes through the model-supplied `args` to
3667
+ * `definition.execute` without validation — the raw-passthrough opt-out. The execution context
3323
3668
  * the tool's `execute` receives reaches `definition.execute` unchanged whichever way `args` are
3324
3669
  * handled, so a handler that reads `context.signal` observes the caller's cancellation. Either way,
3325
3670
  * the definition's return flows back as the tool call's plain result; a throw propagates uncaught,
@@ -3328,7 +3673,7 @@ function createInferTool(options) {
3328
3673
  *
3329
3674
  * @param definition - The endpoint's identity, non-empty samples, and local handler (see
3330
3675
  * {@link import('./types.js').EndpointDefinition})
3331
- * @param options - Construction-time inference tuning and the validate opt-out (see
3676
+ * @param options - Construction-time inference tuning and the strict opt-out (see
3332
3677
  * {@link import('./types.js').EndpointToolOptions})
3333
3678
  * @returns A `ToolInterface` named `definition.name`
3334
3679
  *
@@ -3361,7 +3706,7 @@ function createInferTool(options) {
3361
3706
  * name: 'lookupUser',
3362
3707
  * arguments: { id: true, name: 'Ada' },
3363
3708
  * })
3364
- * // rejected.error -> the TOOL ToolboxError message
3709
+ * // rejected.error -> the ARGUMENTS ToolError message, which names `id`
3365
3710
  * ```
3366
3711
  */
3367
3712
  function createEndpointTool(definition, options) {
@@ -3371,29 +3716,16 @@ function createEndpointTool(definition, options) {
3371
3716
  enum: options?.enum ?? false
3372
3717
  }));
3373
3718
  const parameters = schemaToParameters(objectSchema);
3374
- if (!(options?.validate ?? true)) return createTool({
3375
- name: definition.name,
3376
- description: definition.description,
3377
- ...parameters === void 0 ? {} : { parameters },
3378
- execute(args, context) {
3379
- return definition.execute(args, context);
3380
- }
3381
- });
3382
- const contract = createContract(schemaToShape(objectSchema));
3383
3719
  return createTool({
3384
3720
  name: definition.name,
3385
3721
  description: definition.description,
3386
3722
  ...parameters === void 0 ? {} : { parameters },
3723
+ ...options?.strict === void 0 ? {} : { strict: options.strict },
3387
3724
  execute(args, context) {
3388
- const parsed = contract.parse(args);
3389
- if (parsed === void 0 || !isRecord(parsed)) throw new ToolboxError("TOOL", "malformed endpoint call arguments", {
3390
- name: definition.name,
3391
- faults: contract.explain(args)
3392
- });
3393
- return definition.execute(parsed, context);
3725
+ return definition.execute(args, context);
3394
3726
  }
3395
3727
  });
3396
3728
  }
3397
- export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_CHAIN, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentToolShape, answerToolShape, clampQuery, columnPrimitiveShape, columnSpecShape, compileColumn, compileColumnPrimitive, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowFunctions, createWorkflowTool, createWorkspaceTool, databaseToolShape, deriveWorkflowDepth, describeToolShape, expandInclude, expandSteps, expandTables, extendLineage, includeShape, inferDatabaseCode, inferRelationCode, inferTerminalCode, inferToolShape, isAgentFunction, isColumnPrimitive, isColumnSpec, isDatabaseDefinition, isToolboxError, isWorkflowLineage, keyShape, managerShape, normalizeLineage, normalizeQuery, orderShape, phaseDraftShape, promptToolShape, queryShape, relationToolShape, resolveLimit, resolveRelationManager, resolveRelationModel, resolveWorkflowTarget, rowShape, rowsShape, singleKeyShape, stepShape, summarizeWorkflow, tableSpecShape, tagAgent, tagWorkflow, taskDraftShape, workflowDraftShape, workflowStepsShape, workspaceToolShape };
3729
+ export { AGENT_TOOL_DEPTH, AGENT_TOOL_DESCRIPTION, AGENT_TOOL_NAME, AGENT_TOOL_SUMMARY, ANSWER_TOOL_DESCRIPTION, ANSWER_TOOL_NAME, ANSWER_TOOL_SUMMARY, DATABASE_TOOL_DESCRIPTION, DATABASE_TOOL_LIMIT, DATABASE_TOOL_MUTATIONS, DATABASE_TOOL_NAME, DATABASE_TOOL_SUMMARY, DESCRIBE_TOOL_DESCRIPTION, DESCRIBE_TOOL_NAME, DESCRIBE_TOOL_SUMMARY, DatabaseDefinitionStore, DatabaseResolver, INFER_TOOL_DESCRIPTION, INFER_TOOL_NAME, INFER_TOOL_SUMMARY, MAX_WORKFLOW_CHAIN, MemoryDefinitionStore, PROMPT_TOOL_DESCRIPTION, PROMPT_TOOL_NAME, PROMPT_TOOL_SUMMARY, RELATION_TOOL_DEPTH, RELATION_TOOL_DESCRIPTION, RELATION_TOOL_LIMIT, RELATION_TOOL_NAME, RELATION_TOOL_SUMMARY, ToolboxError, WORKFLOW_TOOL_DESCRIPTION, WORKFLOW_TOOL_FLAT_EXAMPLE, WORKFLOW_TOOL_NAME, WORKFLOW_TOOL_NESTED_EXAMPLE, WORKFLOW_TOOL_SUMMARY, WORKSPACE_TOOL_DESCRIPTION, WORKSPACE_TOOL_EXAMPLE, WORKSPACE_TOOL_NAME, WORKSPACE_TOOL_SUMMARY, agentToolShape, answerToolShape, buildArgumentRefusal, clampQuery, columnPrimitiveShape, columnSpecShape, compileColumn, compileColumnPrimitive, completeDraft, completePhaseDraft, completeTaskDraft, conditionShape, createAgentFunction, createAgentTool, createAnswerTool, createDatabaseDefinitionStore, createDatabaseTool, createDescribeTool, createEndpointTool, createInferTool, createMemoryDefinitionStore, createPromptTool, createRelationTool, createToolFunction, createWorkflowDraftContract, createWorkflowFunctions, createWorkflowTool, createWorkspaceTool, databaseToolShape, definitionContract, definitionShape, deriveWorkflowDepth, describeDefinitionMismatch, describeDefinitionRefusal, describeToolShape, expandInclude, expandSteps, expandTables, extendLineage, includeShape, indexesShape, inferDatabaseCode, inferRelationCode, inferTerminalCode, inferToolShape, isAgentFunction, isColumnPrimitive, isColumnSpec, isDatabaseDefinition, isIndexMap, isPrimaryMap, isTableSpec, isToolboxError, isWorkflowLineage, keyShape, limitRefusal, managerShape, normalizeLineage, normalizeQuery, orderShape, phaseDraftShape, primaryShape, promptToolShape, queryShape, relationToolShape, renderCandidates, requireActiveWorkspace, requireToolCall, resolveBound, resolveLimit, resolveRelationManager, resolveRelationModel, resolveWorkflowTarget, rowShape, rowsShape, singleKeyShape, stepShape, summarizeWorkflow, tableSpecShape, tagAgent, tagWorkflow, taskDraftShape, workflowDraftShape, workflowStepsShape, workspaceToolShape };
3398
3730
 
3399
3731
  //# sourceMappingURL=index.js.map