feat: dsh-gate — auto-run non-destructive mutations when no session ID

PolicyService.route now auto-runs config_mutation and reversible_low when
sessionID is empty (dsh sidecar path), routing as 'dsh-gate'. Destructive
still queues. This eliminates the two-layer consent issue: dsh pre-execute
is the sole consent gate, oikos no longer queues dsh-originated mutations.

Also updates the dsh plan with Phase 1-3 completion status and the
architectural decision that _session_id injection is permanently deferred.

v0.38.0
This commit is contained in:
2026-08-16 17:03:27 +02:00
parent 04aa1bd5e8
commit eca81ae9af
4 changed files with 69 additions and 62 deletions

View File

@@ -169,6 +169,14 @@ func (s *PolicyService) route(ctx context.Context, in PolicySubmitInput, riskCla
return PolicyDecision{Action: DecisionAuto, RiskClass: riskClass, Route: "auto-act"}
}
// No session context (dsh sidecar or direct MCP call without nomos):
// the agent runtime owns consent gating, so oikos auto-runs everything
// except destructive without queueing. The destructive class still
// queues because it requires explicit operator confirmation.
if in.SessionID == "" && riskClass != policy.RiskDestructive {
return PolicyDecision{Action: DecisionAuto, RiskClass: riskClass, Route: "auto-act", AutoViaWindow: "dsh-gate"}
}
// Assent window: the operator approved the plan in this session;
// config_mutation within the window auto-runs.
if riskClass == policy.RiskConfigMutation && s.store.AssentWindowActive(ctx, in.AgentID, in.SessionID) {