corago/codex-keep/intent.go
2026-10-22 22:05:33 +09:00

179 lines
4.3 KiB
Go

package main
import (
"bufio"
"encoding/json"
"errors"
"io"
"os"
"strings"
)
type intentLog struct {
Path string `json:"path"`
Pos int64 `json:"pos"`
Fence int64 `json:"fence"`
Partial bool `json:"partial,omitempty"`
Sealed bool `json:"sealed,omitempty"`
Calls map[string]struct{} `json:"calls,omitempty"`
}
func openIntent(path string) (*intentLog, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() { _ = f.Close() }()
info, err := f.Stat()
if err != nil {
return nil, err
}
log := &intentLog{Path: path, Pos: info.Size(), Fence: info.Size()}
if info.Size() != 0 {
var last [1]byte
if _, err := f.ReadAt(last[:], info.Size()-1); err != nil {
return nil, err
}
log.Partial = last[0] != '\n'
}
return log, nil
}
func (l *intentLog) end() (int64, error) {
info, err := os.Stat(l.Path)
if err != nil {
return 0, err
}
return info.Size(), nil
}
func (l *intentLog) seal() error {
l.Sealed = true
info, err := os.Stat(l.Path)
if err != nil {
return err
}
l.Fence = info.Size()
l.Sealed = false
return nil
}
func (l *intentLog) read(end int64) (goalStatus, error) {
if l.Sealed {
if err := l.seal(); err != nil {
return "", err
}
}
f, err := os.Open(l.Path)
if err != nil {
return "", err
}
defer func() { _ = f.Close() }()
reader := bufio.NewReader(io.NewSectionReader(f, l.Pos, end-l.Pos))
var latest goalStatus
for {
line, err := reader.ReadBytes('\n')
if errors.Is(err, io.EOF) {
return latest, nil
}
if err != nil {
return "", err
}
next := l.Pos + int64(len(line))
if l.Partial {
l.Partial = false
l.Pos = next
continue
}
if l.Pos < l.Fence {
l.Pos = next
continue
}
status, match, err := l.parse(line)
if err != nil {
return "", err
}
l.Pos = next
if match {
latest = status
}
}
}
func (l *intentLog) parse(line []byte) (goalStatus, bool, error) {
var record struct {
Type string `json:"type"`
Payload struct {
Type string `json:"type"`
Role string `json:"role"`
Name string `json:"name"`
Namespace string `json:"namespace"`
CallID string `json:"call_id"`
Arguments json.RawMessage `json:"arguments"`
Output json.RawMessage `json:"output"`
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"content"`
Meta struct {
Kinds []string `json:"content_item_kinds"`
} `json:"internal_chat_message_metadata_passthrough"`
} `json:"payload"`
}
if err := json.Unmarshal(line, &record); err != nil {
return "", false, err
}
if record.Type != "response_item" {
return "", false, nil
}
p := record.Payload
switch p.Type {
case "function_call":
if p.Name == "update_goal" && (p.Namespace == "" || p.Namespace == "functions") && p.CallID != "" {
var args struct {
Status goalStatus `json:"status"`
}
var arguments string
if json.Unmarshal(p.Arguments, &arguments) == nil && json.Unmarshal([]byte(arguments), &args) == nil && args.Status == blocked {
if l.Calls == nil {
l.Calls = make(map[string]struct{})
}
l.Calls[p.CallID] = struct{}{}
}
}
return "", false, nil
case "function_call_output":
if _, ok := l.Calls[p.CallID]; !ok {
return "", false, nil
}
delete(l.Calls, p.CallID)
var output string
var result goalResult
if json.Unmarshal(p.Output, &output) == nil && json.Unmarshal([]byte(output), &result) == nil &&
result.Goal != nil && result.Goal.Status == blocked {
return blocked, true, nil
}
return "", false, nil
}
if p.Type != "message" || p.Role != "user" || len(p.Content) != 1 ||
p.Content[0].Type != "input_text" || len(p.Meta.Kinds) != 1 ||
p.Meta.Kinds[0] != "user.goal" && p.Meta.Kinds[0] != "user.goal.omitted" {
return "", false, nil
}
for line := range strings.SplitSeq(p.Content[0].Text, "\n") {
if line == "User cleared the goal." {
return "cleared", true, nil
}
if value, ok := strings.CutPrefix(line, "User set goal status: "); ok {
var status goalStatus
if err := json.Unmarshal([]byte(strings.TrimSuffix(value, ".")), &status); err != nil {
return "", false, err
}
switch status {
case active, paused, blocked, "complete", "budgetLimited", "usageLimited":
return status, true, nil
}
}
}
return "", true, nil
}