Documentation
¶
Overview ¶
Package unattended implements ADR-023 phase 1: the --unattended flag, one-time per-repo disclosure, JSONL audit log, and the hard kill switch primitive.
Why a separate package: unattended-mode state crosses the CLI (argument parsing, disclosure prompt) and the supervisor (banner header, audit emit on every dispatch). Centralising it here keeps both surfaces calling one canonical implementation — the trust file, the audit path resolver, the banner formatter — and makes the policy testable in isolation.
What this package DOESN'T do (deferred to v1.1, per ADR-023):
- Self-paced wake-up scheduling (`ScheduleWakeup` integration)
- Watch-event resumption (PR merged, CI failed, file changed)
- The compounding-trust clamp around remote A2A peers — that lands when ADR-024 phase 1 (Agent Card endpoint) ships
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuditDir ¶
AuditDir returns the per-session audit directory: $XDG_DATA_HOME/clawtool/sessions/<id>/, or ~/.local/share/clawtool/sessions/<id>/ when XDG isn't set.
func DisclosurePanel ¶
DisclosurePanel returns the operator-facing copy printed on the first --unattended invocation per repo. Lists every downstream flag clawtool will set so the operator confirms knowingly.
Per ADR-023: the disclosure is the flag name + this panel + the audit log. We do NOT add modal popups inside long-running sessions; that's the author's anti-pattern call.
func Grant ¶
Grant adds a trust row for repoPath. Idempotent — re-granting updates GrantedAt but doesn't duplicate.
func IsTrusted ¶
IsTrusted reports whether the operator has previously granted unattended-mode trust to this repo path. Lookup is exact-match on RepoPath after filepath.Clean — symlinks NOT resolved (we trust the operator's CLI invocation path).
func TrustFilePath ¶
func TrustFilePath() string
TrustFilePath returns the canonical path: $XDG_DATA_HOME/clawtool/ unattended-trust.toml, or ~/.local/share/clawtool/unattended- trust.toml when XDG isn't set.
Types ¶
type AuditEntry ¶
type AuditEntry struct {
TS time.Time `json:"ts"`
Session string `json:"session_id"`
Kind string `json:"kind"` // "dispatch" | "result" | "rule_block" | "kill"
Agent string `json:"agent,omitempty"` // instance name when relevant
Family string `json:"family,omitempty"`
Prompt string `json:"prompt,omitempty"` // truncated to ~256 chars
Result string `json:"result,omitempty"` // truncated tail
Error string `json:"error,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
AuditEntry is one line in the JSONL audit log. The schema is intentionally append-only: new fields are additive, never renamed, so an operator can grep across logs from older clawtool versions without a parser break.
type SessionState ¶
type SessionState struct {
ID string `json:"session_id"`
StartedAt time.Time `json:"started_at"`
RepoPath string `json:"repo_path"`
AuditPath string `json:"audit_path"`
YOLOAlias bool `json:"yolo_alias,omitempty"` // true when the operator invoked --yolo
// contains filtered or unexported fields
}
SessionState carries the live unattended-mode session. Every dispatch in unattended mode runs through one of these so the audit log + banner ride together without the supervisor having to thread state through opts.
func Begin ¶
func Begin(repoPath string, yolo bool) (*SessionState, error)
Begin creates a new SessionState with a fresh UUID and audit log path. Caller MUST defer Close on the returned state so the audit file flushes to disk on session end.
func (*SessionState) Banner ¶
func (s *SessionState) Banner() string
Banner returns the persistent status line the supervisor renders on every dispatch result so callers downstream of the dispatch know the chain crossed an unattended boundary. Format mirrors ADR-023 §Behaviour.
func (*SessionState) Close ¶
func (s *SessionState) Close() error
Close flushes and closes the audit file. Safe to call multiple times.
func (*SessionState) Emit ¶
func (s *SessionState) Emit(e AuditEntry)
Emit appends one entry to the session's audit log. Failures silently log to stderr — losing an audit line shouldn't kill the dispatch, but operators should know the audit broke.
type TrustEntry ¶
type TrustEntry struct {
RepoPath string `toml:"repo_path"`
GrantedAt time.Time `toml:"granted_at"`
Note string `toml:"note,omitempty"`
}
TrustEntry is one row in the per-repo trust file. The operator confirms once per repo path; subsequent unattended dispatches from the same path skip the disclosure.