fix(startup): unify agent startup with beacon + instructions in CLI prompt (#977)
All agents now receive their startup beacon + role-specific instructions via the CLI prompt, making sessions identifiable in /resume picker while removing unreliable post-startup nudges. Changes: - Rename FormatStartupNudge → FormatStartupBeacon, StartupNudgeConfig → BeaconConfig - Remove StartupNudge() function (no longer needed) - Remove PropulsionNudge() and PropulsionNudgeForRole() functions - Update deacon, witness, refinery, polecat managers to include beacon in CLI prompt - Update boot to inline beacon (can't import session due to import cycle) - Update daemon/lifecycle.go to include beacon via BuildCommandWithPrompt - Update cmd/deacon.go to include beacon in startup command - Remove redundant StartupNudge and PropulsionNudge calls from all startup paths The beacon is now part of the CLI prompt which is queued before Claude starts, making it more reliable than post-startup nudges which had timing issues. SessionStart hook runs gt prime automatically, so PropulsionNudge was redundant. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/boot"
|
||||
"github.com/steveyegge/gastown/internal/deacon"
|
||||
"github.com/steveyegge/gastown/internal/session"
|
||||
"github.com/steveyegge/gastown/internal/style"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
)
|
||||
@@ -141,7 +142,7 @@ func runBootStatus(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if sessionAlive {
|
||||
fmt.Printf(" Session: %s (alive)\n", boot.SessionName)
|
||||
fmt.Printf(" Session: %s (alive)\n", session.BootSessionName())
|
||||
} else {
|
||||
fmt.Printf(" Session: %s\n", style.Dim.Render("not running"))
|
||||
}
|
||||
@@ -219,7 +220,7 @@ func runBootSpawn(cmd *cobra.Command, args []string) error {
|
||||
if b.IsDegraded() {
|
||||
fmt.Println("Boot spawned in degraded mode (subprocess)")
|
||||
} else {
|
||||
fmt.Printf("Boot spawned in session: %s\n", boot.SessionName)
|
||||
fmt.Printf("Boot spawned in session: %s\n", session.BootSessionName())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -193,10 +193,10 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
// Use FormatStartupBeacon instead of bare "gt prime" which confuses agents
|
||||
// The SessionStart hook handles context injection (gt prime --hook)
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "start",
|
||||
@@ -242,9 +242,9 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
// Use FormatStartupBeacon instead of bare "gt prime" which confuses agents
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "restart",
|
||||
@@ -301,7 +301,7 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
// We're in the session at a shell prompt - start the agent
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, name)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "start",
|
||||
|
||||
@@ -413,9 +413,12 @@ func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
|
||||
return fmt.Errorf("creating deacon settings: %w", err)
|
||||
}
|
||||
|
||||
// Build startup command first
|
||||
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
|
||||
startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("deacon", "", townRoot, "", "", agentOverride)
|
||||
initialPrompt := session.BuildStartupPrompt(session.BeaconConfig{
|
||||
Recipient: "deacon",
|
||||
Sender: "daemon",
|
||||
Topic: "patrol",
|
||||
}, "I am Deacon. Start patrol: check gt hook, if empty create mol-deacon-patrol wisp and execute it.")
|
||||
startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("deacon", "", townRoot, "", initialPrompt, agentOverride)
|
||||
if err != nil {
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
@@ -451,23 +454,6 @@ func startDeaconSession(t *tmux.Tmux, sessionName, agentOverride string) error {
|
||||
runtimeConfig := config.LoadRuntimeConfig("")
|
||||
_ = runtime.RunStartupFallback(t, sessionName, "deacon", runtimeConfig)
|
||||
|
||||
// Inject startup nudge for predecessor discovery via /resume
|
||||
if err := session.StartupNudge(t, sessionName, session.StartupNudgeConfig{
|
||||
Recipient: "deacon",
|
||||
Sender: "daemon",
|
||||
Topic: "patrol",
|
||||
}); err != nil {
|
||||
style.PrintWarning("failed to send startup nudge: %v", err)
|
||||
}
|
||||
|
||||
// GUPP: Gas Town Universal Propulsion Principle
|
||||
// Send the propulsion nudge to trigger autonomous patrol execution.
|
||||
// Wait for beacon to be fully processed (needs to be separate prompt)
|
||||
time.Sleep(2 * time.Second)
|
||||
if err := t.NudgeSession(sessionName, session.PropulsionNudgeForRole("deacon", deaconDir)); err != nil {
|
||||
return fmt.Errorf("sending propulsion nudge: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -391,9 +391,9 @@ func buildRestartCommand(sessionName string) (string, error) {
|
||||
gtRole := identity.GTRole()
|
||||
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
// Use FormatStartupNudge instead of bare "gt prime" which confuses agents
|
||||
// Use FormatStartupBeacon instead of bare "gt prime" which confuses agents
|
||||
// The SessionStart hook handles context injection (gt prime --hook)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: identity.Address(),
|
||||
Sender: "self",
|
||||
Topic: "handoff",
|
||||
|
||||
@@ -188,7 +188,7 @@ func runMayorAttach(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Build startup beacon for context (like gt handoff does)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: "mayor",
|
||||
Sender: "human",
|
||||
Topic: "attach",
|
||||
|
||||
@@ -404,7 +404,7 @@ func startOrRestartCrewMember(t *tmux.Tmux, r *rig.Rig, crewName, townRoot strin
|
||||
// Agent has exited, restart it
|
||||
// Build startup beacon for predecessor discovery via /resume
|
||||
address := fmt.Sprintf("%s/crew/%s", r.Name, crewName)
|
||||
beacon := session.FormatStartupNudge(session.StartupNudgeConfig{
|
||||
beacon := session.FormatStartupBeacon(session.BeaconConfig{
|
||||
Recipient: address,
|
||||
Sender: "human",
|
||||
Topic: "restart",
|
||||
|
||||
Reference in New Issue
Block a user