config, err := defaultAgentConfig()
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("prepare agent defaults: %w", err)
}
decoder := json.NewDecoder(bytes.NewReader(data))
func createAgent(dir string) (*Agent, error) {
config, err := defaultAgentConfig()
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("prepare agent defaults: %w", err)
}
return writeAgent(dir, config)
input, text, err = c.handle(ctx, input, response)
if err != nil {
- return "", err
+ return "", fmt.Errorf("handle model response: %w", err)
}
if len(input) == 0 {
for _, raw := range response.Output {
item, err := chainInputItem(raw)
if err != nil {
- return nil, "", err
+ return nil, "", fmt.Errorf("decode chain input: %w", err)
}
input = append(input, item)
case "message":
text, err := modelOutputText([]json.RawMessage{raw})
if err != nil {
- return nil, "", err
+ return nil, "", fmt.Errorf("read response text: %w", err)
}
text = strings.TrimSpace(text)
func (c *Compactor) Summarize(ctx context.Context, task Task) (string, error) {
text, err := compactorTask(task)
if err != nil {
- return "", err
+ return "", fmt.Errorf("prepare compaction task: %w", err)
}
instructions, err := c.instructions()
if err != nil {
- return "", err
+ return "", fmt.Errorf("read compaction instructions: %w", err)
}
source := task.Context + "\n" + text
}
if len(lines) == 0 {
- return "", err
+ if err != nil {
+ return "", fmt.Errorf("summarize node: %w", err)
+ }
+
+ return "", nil
}
return compactorShortest(lines), nil
dir, err = defaultDataDir()
if err != nil {
- return "", err
+ 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 {
- return Position{}, err
+ return Position{}, fmt.Errorf("parse journal day: %w", err)
}
if number != j.day {
err = j.closeWriter()
if err != nil {
- return Position{}, err
+ 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)
for n, path := range paths {
day, err := journalDayNumber(strings.TrimSuffix(filepath.Base(path), ".jsonl"))
if err != nil {
- return err
+ return fmt.Errorf("parse day filename: %w", err)
}
if day < place.day {
err = journalScanFile(ctx, path, day, offset, n == len(paths)-1, add)
if err != nil {
- return err
+ return fmt.Errorf("scan day file: %w", err)
}
}
lock, err := lockFile(filepath.Join(root, "lock"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("lock message log: %w", err)
}
l := &Log{
if l.index.count > 0 {
record, _, err := l.index.Read(l.index.count - 1)
if err != nil {
- return err
+ return fmt.Errorf("read last log position: %w", err)
}
last := decodePosition(record)
func (l *Log) Message(id int) (Message, error) {
record, found, err := l.index.Read(id)
if err != nil {
- return Message{}, err
+ return Message{}, fmt.Errorf("read message position: %w", err)
}
if !found {
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, err
+ return nil, fmt.Errorf("open memory log: %w", err)
}
m := &Memory{
for id := range m.chat.end {
built, err := m.tree.Built(0, id)
if err != nil {
- return err
+ return fmt.Errorf("check restored leaf: %w", err)
}
if built {
message, err := m.log.Message(id)
if err != nil {
- return err
+ return fmt.Errorf("read restored message: %w", err)
}
leaf, free := freeLeaf(message)
err = m.build(leaf)
if err != nil {
- return err
+ return fmt.Errorf("build restored leaf: %w", err)
}
}
keys, err := m.tree.Keys()
if err != nil {
- return err
+ return fmt.Errorf("read built nodes: %w", err)
}
for _, key := range keys {
err = m.settle(key)
if err != nil {
- return err
+ 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 {
- return err
+ return fmt.Errorf("read pending message: %w", err)
}
err = m.addMessage(message)
if err != nil {
- return err
+ return fmt.Errorf("add pending message: %w", err)
}
}
func (m *Memory) build(node Node) error {
err := m.tree.Add(node)
if err != nil {
- return err
+ return fmt.Errorf("add built node: %w", err)
}
m.chat.Refresh(node)
parent := NodeKey{key.level + 1, key.index / 2}
ready, err := m.ready(parent)
if err != nil || !ready {
- return err
+ if err != nil {
+ return fmt.Errorf("check parent readiness: %w", err)
+ }
+
+ return nil
}
left, right, err := m.children(parent)
if err != nil {
- return err
+ return fmt.Errorf("read parent children: %w", err)
}
node, free := freeMerge(left, right)
err = m.tree.Add(node)
if err != nil {
- return err
+ return fmt.Errorf("add merged node: %w", err)
}
key = parent
func (m *Memory) addMessage(message Message) error {
built, err := m.tree.Built(0, message.ID)
if err != nil {
- return err
+ return fmt.Errorf("check message leaf: %w", err)
}
leaf, free := freeLeaf(message)
case free:
err = m.build(leaf)
if err != nil {
- return err
+ return fmt.Errorf("build message leaf: %w", err)
}
default:
m.queue.AddLeaf(message.ID)
if key.level == 0 {
message, err := m.log.Message(key.index)
if err != nil {
- return Task{}, err
+ return Task{}, fmt.Errorf("read leaf message: %w", err)
}
task.Input = []string{leafSource(message)}
left, right, err := m.children(key)
if err != nil {
- return Task{}, err
+ return Task{}, fmt.Errorf("read task children: %w", err)
}
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 {
- return false, err
+ if err != nil {
+ return false, fmt.Errorf("check child readiness: %w", err)
+ }
+
+ return false, nil
}
}
built, err := m.tree.Built(key.level, key.index)
+ if err != nil {
+ return !built, fmt.Errorf("check parent readiness: %w", err)
+ }
- return !built, err
+ return !built, nil
}
// 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 {
- return Node{}, Node{}, err
+ return Node{}, Node{}, fmt.Errorf("read child node: %w", err)
}
if !built {
}
response.Text, err = modelOutputText(response.Output)
+ if err != nil {
+ return true, fmt.Errorf("read completed response text: %w", err)
+ }
- return true, err
+ return true, nil
case "response.incomplete":
reason := "unknown"
if event.Response != nil && event.Response.IncompleteDetails != nil {
}
if failure != nil {
- return failure
+ return fmt.Errorf("flush durable writes: %w", failure)
}
select {
t.order, err = openRecords(filepath.Join(t.dir, "tree.idx"), treeRecordBytes)
if err != nil {
- return err
+ return fmt.Errorf("open node order index: %w", err)
}
start := Position{}
if t.order.count == 0 {
err = t.removeLevels()
if err != nil {
- return err
+ return fmt.Errorf("remove empty level indexes: %w", err)
}
} else {
record, _, err := t.order.Read(t.order.count - 1)
if err != nil {
- return err
+ return fmt.Errorf("read last node order: %w", err)
}
// 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 {
- return err
+ return fmt.Errorf("restore last node level: %w", err)
}
start = Position{
func (t *Tree) writeLevel(node NodeKey, place Position) error {
level, err := t.level(node.level)
if err != nil {
- return err
+ return fmt.Errorf("open node level index: %w", err)
}
record := make([]byte, positionBytes)
built, err := t.Built(node.Level, node.Index)
if err != nil {
- return err
+ return fmt.Errorf("check target node: %w", err)
}
if built {
for _, index := range []int{2 * node.Index, 2*node.Index + 1} {
built, err = t.Built(node.Level-1, index)
if err != nil {
- return err
+ return fmt.Errorf("check child node: %w", err)
}
if !built {
err := t.order.Append(record)
if err != nil {
- return err
+ return fmt.Errorf("append node order: %w", err)
}
return t.writeLevel(node, place)
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 {
- 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)
err := t.check(node)
if err != nil {
- return err
+ return fmt.Errorf("validate node: %w", err)
}
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 {
- 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 := NodeKey{0, v.end}
_, err := v.load(tree, line)
if err != nil {
- return false, err
+ return false, fmt.Errorf("load appended leaf: %w", err)
}
v.lines = append(v.lines, line)
for v.merging && size > v.low {
best, err := v.mostDue(tree)
if err != nil || best < 0 {
- return merged, err
+ 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 {
- return merged, err
+ return merged, fmt.Errorf("load merged parent: %w", err)
}
size += v.lineSize(parent) - v.lineSize(left) - v.lineSize(right)
built, err := v.load(tree, line)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("load restored view node: %w", err)
}
if line.level > 0 && !built {