Update changes
This commit is contained in:
parent
0b612aa0cc
commit
cbace3407c
|
|
@ -6,7 +6,6 @@ import (
|
|||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
|
@ -71,7 +70,8 @@ var (
|
|||
|
||||
func main() {
|
||||
if err := logging.Init(); err != nil {
|
||||
log.Fatalf("Failed to initialize logging: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize logging: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer logging.Logger().Sync()
|
||||
|
||||
|
|
@ -888,6 +888,7 @@ func handleContextWindow(w *acme.Win, sess *SessionInfo) {
|
|||
fmt.Fprintf(os.Stderr, "Error writing context: %v\n", err)
|
||||
} else {
|
||||
w.Ctl("clean")
|
||||
logging.Logger().Info("context updated", zap.String("session", sess.ID))
|
||||
fmt.Printf("Context updated for session %s\n", sess.ID)
|
||||
}
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"anvillm/internal/logging"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
|
@ -44,7 +43,8 @@ type SessionInfo struct {
|
|||
|
||||
func main() {
|
||||
if err := logging.Init(); err != nil {
|
||||
log.Fatalf("Failed to initialize logging: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize logging: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer logging.Logger().Sync()
|
||||
|
||||
|
|
@ -1117,6 +1117,7 @@ func attachToSession() {
|
|||
tmuxPath := filepath.Join(sess.ID, "tmux")
|
||||
tmuxData, err := readFile(tmuxPath)
|
||||
if err != nil {
|
||||
logging.Logger().Error("failed to read tmux session", zap.String("session", sess.ID), zap.Error(err))
|
||||
fmt.Printf("Error reading tmux session: %v\n", err)
|
||||
fmt.Println("Press Enter to continue...")
|
||||
fmt.Scanln()
|
||||
|
|
@ -1125,6 +1126,7 @@ func attachToSession() {
|
|||
|
||||
tmuxSession := strings.TrimSpace(string(tmuxData))
|
||||
if tmuxSession == "" {
|
||||
logging.Logger().Warn("no tmux session found", zap.String("session", sess.ID))
|
||||
fmt.Println("No tmux session found for this agent")
|
||||
fmt.Println("Press Enter to continue...")
|
||||
fmt.Scanln()
|
||||
|
|
@ -1132,6 +1134,7 @@ func attachToSession() {
|
|||
}
|
||||
|
||||
// Attach to tmux
|
||||
logging.Logger().Info("attaching to tmux session", zap.String("session", sess.ID), zap.String("tmux", tmuxSession))
|
||||
fmt.Printf("Attaching to tmux session: %s\n", tmuxSession)
|
||||
fmt.Printf("Press Ctrl-b d to detach\n\n")
|
||||
|
||||
|
|
@ -1141,6 +1144,7 @@ func attachToSession() {
|
|||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
logging.Logger().Error("failed to attach to tmux", zap.String("tmux", tmuxSession), zap.Error(err))
|
||||
fmt.Printf("Error attaching to tmux: %v\n", err)
|
||||
fmt.Println("Press Enter to continue...")
|
||||
fmt.Scanln()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"anvillm/internal/session"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
|
@ -83,7 +82,8 @@ func usage() {
|
|||
func start(daemonize bool) {
|
||||
// Initialize logging first
|
||||
if err := logging.Init(); err != nil {
|
||||
log.Fatalf("Failed to initialize logging: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize logging: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer logging.Logger().Sync()
|
||||
|
||||
|
|
@ -100,6 +100,7 @@ func start(daemonize bool) {
|
|||
logging.Logger().Fatal("agent already running", zap.Int("pid", existingPid))
|
||||
}
|
||||
// Stale PID file
|
||||
logging.Logger().Warn("removing stale PID file", zap.Int("pid", existingPid))
|
||||
os.Remove(pidFile)
|
||||
}
|
||||
|
||||
|
|
@ -107,10 +108,12 @@ func start(daemonize bool) {
|
|||
if daemonize {
|
||||
// Check if we're already the daemon (via environment variable)
|
||||
if os.Getenv("ANVILSRV_DAEMON") != "1" {
|
||||
logging.Logger().Info("daemonizing anvilsrv")
|
||||
// Fork the daemon process - use full path to executable
|
||||
cmd, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get executable path: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to get executable path: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
args := []string{"fgstart"} // Use fgstart to avoid re-daemonizing
|
||||
|
||||
|
|
@ -122,7 +125,8 @@ func start(daemonize bool) {
|
|||
|
||||
proc, err := os.StartProcess(cmd, append([]string{cmd}, args...), attr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to daemonize: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to daemonize: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Wait a moment to ensure daemon starts and writes PID file
|
||||
|
|
@ -146,29 +150,34 @@ func start(daemonize bool) {
|
|||
pid := os.Getpid()
|
||||
pidContent := fmt.Sprintf("%d\n", pid)
|
||||
|
||||
logging.Logger().Info("starting anvilsrv", zap.Int("pid", pid), zap.Bool("daemonized", daemonize))
|
||||
|
||||
// Try to create PID file exclusively (fails if exists)
|
||||
f, err := os.OpenFile(pidFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
// File exists, check if process is still running
|
||||
if existingPid := readPidFile(); existingPid != 0 {
|
||||
if isProcessRunning(existingPid) {
|
||||
log.Fatalf("anvilsrv already running (PID %d)", existingPid)
|
||||
logging.Logger().Fatal("anvilsrv already running", zap.Int("pid", existingPid))
|
||||
}
|
||||
// Stale PID file, remove and retry
|
||||
logging.Logger().Warn("removing stale PID file on startup", zap.Int("pid", existingPid))
|
||||
os.Remove(pidFile)
|
||||
f, err = os.OpenFile(pidFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create PID file: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to create PID file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Failed to create PID file: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to create PID file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := f.WriteString(pidContent); err != nil {
|
||||
f.Close()
|
||||
os.Remove(pidFile)
|
||||
log.Fatalf("Failed to write PID file: %v", err)
|
||||
logging.Logger().Fatal("failed to write PID file", zap.Error(err))
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove(pidFile)
|
||||
|
|
@ -179,6 +188,7 @@ func start(daemonize bool) {
|
|||
|
||||
// Create all backends
|
||||
nsSuffix := getNamespaceSuffix()
|
||||
logging.Logger().Debug("initializing backends", zap.String("namespace_suffix", nsSuffix))
|
||||
backendMap := map[string]backend.Backend{
|
||||
"kiro-cli": backends.NewKiroCLI(nsSuffix),
|
||||
"claude": backends.NewClaude(nsSuffix),
|
||||
|
|
@ -186,6 +196,7 @@ func start(daemonize bool) {
|
|||
}
|
||||
|
||||
mgr := session.NewManager(backendMap)
|
||||
logging.Logger().Info("session manager initialized")
|
||||
|
||||
// Cleanup tmux sessions on exit
|
||||
defer func() {
|
||||
|
|
@ -205,6 +216,7 @@ func start(daemonize bool) {
|
|||
if beadsDir == "" {
|
||||
beadsDir = filepath.Join(os.Getenv("HOME"), ".beads")
|
||||
}
|
||||
logging.Logger().Debug("initializing beads store", zap.String("path", beadsDir))
|
||||
beadsStore, err := bd.OpenFromConfig(context.Background(), beadsDir)
|
||||
if err != nil {
|
||||
logging.Logger().Warn("failed to initialize beads store", zap.Error(err))
|
||||
|
|
@ -212,6 +224,7 @@ func start(daemonize bool) {
|
|||
}
|
||||
if beadsStore != nil {
|
||||
defer beadsStore.Close()
|
||||
logging.Logger().Info("beads store initialized", zap.String("path", beadsDir))
|
||||
}
|
||||
|
||||
// Start 9P server
|
||||
|
|
@ -228,6 +241,7 @@ func start(daemonize bool) {
|
|||
|
||||
// Start background monitor for self-healing
|
||||
stopMonitor := make(chan struct{})
|
||||
logging.Logger().Debug("starting background session monitor")
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
|
@ -245,6 +259,7 @@ func start(daemonize bool) {
|
|||
}
|
||||
}
|
||||
case <-stopMonitor:
|
||||
logging.Logger().Debug("stopping background session monitor")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -282,7 +297,8 @@ func stop() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Sent SIGTERM to anvilsrv (PID %d)\n", pid)
|
||||
logging.Logger().Info("stopping anvilsrv", zap.Int("pid", pid))
|
||||
fmt.Printf("Stopping anvilsrv (PID %d)\n", pid)
|
||||
}
|
||||
|
||||
func status() {
|
||||
|
|
@ -297,6 +313,7 @@ func status() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
logging.Logger().Info("anvilsrv status check", zap.Int("pid", pid), zap.Bool("running", true))
|
||||
fmt.Printf("anvilsrv is running (PID %d)\n", pid)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ import (
|
|||
"anvillm/internal/logging"
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
|
@ -28,7 +29,8 @@ var (
|
|||
|
||||
func main() {
|
||||
if err := logging.Init(); err != nil {
|
||||
log.Fatalf("Failed to initialize logging: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize logging: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer logging.Logger().Sync()
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -80,7 +79,8 @@ func usage() {
|
|||
|
||||
func startCmd(daemonize bool) {
|
||||
if err := logging.Init(); err != nil {
|
||||
log.Fatalf("Failed to initialize logging: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize logging: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer logging.Logger().Sync()
|
||||
|
||||
|
|
@ -92,12 +92,14 @@ func startCmd(daemonize bool) {
|
|||
logging.Logger().Fatal("anvilwebgw already running", zap.Int("pid", existingPid))
|
||||
}
|
||||
// Stale PID file
|
||||
logging.Logger().Warn("removing stale PID file", zap.Int("pid", existingPid))
|
||||
os.Remove(pidFile)
|
||||
}
|
||||
|
||||
// Daemonize if requested
|
||||
if daemonize {
|
||||
if os.Getenv("ANVILWEBGW_DAEMON") != "1" {
|
||||
logging.Logger().Info("daemonizing anvilwebgw")
|
||||
cmd, err := os.Executable()
|
||||
if err != nil {
|
||||
logging.Logger().Fatal("failed to get executable path", zap.Error(err))
|
||||
|
|
@ -116,7 +118,8 @@ func startCmd(daemonize bool) {
|
|||
|
||||
proc, err := os.StartProcess(cmd, append([]string{cmd}, fwdArgs...), attr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to daemonize: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to daemonize: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
proc.Release()
|
||||
|
||||
|
|
@ -134,25 +137,29 @@ func startCmd(daemonize bool) {
|
|||
|
||||
// We're the daemon (or running in foreground)
|
||||
pid := os.Getpid()
|
||||
logging.Logger().Info("starting anvilwebgw", zap.Int("pid", pid), zap.Bool("daemonized", daemonize))
|
||||
f, err := os.OpenFile(pidFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
if existingPid := readPidFile(); existingPid != 0 {
|
||||
if isProcessRunning(existingPid) {
|
||||
log.Fatalf("anvilwebgw already running (PID %d)", existingPid)
|
||||
logging.Logger().Fatal("anvilwebgw already running", zap.Int("pid", existingPid))
|
||||
}
|
||||
logging.Logger().Warn("removing stale PID file on startup", zap.Int("pid", existingPid))
|
||||
os.Remove(pidFile)
|
||||
f, err = os.OpenFile(pidFile, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create PID file: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to create PID file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Failed to create PID file: %v", err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to create PID file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(f, "%d\n", pid); err != nil {
|
||||
f.Close()
|
||||
os.Remove(pidFile)
|
||||
log.Fatalf("Failed to write PID file: %v", err)
|
||||
logging.Logger().Fatal("failed to write PID file", zap.Error(err))
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove(pidFile)
|
||||
|
|
@ -223,7 +230,8 @@ func stopCmd() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Sent SIGTERM to anvilwebgw (PID %d)\n", pid)
|
||||
logging.Logger().Info("stopping anvilwebgw", zap.Int("pid", pid))
|
||||
fmt.Printf("Stopping anvilwebgw (PID %d)\n", pid)
|
||||
}
|
||||
|
||||
func statusCmd() {
|
||||
|
|
@ -238,6 +246,7 @@ func statusCmd() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
logging.Logger().Info("anvilwebgw status check", zap.Int("pid", pid), zap.Bool("running", true))
|
||||
fmt.Printf("anvilwebgw is running (PID %d)\n", pid)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,13 +139,16 @@ func (s *Session) transitionToLocked(newState string) error {
|
|||
s.idleCond.Broadcast()
|
||||
case s.state == "error" && newState == "starting":
|
||||
// Restart from error state
|
||||
logging.Logger().Info("restarting from error state", zap.String("session", s.id))
|
||||
s.state = newState
|
||||
case newState == "stopped" || newState == "killed":
|
||||
// Allow transition to stopped/exited from any state
|
||||
logging.Logger().Info("session stopped", zap.String("session", s.id), zap.String("old_state", oldState), zap.String("new_state", newState))
|
||||
s.state = newState
|
||||
s.idleCond.Broadcast()
|
||||
case s.state == "stopped" && newState == "starting":
|
||||
// Allow restart from stopped state
|
||||
logging.Logger().Info("restarting from stopped state", zap.String("session", s.id))
|
||||
s.state = newState
|
||||
default:
|
||||
return fmt.Errorf("invalid state transition: %s → %s", s.state, newState)
|
||||
|
|
@ -437,6 +440,8 @@ func (s *Session) Send(ctx context.Context, prompt string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
logging.Logger().Debug("sending prompt to session", zap.String("session", s.id), zap.Int("prompt_length", len(prompt)))
|
||||
|
||||
// Instruction to send response via mailbox (triggers idle transition)
|
||||
outInstruction := fmt.Sprintf("When done, send your response using the send_message tool:\n from: %s\n to: [sender's agent_id, or \"user\"]\n type: [PROMPT_RESPONSE, REVIEW_RESPONSE, QUERY_RESPONSE, or APPROVAL_RESPONSE]\n subject: [brief description of what you did]\n body: YOUR_SUMMARY_HERE\n", s.id)
|
||||
|
||||
|
|
@ -474,6 +479,8 @@ func (s *Session) Send(ctx context.Context, prompt string) (string, error) {
|
|||
return "", fmt.Errorf("send enter failed: %w", err)
|
||||
}
|
||||
|
||||
logging.Logger().Info("prompt sent to session", zap.String("session", s.id))
|
||||
|
||||
// Hook handles state transitions
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,14 @@ func Init() error {
|
|||
return err
|
||||
}
|
||||
|
||||
core := zapcore.NewCore(fileEncoder, zapcore.AddSync(logFile), zapcore.InfoLevel)
|
||||
logLevel := zapcore.InfoLevel
|
||||
if os.Getenv("ANVILLM_DEBUG") != "" {
|
||||
logLevel = zapcore.DebugLevel
|
||||
}
|
||||
|
||||
core := zapcore.NewCore(fileEncoder, zapcore.AddSync(logFile), logLevel)
|
||||
logger = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
logger.Info("logging initialized", zap.String("program", progName), zap.String("log_file", logPath), zap.String("level", logLevel.String()))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,11 +89,14 @@ func (m *Manager) New(opts backend.SessionOptions, backendName string) (backend.
|
|||
m.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
logging.Logger().Warn("backend not found", zap.String("backend", backendName))
|
||||
return nil, backend.ErrBackendNotFound
|
||||
}
|
||||
|
||||
logging.Logger().Info("creating new session", zap.String("backend", backendName), zap.String("cwd", opts.CWD))
|
||||
sess, err := b.CreateSession(context.Background(), opts)
|
||||
if err != nil {
|
||||
logging.Logger().Error("failed to create session", zap.String("backend", backendName), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +116,7 @@ func (m *Manager) New(opts backend.SessionOptions, backendName string) (backend.
|
|||
// Create mailbox structure for new session
|
||||
m.mailManager.EnsureMailbox(sess.ID())
|
||||
|
||||
logging.Logger().Info("session created", zap.String("id", sess.ID()), zap.String("backend", backendName))
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
|
|
@ -165,8 +169,12 @@ func (m *Manager) List() []string {
|
|||
// Remove removes a session from the manager
|
||||
func (m *Manager) Remove(id string) {
|
||||
m.mu.Lock()
|
||||
delete(m.sessions, id)
|
||||
m.mu.Unlock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, exists := m.sessions[id]; exists {
|
||||
logging.Logger().Info("removing session", zap.String("id", id))
|
||||
delete(m.sessions, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the mail processing loop
|
||||
|
|
|
|||
Reference in New Issue