Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ The umbrella `@polymind-inc/agent-framework` package and all `@polymind-inc/agen
packages are versioned in lockstep; one entry here covers the set. During 0.x, **minor releases may
contain breaking changes**; patch releases are fixes only.

## Unreleased

- **`@polymind-inc/agent-framework-core`** — an approval granted for a call id that an earlier
completed call had already used is no longer discarded. The approval layer correlated decisions
against a transcript-wide set of answered call ids, so a provider that reused a call id produced
a permanent approve → re-ask loop, or lost the decision outright, and a turn carrying no decision
deleted the stored request for the same reason. Decisions now bind per call occurrence, derived
from the order of the run's own messages: a result closes only the occurrence before it, and a
request after the latest result for its call opens a new one — the rule the function-calling loop
already applied underneath. Replayed copies of one still-open request coalesce with the first copy
canonical, so a doctored replay cannot displace the request a decision binds against. Nothing
positional is persisted; serialized sessions are unchanged in shape.

## 0.4.0

A hardening and consolidation release: ten breaking changes tighten types, credentials, telemetry
Expand Down
15 changes: 13 additions & 2 deletions packages/core/src/client/function-invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,19 @@ export function createFunctionInvocationClientFactory<TOptions extends ChatOptio
) {
if (isApprovalRequest(content)) {
requested.push(content);
lastRequestPosition.set(content.id, contentPosition);
lastRequestPositionByCallId.set(content.functionCall.callId, contentPosition);
// A copy replayed while the occurrence is still open does not restart it: only a
// result ends one. The first copy keeps the position, so a decision that arrived
// between the copies still answers it instead of being read as preceding a request
// it never saw. A copy after a result is a genuinely new occurrence and does move it.
const closingResult = lastResultPosition.get(content.functionCall.callId) ?? -1;
const openedAt = lastRequestPosition.get(content.id);
if (openedAt === undefined || openedAt < closingResult) {
lastRequestPosition.set(content.id, contentPosition);
}
const openedAtForCall = lastRequestPositionByCallId.get(content.functionCall.callId);
if (openedAtForCall === undefined || openedAtForCall < closingResult) {
lastRequestPositionByCallId.set(content.functionCall.callId, contentPosition);
}
} else {
responses.push(content);
lastResponsePosition.set(content.id, contentPosition);
Expand Down
Loading