Files
Agent-Native/cmd/agent-native/main.go
T
weichenandCursor 694b08cdbe
ci / vet-and-test (pull_request) Failing after 2s
ci / docker-checkout-hostgateway (pull_request) Successful in 2s
Pair skipped tool calls and close the stdio and fetch gaps
Why: a tool-budget stop left tool calls without results, host callbacks raced, a canceled MCP read stole the next response, and fetch re-resolved after the SSRF check.
Evidence: GOWORK=off go vet ./... && go test -count=1 ./... && go test -race ./runtime/... ./adapters/native/... passed, including TestLoopBudgetToolCallCap, TestParallelHostCallbacksAreSerial, TestStdioSlowCancelThenEcho, TestPresetsAndClientForChatCompletions, TestFetchResolveChangesToLoopbackRejected, and TestFetchHostCheckRedirectStillRejectsPrivate.
Impact: over-budget calls get an error result and no tool event; one Run serializes host callbacks; MCP dispatches by id; anthropic and google presets use OpenAI-compatible chat endpoints; fetch dials the address it just checked. deps/agent-intent is 3130190; error-code-system and logging-system are a3ee67b and 49b5d8b.
Refs: N1, N2, N3, N4, N13, N14
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 01:10:29 +00:00

128 lines
3.2 KiB
Go

package main
import (
"bufio"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"agent-native/rpc"
"agent-native/runtime"
"agent-native/runtime/llm"
"agent-native/runtime/session"
)
func main() {
mode := flag.String("mode", "print", "print, json, or rpc")
model := flag.String("model", "", "provider/model[:thinking]")
prompt := flag.String("prompt", "", "user message for print and json modes")
sessionPath := flag.String("session", "", "transcript path")
noSession := flag.Bool("no-session", false, "do not read or write a transcript")
export := flag.String("export", "", "write the active session as HTML and exit")
flag.Parse()
if *sessionPath == "" && !*noSession {
home, _ := os.UserHomeDir()
*sessionPath = filepath.Join(home, ".agent-native", "session.jsonl")
}
var store *session.Store
if !*noSession {
st, err := session.Open(*sessionPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
store = st
}
if *export != "" {
if store == nil {
fmt.Fprintln(os.Stderr, "export requires a session")
os.Exit(1)
}
if err := store.ExportHTML(*export); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
return
}
ref, err := llm.ParseModelRef(*model)
if *model != "" && err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
agent := runtime.NewAgentSession(runtime.Config{
Model: ref.Model,
ThinkingLevel: ref.Thinking,
Session: store,
Stream: liveStream(ref),
OnEvent: func(ev runtime.Event) {
if *mode != "json" {
return
}
raw, _ := json.Marshal(map[string]any{"type": ev.Type, "text": ev.Text, "tool": ev.Tool})
fmt.Println(string(raw))
},
})
srv := &rpc.Server{Agent: agent, Store: store, NoStore: *noSession}
ctx := context.Background()
switch *mode {
case "rpc":
in := bufio.NewScanner(os.Stdin)
in.Buffer(make([]byte, 0, 64*1024), 8*1024*1024)
for in.Scan() {
resp, err := srv.Handle(ctx, in.Bytes())
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Stdout.Write(resp)
}
case "print", "json":
if *prompt == "" {
*prompt = readAll(os.Stdin)
}
res, err := agent.Prompt(ctx, llm.Message{Role: llm.RoleUser, Content: *prompt})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if res.Err != nil {
fmt.Fprintln(os.Stderr, res.Err)
os.Exit(1)
}
if *mode == "print" {
fmt.Println(res.Text)
}
default:
fmt.Fprintln(os.Stderr, "unknown mode")
os.Exit(1)
}
}
func liveStream(ref llm.ModelRef) llm.StreamFunc {
provider, ok := llm.Lookup(ref.Provider, nil)
if !ok {
provider = llm.Provider{Protocol: llm.ProtocolOpenAIChat, BaseURL: os.Getenv("OPENAI_BASE_URL"), APIKey: os.Getenv("OPENAI_API_KEY")}
}
client, err := llm.ClientFor(provider)
if err != nil {
return func(ctx context.Context, model string, messages []llm.Message, tools []llm.ToolDecl, opts llm.StreamOptions) <-chan llm.Event {
ch := make(chan llm.Event, 1)
ch <- llm.Event{Kind: llm.KindError, Err: err, Message: llm.Message{StopReason: llm.StopError}}
close(ch)
return ch
}
}
return llm.WithRetry(client.Stream, 2, nil)
}
func readAll(r io.Reader) string {
raw, _ := io.ReadAll(r)
return string(raw)
}