sanitize invalid ToolCall.Arguments before save; atomic write
This commit is contained in:
parent
08c9b3dc76
commit
dfc387d7b8
|
|
@ -112,6 +112,23 @@ func (s *Session) saveTo(path, id, agentName, cwd string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveToFull writes session state including backend/model info.
|
// 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 {
|
func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, remote string) error {
|
||||||
ps := PersistedSession{
|
ps := PersistedSession{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
|
@ -120,7 +137,7 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r
|
||||||
Model: modelName,
|
Model: modelName,
|
||||||
CWD: cwd,
|
CWD: cwd,
|
||||||
Remote: remote,
|
Remote: remote,
|
||||||
Messages: s.messages,
|
Messages: sanitizeMessages(s.messages),
|
||||||
TaskState: s.TaskState,
|
TaskState: s.TaskState,
|
||||||
TotalInputTokens: s.TotalInputTokens,
|
TotalInputTokens: s.TotalInputTokens,
|
||||||
TotalCachedInputTokens: s.TotalCachedInputTokens,
|
TotalCachedInputTokens: s.TotalCachedInputTokens,
|
||||||
|
|
@ -138,7 +155,12 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("session save: %w", err)
|
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.
|
// LoadPersistedSession reads a PersistedSession from a JSON file.
|
||||||
|
|
|
||||||
Reference in New Issue