Memory, tree, view, log, and chain failures name their local operation
while retaining the original error through wrapping. Combined result
checks keep their successful nil error instead of wrapping it. Error
classification, cancellation causes, and successful values stay intact.
Issue:
3cdc678
config, err := defaultAgentConfig()
if err != nil {
config, err := defaultAgentConfig()
if err != nil {
+ return nil, fmt.Errorf("prepare agent defaults: %w", err)
}
decoder := json.NewDecoder(bytes.NewReader(data))
}
decoder := json.NewDecoder(bytes.NewReader(data))
func createAgent(dir string) (*Agent, error) {
config, err := defaultAgentConfig()
if err != nil {
func createAgent(dir string) (*Agent, error) {
config, err := defaultAgentConfig()
if err != nil {
+ return nil, fmt.Errorf("prepare agent defaults: %w", err)
}
return writeAgent(dir, config)
}
return writeAgent(dir, config)
input, text, err = c.handle(ctx, input, response)
if err != nil {
input, text, err = c.handle(ctx, input, response)
if err != nil {
+ return "", fmt.Errorf("handle model response: %w", err)
for _, raw := range response.Output {
item, err := chainInputItem(raw)
if err != nil {
for _, raw := range response.Output {
item, err := chainInputItem(raw)
if err != nil {
+ return nil, "", fmt.Errorf("decode chain input: %w", err)
}
input = append(input, item)
}
input = append(input, item)
case "message":
text, err := modelOutputText([]json.RawMessage{raw})
if err != nil {
case "message":
text, err := modelOutputText([]json.RawMessage{raw})
if err != nil {
+ return nil, "", fmt.Errorf("read response text: %w", err)
}
text = strings.TrimSpace(text)
}
text = strings.TrimSpace(text)
func (c *Compactor) Summarize(ctx context.Context, task Task) (string, error) {
text, err := compactorTask(task)
if err != nil {
func (c *Compactor) Summarize(ctx context.Context, task Task) (string, error) {
text, err := compactorTask(task)
if err != nil {
+ return "", fmt.Errorf("prepare compaction task: %w", err)
}
instructions, err := c.instructions()
if err != nil {
}
instructions, err := c.instructions()
if err != nil {
+ return "", fmt.Errorf("read compaction instructions: %w", err)
}
source := task.Context + "\n" + text
}
source := task.Context + "\n" + text
+ if err != nil {
+ return "", fmt.Errorf("summarize node: %w", err)
+ }
+
+ return "", nil
}
return compactorShortest(lines), nil
}
return compactorShortest(lines), nil
dir, err = defaultDataDir()
if err != nil {
dir, err = defaultDataDir()
if err != nil {
+ return "", fmt.Errorf("find default data directory: %w", err)
func (j *Journal) Write(day string, line []byte) (Position, error) {
number, err := journalDayNumber(day)
if err != nil {
func (j *Journal) Write(day string, line []byte) (Position, error) {
number, err := journalDayNumber(day)
if err != nil {
+ return Position{}, fmt.Errorf("parse journal day: %w", err)
}
if number != j.day {
err = j.closeWriter()
if err != nil {
}
if number != j.day {
err = j.closeWriter()
if err != nil {
+ return Position{}, fmt.Errorf("close previous day writer: %w", err)
}
j.file, err = os.OpenFile(filepath.Join(j.dir, day+".jsonl"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
}
j.file, err = os.OpenFile(filepath.Join(j.dir, day+".jsonl"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
for n, path := range paths {
day, err := journalDayNumber(strings.TrimSuffix(filepath.Base(path), ".jsonl"))
if err != nil {
for n, path := range paths {
day, err := journalDayNumber(strings.TrimSuffix(filepath.Base(path), ".jsonl"))
if err != nil {
+ return fmt.Errorf("parse day filename: %w", err)
err = journalScanFile(ctx, path, day, offset, n == len(paths)-1, add)
if err != nil {
err = journalScanFile(ctx, path, day, offset, n == len(paths)-1, add)
if err != nil {
+ return fmt.Errorf("scan day file: %w", err)
lock, err := lockFile(filepath.Join(root, "lock"))
if err != nil {
lock, err := lockFile(filepath.Join(root, "lock"))
if err != nil {
+ return nil, fmt.Errorf("lock message log: %w", err)
if l.index.count > 0 {
record, _, err := l.index.Read(l.index.count - 1)
if err != nil {
if l.index.count > 0 {
record, _, err := l.index.Read(l.index.count - 1)
if err != nil {
+ return fmt.Errorf("read last log position: %w", err)
}
last := decodePosition(record)
}
last := decodePosition(record)
func (l *Log) Message(id int) (Message, error) {
record, found, err := l.index.Read(id)
if err != nil {
func (l *Log) Message(id int) (Message, error) {
record, found, err := l.index.Read(id)
if err != nil {
+ return Message{}, fmt.Errorf("read message position: %w", err)
func openMemory(ctx context.Context, root string, clock func() time.Time, summarizer Summarizer, logger *slog.Logger) (*Memory, error) {
log, err := openLog(ctx, root, clock)
if err != nil {
func openMemory(ctx context.Context, root string, clock func() time.Time, summarizer Summarizer, logger *slog.Logger) (*Memory, error) {
log, err := openLog(ctx, root, clock)
if err != nil {
+ return nil, fmt.Errorf("open memory log: %w", err)
for id := range m.chat.end {
built, err := m.tree.Built(0, id)
if err != nil {
for id := range m.chat.end {
built, err := m.tree.Built(0, id)
if err != nil {
+ return fmt.Errorf("check restored leaf: %w", err)
message, err := m.log.Message(id)
if err != nil {
message, err := m.log.Message(id)
if err != nil {
+ return fmt.Errorf("read restored message: %w", err)
}
leaf, free := freeLeaf(message)
}
leaf, free := freeLeaf(message)
err = m.build(leaf)
if err != nil {
err = m.build(leaf)
if err != nil {
+ return fmt.Errorf("build restored leaf: %w", err)
}
}
keys, err := m.tree.Keys()
if err != nil {
}
}
keys, err := m.tree.Keys()
if err != nil {
+ return fmt.Errorf("read built nodes: %w", err)
}
for _, key := range keys {
}
for _, key := range keys {
err = m.settle(key)
if err != nil {
err = m.settle(key)
if err != nil {
+ return fmt.Errorf("settle restored node: %w", err)
for m.chat.end < m.log.Len() {
message, err := m.log.Message(m.chat.end)
if err != nil {
for m.chat.end < m.log.Len() {
message, err := m.log.Message(m.chat.end)
if err != nil {
+ return fmt.Errorf("read pending message: %w", err)
}
err = m.addMessage(message)
if err != nil {
}
err = m.addMessage(message)
if err != nil {
+ return fmt.Errorf("add pending message: %w", err)
func (m *Memory) build(node Node) error {
err := m.tree.Add(node)
if err != nil {
func (m *Memory) build(node Node) error {
err := m.tree.Add(node)
if err != nil {
+ return fmt.Errorf("add built node: %w", err)
parent := NodeKey{key.level + 1, key.index / 2}
ready, err := m.ready(parent)
if err != nil || !ready {
parent := NodeKey{key.level + 1, key.index / 2}
ready, err := m.ready(parent)
if err != nil || !ready {
+ if err != nil {
+ return fmt.Errorf("check parent readiness: %w", err)
+ }
+
+ return nil
}
left, right, err := m.children(parent)
if err != nil {
}
left, right, err := m.children(parent)
if err != nil {
+ return fmt.Errorf("read parent children: %w", err)
}
node, free := freeMerge(left, right)
}
node, free := freeMerge(left, right)
err = m.tree.Add(node)
if err != nil {
err = m.tree.Add(node)
if err != nil {
+ return fmt.Errorf("add merged node: %w", err)
func (m *Memory) addMessage(message Message) error {
built, err := m.tree.Built(0, message.ID)
if err != nil {
func (m *Memory) addMessage(message Message) error {
built, err := m.tree.Built(0, message.ID)
if err != nil {
+ return fmt.Errorf("check message leaf: %w", err)
}
leaf, free := freeLeaf(message)
}
leaf, free := freeLeaf(message)
case free:
err = m.build(leaf)
if err != nil {
case free:
err = m.build(leaf)
if err != nil {
+ return fmt.Errorf("build message leaf: %w", err)
}
default:
m.queue.AddLeaf(message.ID)
}
default:
m.queue.AddLeaf(message.ID)
if key.level == 0 {
message, err := m.log.Message(key.index)
if err != nil {
if key.level == 0 {
message, err := m.log.Message(key.index)
if err != nil {
+ return Task{}, fmt.Errorf("read leaf message: %w", err)
}
task.Input = []string{leafSource(message)}
}
task.Input = []string{leafSource(message)}
left, right, err := m.children(key)
if err != nil {
left, right, err := m.children(key)
if err != nil {
+ return Task{}, fmt.Errorf("read task children: %w", err)
}
for _, child := range []Node{left, right} {
}
for _, child := range []Node{left, right} {
for _, index := range []int{2 * key.index, 2*key.index + 1} {
built, err := m.tree.Built(key.level-1, index)
if err != nil || !built {
for _, index := range []int{2 * key.index, 2*key.index + 1} {
built, err := m.tree.Built(key.level-1, index)
if err != nil || !built {
+ if err != nil {
+ return false, fmt.Errorf("check child readiness: %w", err)
+ }
+
+ return false, nil
}
}
built, err := m.tree.Built(key.level, key.index)
}
}
built, err := m.tree.Built(key.level, key.index)
+ if err != nil {
+ return !built, fmt.Errorf("check parent readiness: %w", err)
+ }
}
// children reads the two children of the node key.
}
// children reads the two children of the node key.
for n := range children {
child, built, err := m.tree.Node(key.level-1, 2*key.index+n)
if err != nil {
for n := range children {
child, built, err := m.tree.Node(key.level-1, 2*key.index+n)
if err != nil {
- return Node{}, Node{}, err
+ return Node{}, Node{}, fmt.Errorf("read child node: %w", err)
}
response.Text, err = modelOutputText(response.Output)
}
response.Text, err = modelOutputText(response.Output)
+ if err != nil {
+ return true, fmt.Errorf("read completed response text: %w", err)
+ }
case "response.incomplete":
reason := "unknown"
if event.Response != nil && event.Response.IncompleteDetails != nil {
case "response.incomplete":
reason := "unknown"
if event.Response != nil && event.Response.IncompleteDetails != nil {
+ return fmt.Errorf("flush durable writes: %w", failure)
t.order, err = openRecords(filepath.Join(t.dir, "tree.idx"), treeRecordBytes)
if err != nil {
t.order, err = openRecords(filepath.Join(t.dir, "tree.idx"), treeRecordBytes)
if err != nil {
+ return fmt.Errorf("open node order index: %w", err)
}
start := Position{}
if t.order.count == 0 {
err = t.removeLevels()
if err != nil {
}
start := Position{}
if t.order.count == 0 {
err = t.removeLevels()
if err != nil {
+ return fmt.Errorf("remove empty level indexes: %w", err)
}
} else {
record, _, err := t.order.Read(t.order.count - 1)
if err != nil {
}
} else {
record, _, err := t.order.Read(t.order.count - 1)
if err != nil {
+ return fmt.Errorf("read last node order: %w", err)
}
// A crash can stop an add between the two index writes, so the
}
// A crash can stop an add between the two index writes, so the
node, last := treeDecodeRecord(record)
err = t.writeLevel(node, last)
if err != nil {
node, last := treeDecodeRecord(record)
err = t.writeLevel(node, last)
if err != nil {
+ return fmt.Errorf("restore last node level: %w", err)
func (t *Tree) writeLevel(node NodeKey, place Position) error {
level, err := t.level(node.level)
if err != nil {
func (t *Tree) writeLevel(node NodeKey, place Position) error {
level, err := t.level(node.level)
if err != nil {
+ return fmt.Errorf("open node level index: %w", err)
}
record := make([]byte, positionBytes)
}
record := make([]byte, positionBytes)
built, err := t.Built(node.Level, node.Index)
if err != nil {
built, err := t.Built(node.Level, node.Index)
if err != nil {
+ return fmt.Errorf("check target node: %w", err)
for _, index := range []int{2 * node.Index, 2*node.Index + 1} {
built, err = t.Built(node.Level-1, index)
if err != nil {
for _, index := range []int{2 * node.Index, 2*node.Index + 1} {
built, err = t.Built(node.Level-1, index)
if err != nil {
+ return fmt.Errorf("check child node: %w", err)
err := t.order.Append(record)
if err != nil {
err := t.order.Append(record)
if err != nil {
+ return fmt.Errorf("append node order: %w", err)
}
return t.writeLevel(node, place)
}
return t.writeLevel(node, place)
records, err := t.level(level)
if err != nil {
records, err := t.level(level)
if err != nil {
- return Position{}, false, err
+ return Position{}, false, fmt.Errorf("open node level index: %w", err)
}
record, found, err := records.Read(index)
if err != nil || !found {
}
record, found, err := records.Read(index)
if err != nil || !found {
- return Position{}, false, err
+ if err != nil {
+ return Position{}, false, fmt.Errorf("read node level record: %w", err)
+ }
+
+ return Position{}, false, nil
}
place := decodePosition(record)
}
place := decodePosition(record)
err := t.check(node)
if err != nil {
err := t.check(node)
if err != nil {
+ return fmt.Errorf("validate node: %w", err)
}
line, err := json.Marshal(node)
}
line, err := json.Marshal(node)
func (t *Tree) Node(level, index int) (Node, bool, error) {
place, built, err := t.place(level, index)
if err != nil || !built {
func (t *Tree) Node(level, index int) (Node, bool, error) {
place, built, err := t.place(level, index)
if err != nil || !built {
- return Node{}, false, err
+ if err != nil {
+ return Node{}, false, fmt.Errorf("find node position: %w", err)
+ }
+
+ return Node{}, false, nil
}
line, err := t.journal.Read(place)
}
line, err := t.journal.Read(place)
line := NodeKey{0, v.end}
_, err := v.load(tree, line)
if err != nil {
line := NodeKey{0, v.end}
_, err := v.load(tree, line)
if err != nil {
+ return false, fmt.Errorf("load appended leaf: %w", err)
}
v.lines = append(v.lines, line)
}
v.lines = append(v.lines, line)
for v.merging && size > v.low {
best, err := v.mostDue(tree)
if err != nil || best < 0 {
for v.merging && size > v.low {
best, err := v.mostDue(tree)
if err != nil || best < 0 {
+ if err != nil {
+ return merged, fmt.Errorf("choose merge candidate: %w", err)
+ }
+
+ return merged, nil
}
left, right := v.lines[best], v.lines[best+1]
parent := NodeKey{left.level + 1, left.index / 2}
_, err = v.load(tree, parent)
if err != nil {
}
left, right := v.lines[best], v.lines[best+1]
parent := NodeKey{left.level + 1, left.index / 2}
_, err = v.load(tree, parent)
if err != nil {
+ return merged, fmt.Errorf("load merged parent: %w", err)
}
size += v.lineSize(parent) - v.lineSize(left) - v.lineSize(right)
}
size += v.lineSize(parent) - v.lineSize(left) - v.lineSize(right)
built, err := v.load(tree, line)
if err != nil {
built, err := v.load(tree, line)
if err != nil {
+ return nil, fmt.Errorf("load restored view node: %w", err)
}
if line.level > 0 && !built {
}
if line.level > 0 && !built {