agentfootprint 9.91.0 → 9.93.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/AGENTS.md +1 -1
- package/CHANGELOG.md +236 -0
- package/CLAUDE.md +1 -1
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/core/Agent.js +38 -7
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/LLMCall.js +6 -4
- package/dist/core/LLMCall.js.map +1 -1
- package/dist/core/agent/buildAgentMessageApiChart.js +10 -0
- package/dist/core/agent/buildAgentMessageApiChart.js.map +1 -1
- package/dist/core/agent/buildToolRegistry.js +90 -33
- package/dist/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/core/agent/messageApiReceipt.js +1 -0
- package/dist/core/agent/messageApiReceipt.js.map +1 -1
- package/dist/core/agent/stages/callLLM.js +18 -7
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +176 -28
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/stages/window.js +12 -2
- package/dist/core/agent/stages/window.js.map +1 -1
- package/dist/core/agent/window/evictedTurns.js +60 -0
- package/dist/core/agent/window/evictedTurns.js.map +1 -0
- package/dist/core/slots/buildToolsSlot.js +209 -62
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/Agent.d.ts +4 -0
- package/dist/esm/core/Agent.js +39 -8
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/LLMCall.js +6 -4
- package/dist/esm/core/LLMCall.js.map +1 -1
- package/dist/esm/core/agent/buildAgentMessageApiChart.js +10 -0
- package/dist/esm/core/agent/buildAgentMessageApiChart.js.map +1 -1
- package/dist/esm/core/agent/buildToolRegistry.d.ts +80 -31
- package/dist/esm/core/agent/buildToolRegistry.js +90 -33
- package/dist/esm/core/agent/buildToolRegistry.js.map +1 -1
- package/dist/esm/core/agent/messageApiReceipt.d.ts +3 -1
- package/dist/esm/core/agent/messageApiReceipt.js +1 -0
- package/dist/esm/core/agent/messageApiReceipt.js.map +1 -1
- package/dist/esm/core/agent/stages/callLLM.d.ts +9 -0
- package/dist/esm/core/agent/stages/callLLM.js +18 -7
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.d.ts +43 -5
- package/dist/esm/core/agent/stages/toolCalls.js +174 -27
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/stages/window.d.ts +8 -0
- package/dist/esm/core/agent/stages/window.js +12 -2
- package/dist/esm/core/agent/stages/window.js.map +1 -1
- package/dist/esm/core/agent/types.d.ts +15 -0
- package/dist/esm/core/agent/window/evictedTurns.d.ts +52 -0
- package/dist/esm/core/agent/window/evictedTurns.js +56 -0
- package/dist/esm/core/agent/window/evictedTurns.js.map +1 -0
- package/dist/esm/core/slots/buildToolsSlot.d.ts +100 -0
- package/dist/esm/core/slots/buildToolsSlot.js +207 -61
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +92 -18
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/lib/time-travel/receipt.d.ts +71 -28
- package/dist/esm/lib/time-travel/receipt.js +33 -17
- package/dist/esm/lib/time-travel/receipt.js.map +1 -1
- package/dist/esm/lib/time-travel/servedView.d.ts +31 -22
- package/dist/esm/lib/time-travel/servedView.js +78 -43
- package/dist/esm/lib/time-travel/servedView.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/lib/time-travel/receipt.js +33 -17
- package/dist/lib/time-travel/receipt.js.map +1 -1
- package/dist/lib/time-travel/servedView.js +78 -43
- package/dist/lib/time-travel/servedView.js.map +1 -1
- package/dist/types/core/Agent.d.ts +4 -0
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/LLMCall.d.ts.map +1 -1
- package/dist/types/core/agent/buildAgentMessageApiChart.d.ts.map +1 -1
- package/dist/types/core/agent/buildToolRegistry.d.ts +80 -31
- package/dist/types/core/agent/buildToolRegistry.d.ts.map +1 -1
- package/dist/types/core/agent/messageApiReceipt.d.ts +3 -1
- package/dist/types/core/agent/messageApiReceipt.d.ts.map +1 -1
- package/dist/types/core/agent/stages/callLLM.d.ts +9 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts +43 -5
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/stages/window.d.ts +8 -0
- package/dist/types/core/agent/stages/window.d.ts.map +1 -1
- package/dist/types/core/agent/types.d.ts +15 -0
- package/dist/types/core/agent/types.d.ts.map +1 -1
- package/dist/types/core/agent/window/evictedTurns.d.ts +53 -0
- package/dist/types/core/agent/window/evictedTurns.d.ts.map +1 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts +100 -0
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +92 -18
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/lib/time-travel/receipt.d.ts +71 -28
- package/dist/types/lib/time-travel/receipt.d.ts.map +1 -1
- package/dist/types/lib/time-travel/servedView.d.ts +31 -22
- package/dist/types/lib/time-travel/servedView.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
* Treats that answer as the paused tool's result, appends to
|
|
12
12
|
* history, then continues the ReAct iteration loop.
|
|
13
13
|
*
|
|
14
|
-
* Dispatch resolution order:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Dispatch resolution order (9.92.0 — DISPATCH FOLLOWS THE OFFER):
|
|
15
|
+
* 0. A name on THIS epoch's wire resolves to the party whose contract the
|
|
16
|
+
* model read (`servedTools`, written by the tools slot from the same
|
|
17
|
+
* merge that built the wire): a provider's schema → the provider's tool
|
|
18
|
+
* from `providerToolCache`; anything else → `registryByName`.
|
|
19
|
+
* 1. A name NOT on the wire this epoch — held out by a step, a park or a
|
|
20
|
+
* scoping, or named from a restored transcript — resolves as it always
|
|
21
|
+
* did: `registryByName` first, then the provider cache.
|
|
18
22
|
*
|
|
19
23
|
* Permission gate (when `permissionChecker` is configured) runs BEFORE
|
|
20
24
|
* `tool.execute`. Deny → tool not executed; result is a synthetic
|
|
@@ -53,7 +57,8 @@ import type { ArtifactStore } from '../../../artifacts/types.js';
|
|
|
53
57
|
import { type ArtifactPlacement } from '../../../artifacts/placement.js';
|
|
54
58
|
import type { AuthorizationRequiredMode } from '../../../identity/consent.js';
|
|
55
59
|
import { type ResolvedCheckInConfig } from '../../checkin.js';
|
|
56
|
-
import type { ProviderToolCache } from '../../slots/buildToolsSlot.js';
|
|
60
|
+
import type { ProviderToolCache, ServedToolParties } from '../../slots/buildToolsSlot.js';
|
|
61
|
+
import type { ToolClaim } from '../buildToolRegistry.js';
|
|
57
62
|
import type { Tool } from '../../tools.js';
|
|
58
63
|
import type { MemoryIdentity } from '../../../memory/identity/types.js';
|
|
59
64
|
import type { ToolSessionTier } from '../../toolSessions.js';
|
|
@@ -143,6 +148,21 @@ export interface ToolCallsHandlerDeps {
|
|
|
143
148
|
* within one chart build.
|
|
144
149
|
*/
|
|
145
150
|
readonly providerToolCache?: ProviderToolCache;
|
|
151
|
+
/**
|
|
152
|
+
* Who put each name on this epoch's wire (9.92.0) — the tools slot's own
|
|
153
|
+
* record of its merge, shared by reference like `providerToolCache`. The
|
|
154
|
+
* first thing `lookupTool` consults, so the party whose contract the model
|
|
155
|
+
* read is the party that answers. Absent (a chart without the slot's
|
|
156
|
+
* record) → resolution order 1 alone, exactly as before 9.92.0.
|
|
157
|
+
*/
|
|
158
|
+
readonly servedTools?: ServedToolParties;
|
|
159
|
+
/**
|
|
160
|
+
* Every build-time claimant per tool name (9.92.0) — `buildToolRegistry`'s
|
|
161
|
+
* `toolClaimants`. Read for one thing: which party a registry-routed tool
|
|
162
|
+
* belongs to, so an off-wire dispatch can be REPORTED with its answering
|
|
163
|
+
* party (`tools.answered_off_wire`). Absent → `'registry'`.
|
|
164
|
+
*/
|
|
165
|
+
readonly toolClaimants?: ReadonlyMap<string, readonly ToolClaim[]>;
|
|
146
166
|
/** Optional permission gate. When present, every tool dispatch
|
|
147
167
|
* awaits `check({capability: 'tool_call', ...})` BEFORE executing.
|
|
148
168
|
* Throwing checkers are treated as deny-by-default. */
|
|
@@ -495,6 +515,24 @@ export declare function atMountedCursor(args: {
|
|
|
495
515
|
* Exported for the producer registry, and used by BOTH dispatch doors so they
|
|
496
516
|
* cannot drift.
|
|
497
517
|
*/
|
|
518
|
+
/**
|
|
519
|
+
* What a call gets back when the party whose contract the model read under
|
|
520
|
+
* this name cannot answer it, and no other party may (9.92.0).
|
|
521
|
+
*
|
|
522
|
+
* Two doors reach here. A name that LEFT the wire — the model read a
|
|
523
|
+
* provider's contract on an earlier call and the provider withdrew the name —
|
|
524
|
+
* whose only remaining holder is a party the model was never shown under that
|
|
525
|
+
* name (a scoped skill that never activated): dispatching that holder would
|
|
526
|
+
* hand the model's belief to a stranger's function, which is the silent
|
|
527
|
+
* identity swap this release closed. And a resume in a fresh Agent instance
|
|
528
|
+
* of a provider-served call, where the served party is on the checkpoint but
|
|
529
|
+
* the provider's list has not been resolved on this instance.
|
|
530
|
+
*
|
|
531
|
+
* It names no roster on purpose: `unknownToolResult`'s roster is the DISPATCH
|
|
532
|
+
* map, and the map here holds exactly the implementation this sentence is
|
|
533
|
+
* refusing. Anchored past tense, one clause, like every persistent result.
|
|
534
|
+
*/
|
|
535
|
+
export declare function notServedResult(toolName: string): string;
|
|
498
536
|
export declare function unknownToolResult(toolName: string, dispatchable: SpokenIds): string;
|
|
499
537
|
/**
|
|
500
538
|
* THE ONE COMPOSER of every `read_skill` refusal (9.86.0) — reachability,
|
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
* Treats that answer as the paused tool's result, appends to
|
|
12
12
|
* history, then continues the ReAct iteration loop.
|
|
13
13
|
*
|
|
14
|
-
* Dispatch resolution order:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* Dispatch resolution order (9.92.0 — DISPATCH FOLLOWS THE OFFER):
|
|
15
|
+
* 0. A name on THIS epoch's wire resolves to the party whose contract the
|
|
16
|
+
* model read (`servedTools`, written by the tools slot from the same
|
|
17
|
+
* merge that built the wire): a provider's schema → the provider's tool
|
|
18
|
+
* from `providerToolCache`; anything else → `registryByName`.
|
|
19
|
+
* 1. A name NOT on the wire this epoch — held out by a step, a park or a
|
|
20
|
+
* scoping, or named from a restored transcript — resolves as it always
|
|
21
|
+
* did: `registryByName` first, then the provider cache.
|
|
18
22
|
*
|
|
19
23
|
* Permission gate (when `permissionChecker` is configured) runs BEFORE
|
|
20
24
|
* `tool.execute`. Deny → tool not executed; result is a synthetic
|
|
@@ -216,6 +220,27 @@ export function atMountedCursor(args) {
|
|
|
216
220
|
* Exported for the producer registry, and used by BOTH dispatch doors so they
|
|
217
221
|
* cannot drift.
|
|
218
222
|
*/
|
|
223
|
+
/**
|
|
224
|
+
* What a call gets back when the party whose contract the model read under
|
|
225
|
+
* this name cannot answer it, and no other party may (9.92.0).
|
|
226
|
+
*
|
|
227
|
+
* Two doors reach here. A name that LEFT the wire — the model read a
|
|
228
|
+
* provider's contract on an earlier call and the provider withdrew the name —
|
|
229
|
+
* whose only remaining holder is a party the model was never shown under that
|
|
230
|
+
* name (a scoped skill that never activated): dispatching that holder would
|
|
231
|
+
* hand the model's belief to a stranger's function, which is the silent
|
|
232
|
+
* identity swap this release closed. And a resume in a fresh Agent instance
|
|
233
|
+
* of a provider-served call, where the served party is on the checkpoint but
|
|
234
|
+
* the provider's list has not been resolved on this instance.
|
|
235
|
+
*
|
|
236
|
+
* It names no roster on purpose: `unknownToolResult`'s roster is the DISPATCH
|
|
237
|
+
* map, and the map here holds exactly the implementation this sentence is
|
|
238
|
+
* refusing. Anchored past tense, one clause, like every persistent result.
|
|
239
|
+
*/
|
|
240
|
+
export function notServedResult(toolName) {
|
|
241
|
+
return (`Tool '${toolName}' did not run on that call: the implementation whose contract was ` +
|
|
242
|
+
`offered under that name was not available to answer it.`);
|
|
243
|
+
}
|
|
219
244
|
export function unknownToolResult(toolName, dispatchable) {
|
|
220
245
|
const head = `Unknown tool '${toolName}' on that call.`;
|
|
221
246
|
if (dispatchable.named.length > 0) {
|
|
@@ -422,7 +447,7 @@ function appendBatchResult(scope, entry) {
|
|
|
422
447
|
*/
|
|
423
448
|
const UNSCOPED_RUN = '#no-run-id';
|
|
424
449
|
export function buildToolCallsHandler(deps) {
|
|
425
|
-
const { registryByName, externalToolProvider, providerToolCache, permissionChecker } = deps;
|
|
450
|
+
const { registryByName, externalToolProvider, providerToolCache, servedTools, toolClaimants, permissionChecker, } = deps;
|
|
426
451
|
const toolArgValidation = deps.toolArgValidation ?? 'enforce';
|
|
427
452
|
// 8.6.0 — default `'pause'`. Consent is work waiting on a person, and every
|
|
428
453
|
// other place this library needs a person stops the run and asks the caller.
|
|
@@ -1686,15 +1711,91 @@ export function buildToolCallsHandler(deps) {
|
|
|
1686
1711
|
// one resolver. The Tools slot already invoked `provider.list(ctx)` this
|
|
1687
1712
|
// iteration and cached the resolved Tool[] in `providerToolCache` — read from
|
|
1688
1713
|
// there to avoid a second discovery call (vital for async network providers).
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1714
|
+
//
|
|
1715
|
+
// DISPATCH FOLLOWS THE OFFER (9.92.0). A name on this epoch's wire resolves
|
|
1716
|
+
// to the implementation of the party whose contract the model read — the
|
|
1717
|
+
// slot's own record of its merge (`servedTools`), never a re-derivation.
|
|
1718
|
+
// Before this, `registryByName` was consulted first for every name, so a
|
|
1719
|
+
// provider's contract on the wire could be answered by a skill's `execute`
|
|
1720
|
+
// (an INACTIVE skill's, even) and a provider's `skip_step` by the framework's
|
|
1721
|
+
// — recorded-not-built entries 1 and 3.
|
|
1722
|
+
//
|
|
1723
|
+
// A name NOT on the wire keeps the capability law's held-out clause — a
|
|
1724
|
+
// later step's tool, a parked map's tool, a scoped tool named from a
|
|
1725
|
+
// restored transcript still run — under ONE condition: the party that
|
|
1726
|
+
// answers is the party the model LAST READ the name under (`lastServed`), or
|
|
1727
|
+
// the name's only holder when it was never served this run. A name whose
|
|
1728
|
+
// last-served party can no longer answer (a provider withdrew it) is REFUSED
|
|
1729
|
+
// as a recorded tool result rather than handed to a party the model was
|
|
1730
|
+
// never shown under that name; a never-served name held by two parties is
|
|
1731
|
+
// refused the same way. Every off-wire dispatch that DOES happen is put on
|
|
1732
|
+
// the record by `tools.answered_off_wire`.
|
|
1733
|
+
const fromProviderCache = (toolName) => externalToolProvider
|
|
1734
|
+
? (providerToolCache?.current ?? []).find((t) => t.schema.name === toolName)
|
|
1735
|
+
: undefined;
|
|
1736
|
+
const providerParty = {
|
|
1737
|
+
channel: 'provider',
|
|
1738
|
+
...(externalToolProvider?.id !== undefined && { id: externalToolProvider.id }),
|
|
1739
|
+
};
|
|
1740
|
+
/** The party a registry-routed name belongs to — its first build-time claimant. */
|
|
1741
|
+
const registryPartyOf = (toolName) => {
|
|
1742
|
+
const first = toolClaimants?.get(toolName)?.[0];
|
|
1743
|
+
return first === undefined
|
|
1744
|
+
? { channel: 'registry' }
|
|
1745
|
+
: { channel: first.channel, ...(first.id !== undefined && { id: first.id }) };
|
|
1746
|
+
};
|
|
1747
|
+
const resolveTool = (toolName, pinned) => {
|
|
1748
|
+
const wireParty = pinned ?? servedTools?.current.get(toolName);
|
|
1749
|
+
if (wireParty !== undefined) {
|
|
1750
|
+
const owner = wireParty.channel === 'provider'
|
|
1751
|
+
? fromProviderCache(toolName)
|
|
1752
|
+
: registryByName.get(toolName);
|
|
1753
|
+
return owner !== undefined
|
|
1754
|
+
? { tool: owner, party: wireParty }
|
|
1755
|
+
: { refusal: notServedResult(toolName) };
|
|
1756
|
+
}
|
|
1757
|
+
const registryHolder = registryByName.get(toolName);
|
|
1758
|
+
const providerHolder = fromProviderCache(toolName);
|
|
1759
|
+
if (registryHolder === undefined && providerHolder === undefined)
|
|
1760
|
+
return {};
|
|
1761
|
+
const last = servedTools?.lastServed.get(toolName);
|
|
1762
|
+
if (last !== undefined) {
|
|
1763
|
+
const owner = last.channel === 'provider' ? providerHolder : registryHolder;
|
|
1764
|
+
return owner !== undefined
|
|
1765
|
+
? { tool: owner, party: last, offWire: true }
|
|
1766
|
+
: { refusal: notServedResult(toolName) };
|
|
1767
|
+
}
|
|
1768
|
+
if (registryHolder !== undefined && providerHolder !== undefined) {
|
|
1769
|
+
return { refusal: notServedResult(toolName) };
|
|
1770
|
+
}
|
|
1771
|
+
return registryHolder !== undefined
|
|
1772
|
+
? { tool: registryHolder, party: registryPartyOf(toolName), offWire: true }
|
|
1773
|
+
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1774
|
+
{ tool: providerHolder, party: providerParty, offWire: true };
|
|
1697
1775
|
};
|
|
1776
|
+
const lookupTool = (toolName, pinned) => resolveTool(toolName, pinned).tool;
|
|
1777
|
+
/** The record of an off-wire dispatch — once per such call, before it runs. */
|
|
1778
|
+
const noteOffWire = (scope, resolved, call) => {
|
|
1779
|
+
if (resolved.offWire !== true || resolved.party === undefined)
|
|
1780
|
+
return;
|
|
1781
|
+
typedEmit(scope, 'agentfootprint.tools.answered_off_wire', {
|
|
1782
|
+
toolName: call.toolName,
|
|
1783
|
+
toolCallId: call.toolCallId,
|
|
1784
|
+
iteration: call.iteration,
|
|
1785
|
+
answeredBy: resolved.party.channel,
|
|
1786
|
+
...(resolved.party.id !== undefined && { answeredById: resolved.party.id }),
|
|
1787
|
+
});
|
|
1788
|
+
};
|
|
1789
|
+
// The framework's own `skip_step` — `buildToolRegistry` installs it in the
|
|
1790
|
+
// dispatch map whenever a skill declares steps. The step bookkeeping below
|
|
1791
|
+
// keys on THIS instance having answered, not on the name: since 9.92.0 a
|
|
1792
|
+
// provider's `skip_step` on the wire is the provider's to answer, and a
|
|
1793
|
+
// procedure must not advance on a call whose contract the model read from
|
|
1794
|
+
// somebody else (recorded-not-built, entry 3).
|
|
1795
|
+
const frameworkSkipStep = deps.stepPlanFor ? registryByName.get(SKIP_STEP_TOOL_NAME) : undefined;
|
|
1796
|
+
const frameworkSkipStepAnswered = (name, answered) => frameworkSkipStep !== undefined &&
|
|
1797
|
+
name === SKIP_STEP_TOOL_NAME &&
|
|
1798
|
+
answered === frameworkSkipStep;
|
|
1698
1799
|
/**
|
|
1699
1800
|
* Every member id of every map the kernel currently holds PARKED (9.86.0
|
|
1700
1801
|
* fix pass) — asked of `parkedMemberIds`, the kernel's own owner of the fact.
|
|
@@ -1809,9 +1910,17 @@ export function buildToolCallsHandler(deps) {
|
|
|
1809
1910
|
// runs now. Fail-closed: a blocked/failed credential surfaces to the model
|
|
1810
1911
|
// and the tool does NOT run. A tool that pauses again during an approved
|
|
1811
1912
|
// resume can't re-pause (resume returns void), so that is surfaced as an error.
|
|
1812
|
-
const resolveCredentialAndExecute = async (scope,
|
|
1813
|
-
|
|
1814
|
-
|
|
1913
|
+
const resolveCredentialAndExecute = async (scope, resolved, toolName, args, toolCallId, iteration, env) => {
|
|
1914
|
+
const tool = resolved.tool;
|
|
1915
|
+
// The two refusals share one door here too (9.92.0): a name nothing holds,
|
|
1916
|
+
// and a name whose offered party cannot answer on this resume.
|
|
1917
|
+
if (!tool) {
|
|
1918
|
+
return {
|
|
1919
|
+
result: resolved.refusal ?? unknownToolResult(toolName, dispatchRoster(scope)),
|
|
1920
|
+
error: true,
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
noteOffWire(scope, resolved, { toolName, toolCallId, iteration });
|
|
1815
1924
|
// Declared artifact arguments (9.22.0) — the same resolution the batch
|
|
1816
1925
|
// loop applies, at this door: a resumed call's refs are judged exactly
|
|
1817
1926
|
// as an inline call's, BEFORE credentials, and a refusal means the tool
|
|
@@ -2118,7 +2227,8 @@ export function buildToolCallsHandler(deps) {
|
|
|
2118
2227
|
const runIdentity = scope.runIdentity;
|
|
2119
2228
|
const env = scope.$getEnv();
|
|
2120
2229
|
for (const tc of toolCalls) {
|
|
2121
|
-
const
|
|
2230
|
+
const resolved = resolveTool(tc.name);
|
|
2231
|
+
const tool = resolved.tool;
|
|
2122
2232
|
typedEmit(scope, 'agentfootprint.stream.tool_start', {
|
|
2123
2233
|
toolName: tc.name,
|
|
2124
2234
|
toolCallId: tc.id,
|
|
@@ -2389,6 +2499,11 @@ export function buildToolCallsHandler(deps) {
|
|
|
2389
2499
|
scope.pausedAskArgs = chain.args;
|
|
2390
2500
|
scope.pausedAskIndex = chain.index;
|
|
2391
2501
|
scope.pausedAskMiddleware = chain.middleware;
|
|
2502
|
+
// The party this call was resolved against (9.92.0) — so a resume
|
|
2503
|
+
// in a FRESH instance dispatches to the same party, never to the
|
|
2504
|
+
// build-time map's first holder. Pause-path-only.
|
|
2505
|
+
if (resolved.party !== undefined)
|
|
2506
|
+
scope.pausedToolParty = resolved.party;
|
|
2392
2507
|
// Which surface will collect the decision — carried so the
|
|
2393
2508
|
// resume-side ledger rows can say which component ANSWERED.
|
|
2394
2509
|
// Written only when one exists: a component-less ask's
|
|
@@ -2544,6 +2659,9 @@ export function buildToolCallsHandler(deps) {
|
|
|
2544
2659
|
scope.pausedToolStartMs = startMs;
|
|
2545
2660
|
scope.pausedCheckIn = true;
|
|
2546
2661
|
scope.pausedCheckInArgs = callArgs;
|
|
2662
|
+
// Same carrier as the middleware-ask pause (9.92.0).
|
|
2663
|
+
if (resolved.party !== undefined)
|
|
2664
|
+
scope.pausedToolParty = resolved.party;
|
|
2547
2665
|
// Which surface will collect the decision — read back on resume
|
|
2548
2666
|
// so `checkin.decision` says which component ANSWERED. Written
|
|
2549
2667
|
// only when one exists (byte-identical otherwise).
|
|
@@ -2637,6 +2755,9 @@ export function buildToolCallsHandler(deps) {
|
|
|
2637
2755
|
scope.pausedCredential = true;
|
|
2638
2756
|
scope.pausedCredentialArgs = callArgs;
|
|
2639
2757
|
scope.pausedCredentialService = need.credential;
|
|
2758
|
+
// Same carrier as the middleware-ask pause (9.92.0).
|
|
2759
|
+
if (resolved.party !== undefined)
|
|
2760
|
+
scope.pausedToolParty = resolved.party;
|
|
2640
2761
|
// A defined return value triggers the footprintjs pause; this
|
|
2641
2762
|
// object becomes the checkpoint's pauseData, and standingAgent's
|
|
2642
2763
|
// describePause hands it to the caller as `PendingAsk`.
|
|
@@ -2683,8 +2804,13 @@ export function buildToolCallsHandler(deps) {
|
|
|
2683
2804
|
}
|
|
2684
2805
|
if (!credentialBlocked && !wantsBlocked) {
|
|
2685
2806
|
try {
|
|
2686
|
-
|
|
2687
|
-
|
|
2807
|
+
// The two refusals share one door: a name nothing holds, and a
|
|
2808
|
+
// name whose offered party cannot answer (9.92.0). Both land as
|
|
2809
|
+
// an errored tool result the model reads, on the same path.
|
|
2810
|
+
if (!tool) {
|
|
2811
|
+
throw new Error(resolved.refusal ?? unknownToolResult(tc.name, dispatchRoster(scope)));
|
|
2812
|
+
}
|
|
2813
|
+
noteOffWire(scope, resolved, { toolName: tc.name, toolCallId: tc.id, iteration });
|
|
2688
2814
|
// Set BEFORE the await: a tool that throws has still run, and a
|
|
2689
2815
|
// tool that does not exist has not. This flag is the entire
|
|
2690
2816
|
// precondition of the after-tool moment below.
|
|
@@ -3077,7 +3203,7 @@ export function buildToolCallsHandler(deps) {
|
|
|
3077
3203
|
// Postures never gate it — skipping is judgment inside a step, not
|
|
3078
3204
|
// routing. Zero-cost gate: `stepPlanFor` is undefined on every agent
|
|
3079
3205
|
// without a stepped skill.
|
|
3080
|
-
if (
|
|
3206
|
+
if (frameworkSkipStepAnswered(tc.name, tool) && !error && !denied) {
|
|
3081
3207
|
result = applySkipStep(scope, { args: callArgs, toolCallId: tc.id, iteration });
|
|
3082
3208
|
}
|
|
3083
3209
|
// ── present bookkeeping (9.22.0) ───────────────────────────────
|
|
@@ -3453,7 +3579,12 @@ export function buildToolCallsHandler(deps) {
|
|
|
3453
3579
|
// continue here must see the same `toolSource` the links before the
|
|
3454
3580
|
// ask saw. A chain that changed its mind about where a tool came from
|
|
3455
3581
|
// halfway through one dispatch would be worse than not knowing.
|
|
3456
|
-
|
|
3582
|
+
//
|
|
3583
|
+
// Resolved against the party the CHECKPOINT carries (9.92.0): a
|
|
3584
|
+
// fresh instance has an empty served record and Compose has not
|
|
3585
|
+
// re-run, so without it the build-time map's first holder answered.
|
|
3586
|
+
const resolved = resolveTool(toolName, scope.pausedToolParty);
|
|
3587
|
+
const tool = resolved.tool;
|
|
3457
3588
|
const rest = await runToolChain(deps.toolMiddleware ?? [], {
|
|
3458
3589
|
toolName,
|
|
3459
3590
|
...(tool?.source !== undefined && { toolSource: tool.source }),
|
|
@@ -3516,7 +3647,7 @@ export function buildToolCallsHandler(deps) {
|
|
|
3516
3647
|
}
|
|
3517
3648
|
else {
|
|
3518
3649
|
const env = scope.$getEnv();
|
|
3519
|
-
const dispatched = await resolveCredentialAndExecute(scope,
|
|
3650
|
+
const dispatched = await resolveCredentialAndExecute(scope, resolved, toolName, rest.args, toolCallId, iteration, env);
|
|
3520
3651
|
result = dispatched.result;
|
|
3521
3652
|
error = dispatched.error;
|
|
3522
3653
|
// A ceiling-refused call RAN but must not advance a step (9.20.0);
|
|
@@ -3531,7 +3662,7 @@ export function buildToolCallsHandler(deps) {
|
|
|
3531
3662
|
// placeholder just landed — replace it with the authoritative
|
|
3532
3663
|
// sentence BEFORE the chain's last word, the execute loop's
|
|
3533
3664
|
// composition kept.
|
|
3534
|
-
if (
|
|
3665
|
+
if (frameworkSkipStepAnswered(toolName, tool) && stepToolRan) {
|
|
3535
3666
|
result = applySkipStep(scope, { args: rest.args, toolCallId, iteration });
|
|
3536
3667
|
}
|
|
3537
3668
|
// present behind a middleware ask, approved (9.22.0): same
|
|
@@ -3635,6 +3766,11 @@ export function buildToolCallsHandler(deps) {
|
|
|
3635
3766
|
// carried one must not gain the key (byte-identical).
|
|
3636
3767
|
if (answeredVia !== undefined)
|
|
3637
3768
|
scope.pausedComponentId = undefined;
|
|
3769
|
+
// The served party the checkpoint carried is consumed with it (9.92.0);
|
|
3770
|
+
// written back only when it was there, so an older checkpoint's resume
|
|
3771
|
+
// commits exactly what it always did.
|
|
3772
|
+
if (scope.pausedToolParty !== undefined)
|
|
3773
|
+
scope.pausedToolParty = undefined;
|
|
3638
3774
|
return;
|
|
3639
3775
|
}
|
|
3640
3776
|
// ── Check-in decision path ───────────────────────────────────────
|
|
@@ -3677,8 +3813,10 @@ export function buildToolCallsHandler(deps) {
|
|
|
3677
3813
|
let resumeEnvelope;
|
|
3678
3814
|
if (decision.approved) {
|
|
3679
3815
|
const env = scope.$getEnv();
|
|
3680
|
-
|
|
3681
|
-
const
|
|
3816
|
+
// Resolved against the party the checkpoint carries (9.92.0).
|
|
3817
|
+
const resolved = resolveTool(toolName, scope.pausedToolParty);
|
|
3818
|
+
const tool = resolved.tool;
|
|
3819
|
+
const dispatched = await resolveCredentialAndExecute(scope, resolved, toolName, args, toolCallId, iteration, env);
|
|
3682
3820
|
result = dispatched.result;
|
|
3683
3821
|
error = dispatched.error;
|
|
3684
3822
|
// Same ceiling law as the ask path: ran ≠ completed a step (9.20.0).
|
|
@@ -3766,6 +3904,11 @@ export function buildToolCallsHandler(deps) {
|
|
|
3766
3904
|
// Cleared ONLY when it was set — see the ask path's twin note.
|
|
3767
3905
|
if (answeredVia !== undefined)
|
|
3768
3906
|
scope.pausedComponentId = undefined;
|
|
3907
|
+
// The served party the checkpoint carried is consumed with it (9.92.0);
|
|
3908
|
+
// written back only when it was there, so an older checkpoint's resume
|
|
3909
|
+
// commits exactly what it always did.
|
|
3910
|
+
if (scope.pausedToolParty !== undefined)
|
|
3911
|
+
scope.pausedToolParty = undefined;
|
|
3769
3912
|
return;
|
|
3770
3913
|
}
|
|
3771
3914
|
// ── Credential-consent decision path (8.6.0) ─────────────────────
|
|
@@ -3785,8 +3928,10 @@ export function buildToolCallsHandler(deps) {
|
|
|
3785
3928
|
const iteration = scope.iteration;
|
|
3786
3929
|
const args = (scope.pausedCredentialArgs ?? {});
|
|
3787
3930
|
const env = scope.$getEnv();
|
|
3788
|
-
|
|
3789
|
-
const
|
|
3931
|
+
// Resolved against the party the checkpoint carries (9.92.0).
|
|
3932
|
+
const resolved = resolveTool(toolName, scope.pausedToolParty);
|
|
3933
|
+
const tool = resolved.tool;
|
|
3934
|
+
const dispatched = await resolveCredentialAndExecute(scope, resolved, toolName, args, toolCallId, iteration, env);
|
|
3790
3935
|
const result = dispatched.result;
|
|
3791
3936
|
const error = dispatched.error;
|
|
3792
3937
|
let modelResult;
|
|
@@ -3865,6 +4010,8 @@ export function buildToolCallsHandler(deps) {
|
|
|
3865
4010
|
scope.pausedCredential = false;
|
|
3866
4011
|
scope.pausedCredentialArgs = undefined;
|
|
3867
4012
|
scope.pausedCredentialService = undefined;
|
|
4013
|
+
if (scope.pausedToolParty !== undefined)
|
|
4014
|
+
scope.pausedToolParty = undefined;
|
|
3868
4015
|
return;
|
|
3869
4016
|
}
|
|
3870
4017
|
// ── The `pauseHere` / `askHuman` path ────────────────────────────
|