@rulvar/core 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3112,11 +3112,14 @@ interface ResolvedToolset {
3112
3112
  /** The empty toolset (no tools declared anywhere). */
3113
3113
  declare function emptyToolset(): ResolvedToolset;
3114
3114
  /**
3115
- * Expands sources, validates every tool name and duplicate names across
3116
- * the whole toolset (ConfigError at spawn time), and computes the
3117
- * toolsetHash over contracts sorted by name.
3118
- */
3119
- declare function resolveToolset(specs: ToolsOption | undefined, session: ToolSourceSession): Promise<ResolvedToolset>;
3115
+ * Expands registered names and sources, validates every tool name and
3116
+ * duplicate names across the whole toolset (ConfigError at spawn time),
3117
+ * and computes the toolsetHash over contracts sorted by name. The
3118
+ * `toolsets` registry is the engine's `defaults.toolsets` snapshot;
3119
+ * without one, string entries fail with the same unknown-name error as
3120
+ * a miss, so nothing outside the declared registry is ever reachable.
3121
+ */
3122
+ declare function resolveToolset(specs: ToolsOption | undefined, session: ToolSourceSession, toolsets?: Record<string, ToolsOption>): Promise<ResolvedToolset>;
3120
3123
  //#endregion
3121
3124
  //#region src/journal/termination.d.ts
3122
3125
  /** The frozen limits vector written into termination.init. */
@@ -6081,9 +6084,12 @@ declare function tierWithinCaps(tier: StructuredOutputTier, caps: ModelCaps): bo
6081
6084
  /**
6082
6085
  * Renders the registry into the shared agent vocabulary card. Sorted,
6083
6086
  * deterministic, byte-stable; an empty registry renders explicitly so
6084
- * the planner never guesses at unregistered agentTypes.
6087
+ * the planner never guesses at unregistered agentTypes. When the engine
6088
+ * registers toolsets, their names render as a closing line (v1.17.0
6089
+ * review P1-3): those are the ONLY values valid as string entries of a
6090
+ * tools option, so the planner never invents a registry name.
6085
6091
  */
6086
- declare function profileCard(profiles: Record<string, AgentProfile> | undefined): string;
6092
+ declare function profileCard(profiles: Record<string, AgentProfile> | undefined, toolsets?: Record<string, ToolsOption>): string;
6087
6093
  //#endregion
6088
6094
  //#region src/model/projector.d.ts
6089
6095
  /** The provider family of an adapter: `provider` when set, else `id`. */
package/dist/index.js CHANGED
@@ -2597,9 +2597,14 @@ function toolContract(def) {
2597
2597
  * Toolset resolution and hashing (M3-T01): expands the per-spawn tools
2598
2598
  * array (ToolDef | ToolSource | string) into the spawn's toolset snapshot,
2599
2599
  * validates names and collisions, and derives toolsetHash from the
2600
- * contracts only. The snapshot is captured at spawn time and stays stable
2601
- * for the agent's lifetime; provider-side drift of a source's tools
2602
- * changes the content key of NEW spawns only.
2600
+ * contracts only. A string entry names a registered toolset from
2601
+ * `createEngine({ defaults: { toolsets } })` (v1.17.0 review P1-3): the
2602
+ * registry snapshot belongs to the engine configuration, so the same
2603
+ * name expands identically for direct calls, agent profiles, and the
2604
+ * sandbox dialect, and an unknown name is a typed ConfigError at spawn
2605
+ * time, before any provider call. The snapshot is captured at spawn
2606
+ * time and stays stable for the agent's lifetime; provider-side drift
2607
+ * of a source's tools changes the content key of NEW spawns only.
2603
2608
  *
2604
2609
  * Docs: https://docs.rulvar.com/guide/tools.
2605
2610
  */
@@ -2615,21 +2620,35 @@ function isToolDef(spec) {
2615
2620
  return typeof spec !== "string" && spec.kind === "tool";
2616
2621
  }
2617
2622
  /**
2618
- * Expands sources, validates every tool name and duplicate names across
2619
- * the whole toolset (ConfigError at spawn time), and computes the
2620
- * toolsetHash over contracts sorted by name.
2623
+ * Expands registered names and sources, validates every tool name and
2624
+ * duplicate names across the whole toolset (ConfigError at spawn time),
2625
+ * and computes the toolsetHash over contracts sorted by name. The
2626
+ * `toolsets` registry is the engine's `defaults.toolsets` snapshot;
2627
+ * without one, string entries fail with the same unknown-name error as
2628
+ * a miss, so nothing outside the declared registry is ever reachable.
2621
2629
  */
2622
- async function resolveToolset(specs, session) {
2630
+ async function resolveToolset(specs, session, toolsets) {
2623
2631
  if (specs === void 0 || specs.length === 0) return emptyToolset();
2624
2632
  const tools = [];
2625
2633
  for (const spec of specs) {
2626
- if (typeof spec === "string") throw new ConfigError(`tools by registered name ('${spec}') are not supported here: pass ToolDef or ToolSource values. Registered toolset names exist only for the dynamic orchestrator's spawn_agent toolsetRef (https://docs.rulvar.com/guide/tools)`);
2634
+ if (typeof spec === "string") {
2635
+ const named = toolsets?.[spec];
2636
+ if (named === void 0) throw new ConfigError(`unknown registered toolset '${spec}': register it under defaults.toolsets (https://docs.rulvar.com/guide/tools)`);
2637
+ for (const entry of named) {
2638
+ if (typeof entry === "string") throw new ConfigError(`registered toolset '${spec}' contains the name '${entry}': registry values hold ToolDef or ToolSource entries, never other registered names`);
2639
+ if (isToolDef(entry)) {
2640
+ tools.push(entry);
2641
+ continue;
2642
+ }
2643
+ tools.push(...await entry.tools(session));
2644
+ }
2645
+ continue;
2646
+ }
2627
2647
  if (isToolDef(spec)) {
2628
2648
  tools.push(spec);
2629
2649
  continue;
2630
2650
  }
2631
- const imported = await spec.tools(session);
2632
- tools.push(...imported);
2651
+ tools.push(...await spec.tools(session));
2633
2652
  }
2634
2653
  const seen = /* @__PURE__ */ new Map();
2635
2654
  for (const def of tools) {
@@ -6676,7 +6695,7 @@ function affordableOutputTokens(pricing, remainingUsd, estimatedInputTokens) {
6676
6695
  //#region src/model/profile-card.ts
6677
6696
  function toolNamesOf(profile) {
6678
6697
  return (profile.tools ?? []).map((entry) => {
6679
- if (typeof entry === "string") return `${entry} (profile toolset)`;
6698
+ if (typeof entry === "string") return `${entry} (registered toolset)`;
6680
6699
  if ("kind" in entry && entry.kind === "tool") return entry.name;
6681
6700
  return `${entry.id}:* (tool source)`;
6682
6701
  });
@@ -6684,11 +6703,19 @@ function toolNamesOf(profile) {
6684
6703
  /**
6685
6704
  * Renders the registry into the shared agent vocabulary card. Sorted,
6686
6705
  * deterministic, byte-stable; an empty registry renders explicitly so
6687
- * the planner never guesses at unregistered agentTypes.
6688
- */
6689
- function profileCard(profiles) {
6706
+ * the planner never guesses at unregistered agentTypes. When the engine
6707
+ * registers toolsets, their names render as a closing line (v1.17.0
6708
+ * review P1-3): those are the ONLY values valid as string entries of a
6709
+ * tools option, so the planner never invents a registry name.
6710
+ */
6711
+ function profileCard(profiles, toolsets) {
6712
+ const toolsetNames = Object.keys(toolsets ?? {}).sort();
6713
+ const toolsetsLine = toolsetNames.length === 0 ? void 0 : `Registered toolsets (valid string entries of a tools option): ${toolsetNames.join(", ")}.`;
6690
6714
  const names = Object.keys(profiles ?? {}).sort();
6691
- if (profiles === void 0 || names.length === 0) return "Agent profiles: none registered. Calls take no agentType.";
6715
+ if (profiles === void 0 || names.length === 0) {
6716
+ const empty = "Agent profiles: none registered. Calls take no agentType.";
6717
+ return toolsetsLine === void 0 ? empty : `${empty}\n${toolsetsLine}`;
6718
+ }
6692
6719
  const lines = ["Agent profiles (agentType values):"];
6693
6720
  for (const name of names) {
6694
6721
  const profile = profiles[name];
@@ -6700,6 +6727,7 @@ function profileCard(profiles) {
6700
6727
  if (profile.estCost !== void 0) lines.push(` estCost: ${profile.estCost.toFixed(2)} USD`);
6701
6728
  if (profile.escalation !== void 0) lines.push(` escalation: flavor ${profile.escalation.flavor ?? "A"} (opt-in)`);
6702
6729
  }
6730
+ if (toolsetsLine !== void 0) lines.push(toolsetsLine);
6703
6731
  return lines.join("\n");
6704
6732
  }
6705
6733
  //#endregion
@@ -10325,7 +10353,7 @@ function createCtx(internals, rootWorkflow) {
10325
10353
  if (escalation.flavor === "B" && escalation.deadlineMs === void 0) throw new ConfigError("escalation flavor 'B' requires an explicit deadlineMs");
10326
10354
  }
10327
10355
  const declaredTools = opts.tools ?? profile?.tools ?? [];
10328
- const toolset = await resolveToolset(escalation === void 0 ? declaredTools : [...declaredTools, escalateTool()], { runId: internals.runId });
10356
+ const toolset = await resolveToolset(escalation === void 0 ? declaredTools : [...declaredTools, escalateTool()], { runId: internals.runId }, internals.defaults.toolsets);
10329
10357
  const layers = [
10330
10358
  callLayer,
10331
10359
  profileLayer,
@@ -13028,10 +13056,10 @@ function createEngine(options) {
13028
13056
  pruneRun,
13029
13057
  profileCard: (names) => {
13030
13058
  const registered = defaults.profiles ?? {};
13031
- if (names === void 0) return profileCard(registered);
13059
+ if (names === void 0) return profileCard(registered, defaults.toolsets);
13032
13060
  const filtered = {};
13033
13061
  for (const name of names) if (registered[name] !== void 0) filtered[name] = registered[name];
13034
- return profileCard(filtered);
13062
+ return profileCard(filtered, defaults.toolsets);
13035
13063
  }
13036
13064
  };
13037
13065
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",