| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "encoding/json" |
| 5 | |
| 6 | "reasonix/internal/evidence" |
| 7 | "reasonix/internal/tool" |
| 8 | ) |
| 9 | |
| 10 | // recordToolReceipts files the turn-scoped evidence for one executed call: |
| 11 | // always the model-visible call for audit, plus the real target's attributes |
| 12 | // for mutation/read classification when a proxy resolved elsewhere. |
| 13 | func (a *Agent) finalizeObservedToolReceipts(plan *toolCallPlan, result string, execution *tool.ShellExecution, err error) evidence.Receipt { |
| 14 | a.observeAfterMutation(plan) |
| 15 | plan.mutationAfterDone = true |
| 16 | return a.recordToolReceipts(plan, result, execution, err) |
| 17 | } |
| 18 | |
| 19 | func (a *Agent) recordToolReceipts(plan *toolCallPlan, result string, execution *tool.ShellExecution, err error) evidence.Receipt { |
| 20 | if a.task.ledger == nil { |
| 21 | return evidence.Receipt{} |
| 22 | } |
| 23 | call := plan.call |
| 24 | args := json.RawMessage(call.Arguments) |
| 25 | switch { |
| 26 | case plan.evidenceName != call.Name: |
| 27 | proxy := evidence.ReceiptFromToolCall(call.Name, args, err == nil, true) |
| 28 | proxy.ToolCallID = call.ID |
| 29 | a.task.ledger.Record(proxy) |
| 30 | rec := evidence.ReceiptFromToolCall(plan.evidenceName, plan.evidenceArgs, err == nil, plan.readOnly) |
| 31 | rec.ToolCallID = call.ID |
| 32 | rec.Mutation = plan.effects.ContentMutation |
| 33 | a.stampReceiptDeliveryScope(&rec) |
| 34 | decorateExecutionReceipt(&rec, result, execution) |
| 35 | rec = a.task.ledger.Record(rec) |
| 36 | return rec |
| 37 | default: |
| 38 | rec := evidence.ReceiptFromToolCall(call.Name, args, err == nil, plan.tool.ReadOnly()) |
| 39 | rec.ToolCallID = call.ID |
| 40 | rec.Mutation = plan.effects.ContentMutation |
| 41 | a.stampReceiptDeliveryScope(&rec) |
| 42 | decorateExecutionReceipt(&rec, result, execution) |
| 43 | rec = a.task.ledger.Record(rec) |
| 44 | return rec |
| 45 | } |
| 46 | } |
| 47 |