sanitize invalid ToolCall.Arguments before save; atomic write

This commit is contained in:
ollie 2026-07-27 20:01:31 +02:00
parent 08c9b3dc76
commit dfc387d7b8
1 changed files with 24 additions and 2 deletions

View File

@ -112,6 +112,23 @@ func (s *Session) saveTo(path, id, agentName, cwd string) error {
}
// saveToFull writes session state including backend/model info.
func sanitizeMessages(msgs []backend.Message) []backend.Message {
out := make([]backend.Message, 0, len(msgs))
for _, m := range msgs {
for j, tc := range m.ToolCalls {
if len(tc.Arguments) > 0 {
var dummy json.RawMessage
if err := json.Unmarshal(tc.Arguments, &dummy); err != nil {
// Corrupted arguments — replace with empty object so save succeeds.
m.ToolCalls[j].Arguments = json.RawMessage("{}")
}
}
}
out = append(out, m)
}
return out
}
func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, remote string) error {
ps := PersistedSession{
ID: id,
@ -120,7 +137,7 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r
Model: modelName,
CWD: cwd,
Remote: remote,
Messages: s.messages,
Messages: sanitizeMessages(s.messages),
TaskState: s.TaskState,
TotalInputTokens: s.TotalInputTokens,
TotalCachedInputTokens: s.TotalCachedInputTokens,
@ -138,7 +155,12 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r
if err != nil {
return fmt.Errorf("session save: %w", err)
}
return os.WriteFile(path, data, 0600)
// Atomic write: write to temp file, then rename to preserve last good snapshot.
tmpPath := path + ".tmp"
if err := os.WriteFile(tmpPath, data, 0600); err != nil {
return fmt.Errorf("session save tmp: %w", err)
}
return os.Rename(tmpPath, path)
}
// LoadPersistedSession reads a PersistedSession from a JSON file.