session reliability: reconnect, knowledge loop, retire request_execution
Phase 1 — crash recovery: SSE auto-reconnect + backoff, polling gate during disconnect, connection banner with retry button, empty-response retry 3x, non-terminal resume on empty response, persistent error cards. Phase 2/4 — visibility + continuation: custom ExecutionStatus renderer, approvals extracted on every tool_result (not just done), activity bar with status/goal, SessionDigest live polling, Continue button. Phase 3 — cleanup: complete_task auto-cancels orphaned approvals, deletes assent/destructive window keys, propose_plan marks pending steps as replaced, plan step seq-order enforcement. Phase 5 — knowledge loop: list_lxcs state filter (active/destroyed), SOUL.md unmissable writeback section, propose_plan validation nudge, complete_task writeback check, upsert_knowledge about array support, plan generation grouping in frontend, session approval count badge. Retire request_execution — all mutations now route through run. Updated SOUL.md, AGENTS.md, CLIENTS.md, skills, and agent system notes. Migration 020: plan step generation column, audit_log session_id index, nomos_plan_executions pending-approval index.
This commit is contained in:
@@ -17,12 +17,12 @@ import (
|
||||
)
|
||||
|
||||
// maxIterations bounds one chat turn's tool-calling loop. Provisioning a
|
||||
// service is a long chain (research → plan → request_execution → per-step
|
||||
// service is a long chain (research → plan → run → per-step
|
||||
// install/verify run calls), so this must be generous; a full deploy with the
|
||||
// decomposed pct_create flow can legitimately need many steps. On exhaustion
|
||||
// the loop now produces a real summary (finalSummary) rather than a dead end.
|
||||
const maxIterations = 40
|
||||
const maxLLMRetries = 1
|
||||
const maxLLMRetries = 2
|
||||
|
||||
// historyWindowSize bounds how many of a session's most recent persisted
|
||||
// messages are replayed into the LLM's context on each turn — see
|
||||
@@ -129,7 +129,7 @@ func loadSoul() string {
|
||||
}
|
||||
return `You are Nomos, the steward of the oikos — the AI agent for the hubris homelab.
|
||||
You have access to MCP tools to query topology, health, knowledge, and request
|
||||
gated mutations through request_execution. Be concise. Prefer tools over guessing.`
|
||||
gated mutations through run. Be concise. Prefer tools over guessing.`
|
||||
}
|
||||
|
||||
// assentWindowDuration is how long after an operator approves a plan that
|
||||
@@ -294,7 +294,7 @@ func (a *agent) chatWith(ctx context.Context, sessionID, message, systemInject s
|
||||
}
|
||||
if len(granted) > 0 {
|
||||
a.openAssentWindow(ctx, sessionID)
|
||||
note := fmt.Sprintf("[System: the operator's message approved pending execution(s) %s via chat assent — they are now running. An assent window is now active for 30 minutes: config_mutation commands will auto-run without re-approval. Do not re-request or call request_execution/run again for these; check get_execution_status if you need the outcome. CONTINUE executing the full plan — do not stop and wait for 'continue' after each step. Only surface to the operator for destructive actions (need typed confirmation) or if you're genuinely stuck after trying alternatives.]", strings.Join(granted, ", "))
|
||||
note := fmt.Sprintf("[System: the operator's message approved pending execution(s) %s via chat assent — they are now running. An assent window is now active for 30 minutes: config_mutation commands will auto-run without re-approval. Do not re-request or call run again for these; check get_execution_status if you need the outcome. CONTINUE executing the full plan — do not stop and wait for 'continue' after each step. Only surface to the operator for destructive actions (need typed confirmation) or if you're genuinely stuck after trying alternatives.]", strings.Join(granted, ", "))
|
||||
messages = append(messages, openai.SystemMessage(note))
|
||||
}
|
||||
if len(blocked) > 0 {
|
||||
@@ -305,9 +305,9 @@ func (a *agent) chatWith(ctx context.Context, sessionID, message, systemInject s
|
||||
// The operator said "proceed"/"go ahead"/"yes" but the preceding
|
||||
// assistant turn had NO pending approvals — meaning the agent
|
||||
// proposed a plan in text and asked "shall I?" without calling
|
||||
// request_execution yet. Inject a system note telling the agent
|
||||
// run yet. Inject a system note telling the agent
|
||||
// the operator approved — go execute the plan now.
|
||||
note := "[System: The operator approved your proposed plan. Execute it now — call request_execution or run to carry out the steps you described. Do not re-describe the plan or ask for confirmation again. The assent window is active: config_mutation commands will auto-run once you create them.]"
|
||||
note := "[System: The operator approved your proposed plan. Execute it now — call run to carry out the steps you described. Do not re-describe the plan or ask for confirmation again. The assent window is active: config_mutation commands will auto-run once you create them.]"
|
||||
messages = append(messages, openai.SystemMessage(note))
|
||||
a.openAssentWindow(ctx, sessionID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user