@orkestrel/agent 0.0.27 → 0.0.28

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.
@@ -446,7 +446,12 @@ export declare type AgentEventMap = {
446
446
  readonly tool: readonly [call: ToolCall, result: ToolResult];
447
447
  /** Reports a turn's {@link TokenUsage} — emitted after a usage-bearing provider call. */
448
448
  readonly usage: readonly [usage: TokenUsage];
449
- /** Reports a call the authority denied — the call + the optional reason (not in the chunk stream). */
449
+ /**
450
+ * Reports a call denied by scope or authority, before dispatch.
451
+ * A turn advertising no tools drops each call with `no tool is advertised in the active scope`.
452
+ * Otherwise a call excluded by the tool allow-list carries `<tool name> is not in the active scope` and
453
+ * produces a denial tool result and message. Authority evaluates only admitted calls.
454
+ */
450
455
  readonly deny: readonly [call: ToolCall, reason: string | undefined];
451
456
  /** Reports the run settled successfully (a natural finish or a cancel's partial) — the {@link AgentResult}. */
452
457
  readonly finish: readonly [result: AgentResult];
@@ -761,7 +766,7 @@ export declare interface AgentJudgeInterface extends JudgeInterface {
761
766
  * - `workspaces` — an optional pre-built {@link WorkspaceManagerInterface} forwarded to the
762
767
  * agent's context; a fresh empty one is created when omitted (mirrors {@link AgentContextOptions.workspaces}).
763
768
  * - `scope` — an optional initial active {@link ScopeInterface} forwarded to the agent's context
764
- * (the build-time filter); `undefined` ⇒ no filtering (mirrors {@link AgentContextOptions.scope}).
769
+ * (the context and tool-dispatch filter); `undefined` ⇒ every registered tool is admitted.
765
770
  * - `on` — the reserved {@link EmitterHooks} key: initial listeners for the agent's
766
771
  * {@link AgentEventMap}, wired at construction (for example `{ finish: (r) => log(r) }`).
767
772
  */
@@ -776,7 +781,16 @@ export declare interface AgentOptions {
776
781
  readonly instructions?: InstructionManagerInterface;
777
782
  /** Reuses a pre-built workspace registry forwarded to the agent's context; a fresh empty one is created when omitted. */
778
783
  readonly workspaces?: WorkspaceManagerInterface;
779
- /** Sets the initial active scope forwarded to the agent's context (the build-time filter); `undefined` ⇒ no filtering. */
784
+ /**
785
+ * Sets the initial active scope; `undefined` admits every registered tool.
786
+ * Each turn snapshots the scope's tool allow-list before advertising and checks calls
787
+ * against that list before applying authority. An absent list also admits unknown names
788
+ * to authority and the registry, which returns its tool-not-found failure.
789
+ * A reply to a turn advertising no tools ends the run as its answer, records an assistant
790
+ * message without calls, and emits a `deny` event for every dropped call without marking it partial.
791
+ * Other scoped-out calls produce denial tool results and messages, and the loop continues.
792
+ * Changing the context scope through the `apply` method takes effect on the next turn.
793
+ */
780
794
  readonly scope?: ScopeInterface;
781
795
  /** Caps the tool-iteration turns before the loop stops; defaults to `DEFAULT_AGENT_LIMIT`. */
782
796
  readonly limit?: number;
@@ -789,11 +803,11 @@ export declare interface AgentOptions {
789
803
  /** Carries an external cancel; its abort commits a partial result. */
790
804
  readonly signal?: AbortSignal;
791
805
  /**
792
- * Holds an optional policy gate consulted before each tool call runs — a denied call is
806
+ * Holds an optional policy gate consulted after scope admits a tool call — a denied call is
793
807
  * fed back to the model as a denial {@link ToolResult} (a `tool` chunk + a tool
794
808
  * message) rather than executed (no tool run, no budget cost), so the model sees the
795
- * denial and can react; an allowed call dispatches normally. Omitted ⇒ every call
796
- * dispatches as before.
809
+ * denial and can react; an allowed call dispatches normally. Omitted ⇒ every admitted call
810
+ * dispatches through the registry.
797
811
  */
798
812
  readonly authority?: AuthorityInterface;
799
813
  /**
@@ -1138,6 +1152,11 @@ export declare interface AgentResult {
1138
1152
  readonly thinking?: string;
1139
1153
  /** Holds the summed {@link TokenUsage} across the turn's provider calls (present when any reported it). */
1140
1154
  readonly usage?: TokenUsage;
1155
+ /**
1156
+ * Reports cancellation or a turn limit reached with unresolved tool intent.
1157
+ * A reply to a turn advertising no tools is a complete answer (`false`), even if it
1158
+ * contains calls: those calls are dropped and observed through the `deny` event.
1159
+ */
1141
1160
  readonly partial: boolean;
1142
1161
  }
1143
1162
 
@@ -5193,7 +5212,11 @@ export declare class Scope implements ScopeInterface {
5193
5212
  export declare interface ScopeFilter {
5194
5213
  /** Lists the allowed instruction `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). */
5195
5214
  readonly instructions?: readonly string[];
5196
- /** Lists the allowed tool `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). */
5215
+ /**
5216
+ * Lists the allowed tool `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed).
5217
+ * The loop advertises and dispatches only admitted tools, checking scope before authority.
5218
+ * If no definition is advertised, the reply ends the run without dispatching its calls.
5219
+ */
5197
5220
  readonly tools?: readonly string[];
5198
5221
  /**
5199
5222
  * Lists the allowed active-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
@@ -446,7 +446,12 @@ export declare type AgentEventMap = {
446
446
  readonly tool: readonly [call: ToolCall, result: ToolResult];
447
447
  /** Reports a turn's {@link TokenUsage} — emitted after a usage-bearing provider call. */
448
448
  readonly usage: readonly [usage: TokenUsage];
449
- /** Reports a call the authority denied — the call + the optional reason (not in the chunk stream). */
449
+ /**
450
+ * Reports a call denied by scope or authority, before dispatch.
451
+ * A turn advertising no tools drops each call with `no tool is advertised in the active scope`.
452
+ * Otherwise a call excluded by the tool allow-list carries `<tool name> is not in the active scope` and
453
+ * produces a denial tool result and message. Authority evaluates only admitted calls.
454
+ */
450
455
  readonly deny: readonly [call: ToolCall, reason: string | undefined];
451
456
  /** Reports the run settled successfully (a natural finish or a cancel's partial) — the {@link AgentResult}. */
452
457
  readonly finish: readonly [result: AgentResult];
@@ -761,7 +766,7 @@ export declare interface AgentJudgeInterface extends JudgeInterface {
761
766
  * - `workspaces` — an optional pre-built {@link WorkspaceManagerInterface} forwarded to the
762
767
  * agent's context; a fresh empty one is created when omitted (mirrors {@link AgentContextOptions.workspaces}).
763
768
  * - `scope` — an optional initial active {@link ScopeInterface} forwarded to the agent's context
764
- * (the build-time filter); `undefined` ⇒ no filtering (mirrors {@link AgentContextOptions.scope}).
769
+ * (the context and tool-dispatch filter); `undefined` ⇒ every registered tool is admitted.
765
770
  * - `on` — the reserved {@link EmitterHooks} key: initial listeners for the agent's
766
771
  * {@link AgentEventMap}, wired at construction (for example `{ finish: (r) => log(r) }`).
767
772
  */
@@ -776,7 +781,16 @@ export declare interface AgentOptions {
776
781
  readonly instructions?: InstructionManagerInterface;
777
782
  /** Reuses a pre-built workspace registry forwarded to the agent's context; a fresh empty one is created when omitted. */
778
783
  readonly workspaces?: WorkspaceManagerInterface;
779
- /** Sets the initial active scope forwarded to the agent's context (the build-time filter); `undefined` ⇒ no filtering. */
784
+ /**
785
+ * Sets the initial active scope; `undefined` admits every registered tool.
786
+ * Each turn snapshots the scope's tool allow-list before advertising and checks calls
787
+ * against that list before applying authority. An absent list also admits unknown names
788
+ * to authority and the registry, which returns its tool-not-found failure.
789
+ * A reply to a turn advertising no tools ends the run as its answer, records an assistant
790
+ * message without calls, and emits a `deny` event for every dropped call without marking it partial.
791
+ * Other scoped-out calls produce denial tool results and messages, and the loop continues.
792
+ * Changing the context scope through the `apply` method takes effect on the next turn.
793
+ */
780
794
  readonly scope?: ScopeInterface;
781
795
  /** Caps the tool-iteration turns before the loop stops; defaults to `DEFAULT_AGENT_LIMIT`. */
782
796
  readonly limit?: number;
@@ -789,11 +803,11 @@ export declare interface AgentOptions {
789
803
  /** Carries an external cancel; its abort commits a partial result. */
790
804
  readonly signal?: AbortSignal;
791
805
  /**
792
- * Holds an optional policy gate consulted before each tool call runs — a denied call is
806
+ * Holds an optional policy gate consulted after scope admits a tool call — a denied call is
793
807
  * fed back to the model as a denial {@link ToolResult} (a `tool` chunk + a tool
794
808
  * message) rather than executed (no tool run, no budget cost), so the model sees the
795
- * denial and can react; an allowed call dispatches normally. Omitted ⇒ every call
796
- * dispatches as before.
809
+ * denial and can react; an allowed call dispatches normally. Omitted ⇒ every admitted call
810
+ * dispatches through the registry.
797
811
  */
798
812
  readonly authority?: AuthorityInterface;
799
813
  /**
@@ -1138,6 +1152,11 @@ export declare interface AgentResult {
1138
1152
  readonly thinking?: string;
1139
1153
  /** Holds the summed {@link TokenUsage} across the turn's provider calls (present when any reported it). */
1140
1154
  readonly usage?: TokenUsage;
1155
+ /**
1156
+ * Reports cancellation or a turn limit reached with unresolved tool intent.
1157
+ * A reply to a turn advertising no tools is a complete answer (`false`), even if it
1158
+ * contains calls: those calls are dropped and observed through the `deny` event.
1159
+ */
1141
1160
  readonly partial: boolean;
1142
1161
  }
1143
1162
 
@@ -5193,7 +5212,11 @@ export declare class Scope implements ScopeInterface {
5193
5212
  export declare interface ScopeFilter {
5194
5213
  /** Lists the allowed instruction `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). */
5195
5214
  readonly instructions?: readonly string[];
5196
- /** Lists the allowed tool `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed). */
5215
+ /**
5216
+ * Lists the allowed tool `name`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed).
5217
+ * The loop advertises and dispatches only admitted tools, checking scope before authority.
5218
+ * If no definition is advertised, the reply ends the run without dispatching its calls.
5219
+ */
5197
5220
  readonly tools?: readonly string[];
5198
5221
  /**
5199
5222
  * Lists the allowed active-workspace file `path`s (`undefined` ⇒ all, `[]` ⇒ none, else only-listed) —
@@ -2447,7 +2447,8 @@ var Agent = class {
2447
2447
  broke = true;
2448
2448
  break;
2449
2449
  }
2450
- const advertised = filterAllowList(this.#context.scope?.tools, tools.definitions(), (definition) => definition.name);
2450
+ const names = this.#context.scope?.tools?.slice();
2451
+ const advertised = filterAllowList(names, tools.definitions(), (definition) => definition.name);
2451
2452
  const definitions = advertised.length > 0 ? advertised : void 0;
2452
2453
  let charged = 0;
2453
2454
  let turnContent = "";
@@ -2501,8 +2502,10 @@ var Agent = class {
2501
2502
  usage: resultUsage
2502
2503
  };
2503
2504
  }
2504
- if (result.tools !== void 0 && result.tools.length > 0) {
2505
- const authorization = this.#authorize(result.tools);
2505
+ if (definitions === void 0) for (const call of result.tools ?? []) this.#emitter.emit("deny", call, "no tool is advertised in the active scope");
2506
+ else if (result.tools !== void 0 && result.tools.length > 0) {
2507
+ const admitted = filterAllowList(names, result.tools, (call) => call.name);
2508
+ const authorization = this.#authorize(result.tools, admitted);
2506
2509
  if (abort.signal.aborted) {
2507
2510
  partial = true;
2508
2511
  broke = true;
@@ -2574,24 +2577,33 @@ var Agent = class {
2574
2577
  messages.splice(0, messages.length, ...this.#context.build(this.#provider.format));
2575
2578
  return false;
2576
2579
  }
2577
- #authorize(calls) {
2578
- if (this.#authority === void 0) return void 0;
2580
+ #authorize(calls, admitted) {
2579
2581
  const authority = this.#authority;
2580
- const allowed = [];
2582
+ const allowed = /* @__PURE__ */ new Map();
2581
2583
  const denials = /* @__PURE__ */ new Map();
2582
- for (const call of calls) {
2584
+ for (const [index, call] of calls.entries()) {
2585
+ if (!admitted.includes(call)) {
2586
+ const reason = `${call.name} is not in the active scope`;
2587
+ denials.set(index, denyCall(call, reason));
2588
+ this.#emitter.emit("deny", call, reason);
2589
+ continue;
2590
+ }
2591
+ if (authority === void 0) {
2592
+ allowed.set(index, call);
2593
+ continue;
2594
+ }
2583
2595
  let decision;
2584
2596
  try {
2585
2597
  decision = authority.evaluate({ call });
2586
2598
  } catch (error) {
2587
2599
  const reason = errorToMessage(error);
2588
- denials.set(call.id, denyCall(call, reason));
2600
+ denials.set(index, denyCall(call, reason));
2589
2601
  this.#emitter.emit("deny", call, reason);
2590
2602
  continue;
2591
2603
  }
2592
- if (decision.allowed) allowed.push(call);
2604
+ if (decision.allowed) allowed.set(index, call);
2593
2605
  else {
2594
- denials.set(call.id, denyCall(call, decision.reason));
2606
+ denials.set(index, denyCall(call, decision.reason));
2595
2607
  this.#emitter.emit("deny", call, decision.reason);
2596
2608
  }
2597
2609
  }
@@ -2601,12 +2613,16 @@ var Agent = class {
2601
2613
  };
2602
2614
  }
2603
2615
  async #dispatch(tools, calls, signal, authorization) {
2604
- if (authorization === void 0) return tools.execute(calls, { signal });
2605
2616
  const { allowed, denials } = authorization;
2606
- const executed = allowed.length > 0 ? await tools.execute(allowed, { signal }) : [];
2607
- const byId = new Map(denials);
2608
- for (const result of executed) byId.set(result.id, result);
2609
- return calls.map((call) => byId.get(call.id) ?? denyCall(call, void 0));
2617
+ const executed = allowed.size > 0 ? await tools.execute([...allowed.values()], { signal }) : [];
2618
+ const results = new Map(denials);
2619
+ let offset = 0;
2620
+ for (const index of allowed.keys()) {
2621
+ const result = executed[offset];
2622
+ if (result !== void 0) results.set(index, result);
2623
+ offset += 1;
2624
+ }
2625
+ return calls.map((call, index) => results.get(index) ?? denyCall(call, void 0));
2610
2626
  }
2611
2627
  async *#provide(messages, signal, definitions, think, schema, onDelta) {
2612
2628
  const options = {};