From a200e0051c078e6d573f0da716a4fabee8067ece Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 15 Aug 2026 15:59:47 +0900 Subject: [PATCH] =?UTF-8?q?fix(openai):=20hybrid=20=EB=8F=84=EA=B5=AC=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=9D=84=20pending=20receipt=EB=A1=9C=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/edge/internal/openai/hot_path_light.go | 7 +++++-- .../internal/openai/hot_path_light_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/edge/internal/openai/hot_path_light.go b/apps/edge/internal/openai/hot_path_light.go index 77fe5aef..7c63117e 100644 --- a/apps/edge/internal/openai/hot_path_light.go +++ b/apps/edge/internal/openai/hot_path_light.go @@ -670,7 +670,10 @@ func (s *hotPathLightStore) consume(ownerEdgeID, principalRef, protocol string, byPublic[result.callID] = result } - snap, err := coordinator.consumeContinuationByLineage(ownerEdgeID, principalRef, lineage) + // Command-mode SDKs may reserialize earlier message history between tool + // turns. The pending receipt already binds this continuation to the exact + // request, owner, principal, endpoint, toolset, issued calls, and result IDs. + snap, err := coordinator.consumeArtifactContinuationByLineage(record.requestID, ownerEdgeID, principalRef, lineage) if err != nil { return logicalRequestSnapshot{}, hotPathLightDisposition{}, true, err } @@ -838,7 +841,7 @@ func (s *hotPathLightStore) matchRecordLocked(ownerEdgeID, principalRef, protoco if record.protocol != protocol { return nil, true, fmt.Errorf("%w: protocol changed", errLogicalRequestLineage) } - if record.lineage != lineage.Prefix { + if record.lineage.Endpoint != lineage.Prefix.Endpoint || record.lineage.ToolsetDigest != lineage.Prefix.ToolsetDigest { return nil, true, describeArtifactPrefixMismatch(record.lineage, lineage.Prefix) } return record, true, nil diff --git a/apps/edge/internal/openai/hot_path_light_test.go b/apps/edge/internal/openai/hot_path_light_test.go index 7a575af4..c39d7625 100644 --- a/apps/edge/internal/openai/hot_path_light_test.go +++ b/apps/edge/internal/openai/hot_path_light_test.go @@ -78,6 +78,25 @@ func TestHotPathStageOrdinaryWorkspacePathPassesThrough(t *testing.T) { } } +func TestHotPathLightMatchesPendingReceiptAfterSDKHistoryRewrite(t *testing.T) { + store := &hotPathLightStore{records: map[string]*hotPathLightRecord{ + "req_history_rewrite": { + requestID: "req_history_rewrite", ownerEdgeID: "edge", principalRef: "principal", protocol: "anthropic", + lineage: logicalRequestLineage{Endpoint: "anthropic", HistoryDigest: "before", ToolsetDigest: "tools"}, + pending: map[string]hotPathPendingCall{"call_pending": {publicCallID: "call_pending"}}, pendingHash: "issued", + consumedHashes: map[string]struct{}{}, consumedIDs: map[string]struct{}{}, + }, + }} + lineage := logicalRequestContinuationLineage{ + Prefix: logicalRequestLineage{Endpoint: "anthropic", HistoryDigest: "rewritten", ToolsetDigest: "tools"}, + IssuedCallHash: "issued", ResultIDs: []string{"call_pending"}, + } + record, matched, err := store.matchRecordLocked("edge", "principal", "anthropic", lineage) + if err != nil || !matched || record == nil || record.requestID != "req_history_rewrite" { + t.Fatalf("pending receipt match: record=%+v matched=%t err=%v", record, matched, err) + } +} + func TestWorkerReviewReadIsNotClassifiedAsHandoffWrite(t *testing.T) { binding := mustBinding(t, fullWorkspaceAlternative("workspace", "workspace", false), []any{openAIChatTool("workspace", structuredSchema())}) paths := newReservedPaths("req_review_read")